util.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. import assign from 'assign-deep'
  2. const inBrowser = typeof window !== 'undefined'
  3. export const hasIntersectionObserver = inBrowser && 'IntersectionObserver' in window
  4. export const modeType = {
  5. event: 'event',
  6. observer: 'observer'
  7. }
  8. // CustomEvent polyfill
  9. const CustomEvent = (function () {
  10. if (!inBrowser) return
  11. if (typeof window.CustomEvent === 'function') return window.CustomEvent
  12. function CustomEvent (event, params) {
  13. params = params || { bubbles: false, cancelable: false, detail: undefined }
  14. var evt = document.createEvent('CustomEvent')
  15. evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail)
  16. return evt
  17. }
  18. CustomEvent.prototype = window.Event.prototype
  19. return CustomEvent
  20. })()
  21. function remove (arr, item) {
  22. if (!arr.length) return
  23. const index = arr.indexOf(item)
  24. if (index > -1) return arr.splice(index, 1)
  25. }
  26. function some (arr, fn) {
  27. let has = false
  28. for (let i = 0, len = arr.length; i < len; i++) {
  29. if (fn(arr[i])) {
  30. has = true
  31. break
  32. }
  33. }
  34. return has
  35. }
  36. function getBestSelectionFromSrcset (el, scale) {
  37. if (el.tagName !== 'IMG' || !el.getAttribute('data-srcset')) return
  38. let options = el.getAttribute('data-srcset')
  39. const result = []
  40. const container = el.parentNode
  41. const containerWidth = container.offsetWidth * scale
  42. let spaceIndex
  43. let tmpSrc
  44. let tmpWidth
  45. options = options.trim().split(',')
  46. options.map(item => {
  47. item = item.trim()
  48. spaceIndex = item.lastIndexOf(' ')
  49. if (spaceIndex === -1) {
  50. tmpSrc = item
  51. tmpWidth = 999998
  52. } else {
  53. tmpSrc = item.substr(0, spaceIndex)
  54. tmpWidth = parseInt(item.substr(spaceIndex + 1, item.length - spaceIndex - 2), 10)
  55. }
  56. result.push([tmpWidth, tmpSrc])
  57. })
  58. result.sort(function (a, b) {
  59. if (a[0] < b[0]) {
  60. return -1
  61. }
  62. if (a[0] > b[0]) {
  63. return 1
  64. }
  65. if (a[0] === b[0]) {
  66. if (b[1].indexOf('.webp', b[1].length - 5) !== -1) {
  67. return 1
  68. }
  69. if (a[1].indexOf('.webp', a[1].length - 5) !== -1) {
  70. return -1
  71. }
  72. }
  73. return 0
  74. })
  75. let bestSelectedSrc = ''
  76. let tmpOption
  77. const resultCount = result.length
  78. for (let i = 0; i < resultCount; i++) {
  79. tmpOption = result[i]
  80. if (tmpOption[0] >= containerWidth) {
  81. bestSelectedSrc = tmpOption[1]
  82. break
  83. }
  84. }
  85. return bestSelectedSrc
  86. }
  87. function find (arr, fn) {
  88. let item
  89. for (let i = 0, len = arr.length; i < len; i++) {
  90. if (fn(arr[i])) {
  91. item = arr[i]
  92. break
  93. }
  94. }
  95. return item
  96. }
  97. const getDPR = (scale = 1) => inBrowser ? (window.devicePixelRatio || scale) : scale
  98. function supportWebp () {
  99. if (!inBrowser) return false
  100. let support = true
  101. const d = document
  102. try {
  103. let el = d.createElement('object')
  104. el.type = 'image/webp'
  105. el.style.visibility = 'hidden'
  106. el.innerHTML = '!'
  107. d.body.appendChild(el)
  108. support = !el.offsetWidth
  109. d.body.removeChild(el)
  110. } catch (err) {
  111. support = false
  112. }
  113. return support
  114. }
  115. function throttle (action, delay) {
  116. let timeout = null
  117. let lastRun = 0
  118. return function () {
  119. if (timeout) {
  120. return
  121. }
  122. let elapsed = Date.now() - lastRun
  123. let context = this
  124. let args = arguments
  125. let runCallback = function () {
  126. lastRun = Date.now()
  127. timeout = false
  128. action.apply(context, args)
  129. }
  130. if (elapsed >= delay) {
  131. runCallback()
  132. } else {
  133. timeout = setTimeout(runCallback, delay)
  134. }
  135. }
  136. }
  137. function testSupportsPassive () {
  138. if (!inBrowser) return
  139. let support = false
  140. try {
  141. let opts = Object.defineProperty({}, 'passive', {
  142. get: function () {
  143. support = true
  144. }
  145. })
  146. window.addEventListener('test', null, opts)
  147. } catch (e) {}
  148. return support
  149. }
  150. const supportsPassive = testSupportsPassive()
  151. const _ = {
  152. on (el, type, func, capture = false) {
  153. if (supportsPassive) {
  154. el.addEventListener(type, func, {
  155. capture: capture,
  156. passive: true
  157. })
  158. } else {
  159. el.addEventListener(type, func, capture)
  160. }
  161. },
  162. off (el, type, func, capture = false) {
  163. el.removeEventListener(type, func, capture)
  164. }
  165. }
  166. const loadImageAsync = (item, resolve, reject) => {
  167. let image = new Image()
  168. image.src = item.src
  169. image.onload = function () {
  170. resolve({
  171. naturalHeight: image.naturalHeight,
  172. naturalWidth: image.naturalWidth,
  173. src: image.src
  174. })
  175. }
  176. image.onerror = function (e) {
  177. reject(e)
  178. }
  179. }
  180. const style = (el, prop) => {
  181. return typeof getComputedStyle !== 'undefined'
  182. ? getComputedStyle(el, null).getPropertyValue(prop)
  183. : el.style[prop]
  184. }
  185. const overflow = (el) => {
  186. return style(el, 'overflow') + style(el, 'overflow-y') + style(el, 'overflow-x')
  187. }
  188. const scrollParent = (el) => {
  189. if (!inBrowser) return
  190. if (!(el instanceof HTMLElement)) {
  191. return window
  192. }
  193. let parent = el
  194. while (parent) {
  195. if (parent === document.body || parent === document.documentElement) {
  196. break
  197. }
  198. if (!parent.parentNode) {
  199. break
  200. }
  201. if (/(scroll|auto)/.test(overflow(parent))) {
  202. return parent
  203. }
  204. parent = parent.parentNode
  205. }
  206. return window
  207. }
  208. function isObject (obj) {
  209. return obj !== null && typeof obj === 'object'
  210. }
  211. function ObjectKeys (obj) {
  212. if (!(obj instanceof Object)) return []
  213. if (Object.keys) {
  214. return Object.keys(obj)
  215. } else {
  216. let keys = []
  217. for (let key in obj) {
  218. if (obj.hasOwnProperty(key)) {
  219. keys.push(key)
  220. }
  221. }
  222. return keys
  223. }
  224. }
  225. function ArrayFrom (arrLike) {
  226. let len = arrLike.length
  227. const list = []
  228. for (let i = 0; i < len; i++) {
  229. list.push(arrLike[i])
  230. }
  231. return list
  232. }
  233. function noop () {}
  234. export {
  235. inBrowser,
  236. CustomEvent,
  237. remove,
  238. some,
  239. find,
  240. assign,
  241. noop,
  242. ArrayFrom,
  243. _,
  244. isObject,
  245. throttle,
  246. supportWebp,
  247. getDPR,
  248. scrollParent,
  249. loadImageAsync,
  250. getBestSelectionFromSrcset,
  251. ObjectKeys
  252. }