-
Notifications
You must be signed in to change notification settings - Fork 0
/
lazyloaded.mjs
117 lines (93 loc) · 3.01 KB
/
lazyloaded.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { closestNumber, loadImage, isFunction } from 'book-of-spells'
export class LazyLoaded {
constructor(selector = '.js-lazyloaded', callback = null, options = {}) {
this.selector = selector
this.px_ratio = window.hasOwnProperty('devicePixelRatio') ? window.devicePixelRatio : 1
this.callback = callback
this.observer = null
this.options = options || {}
return this.init(this.selector)
}
parseSrcset(srcset) {
let type = null
const map = {}
const sizes = []
for (const line of srcset.split(',')) {
const trimmedLine = line.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')
const pts = trimmedLine.split(/[\s\uFEFF\xA0]+/g)
if (!pts.length > 1) continue
if (/x$/i.test(pts[1])) {
type = "ratio"
pts[1] = parseFloat(pts[1].replace(/x$/i, ''))
} else {
type = "size"
pts[1] = parseInt(pts[1].replace(/px$|vw$|w$/i, ''), 10)
}
if (!Number.isNaN(pts[1])) {
sizes.push(pts[1])
map[pts[1]] = pts[0]
}
}
return {
type: type,
srcset: map,
sizes: sizes
}
}
loadImage(elem) {
let src = elem.getAttribute('data-src')
const srcset = elem.getAttribute('data-srcset')
if (srcset) {
const data = this.parseSrcset(srcset)
const selected = this.closest(data.sizes, data.type)
src = data.srcset[selected]
}
if (!src || !src.length) return
loadImage(src, () => {
const bg = elem.getAttribute('data-background')
if (bg && (bg === '' || bg === 'true')) {
elem.style.backgroundImage = `url(${src})`
} else {
elem.src = src
}
if (isFunction(this.callback)) this.callback(elem)
elem.classList.add(`${this.selector.replace(/^\.(js\-)?/, '')}--loaded`)
})
}
loadAll(elements) {
if (!elements || !elements.length) return
for (const element of elements) {
this.loadImage(element)
if (this.observer) this.observer.unobserve(element)
}
}
observe(entries, observer) {
for (const entry of entries) {
if (!entry.isIntersecting) continue
const elem = entry.target
this.loadImage(elem)
this.observer.unobserve(elem)
}
}
init(selector) {
const elements = document.querySelectorAll(selector)
if (!elements || !elements.length) return
if (!window.hasOwnProperty('IntersectionObserver')) return this.loadAll(elements)
this.observer = this.observer || new IntersectionObserver(this.observe.bind(this), this.options)
for (const element of elements) {
this.observer.observe(element)
}
}
add(elements) {
if (!elements.hasOwnProperty('length')) elements = [elements]
if (!this.observer) return this.loadAll(elements)
for (const element of elements) {
this.observer.observe(element)
}
}
closest(sizes, type) {
let goal = type !== 'ratio' ? window.innerWidth * this.px_ratio : this.px_ratio;
return closestNumber(goal, sizes);
}
}
export default LazyLoaded