Skip to content

Commit e143b05

Browse files
authored
feat: refactor
Description TBA, fixes #3
1 parent 9ec7b99 commit e143b05

File tree

1 file changed

+61
-45
lines changed

1 file changed

+61
-45
lines changed

src/lazy.ts

Lines changed: 61 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,8 @@ export default class Lazy {
5757
const { src, loading, error, lifecycle, delay } = this._valueFormatter(typeof binding === 'string' ? binding : binding.value)
5858
this._lifecycle(LifecycleEnum.LOADING, lifecycle, el)
5959
el.setAttribute('src', loading || DEFAULT_LOADING)
60-
if (!hasIntersectionObserver) {
61-
this.loadImages(el, src, error, lifecycle)
62-
this._log(() => {
63-
throw new Error('Not support IntersectionObserver!')
64-
})
65-
}
66-
this._initIntersectionObserver(el, src, error, lifecycle, delay)
60+
this._tryInitIntersectionObserver(el, src, loading, error, lifecycle, delay);
61+
6762
}
6863

6964
/**
@@ -76,8 +71,8 @@ export default class Lazy {
7671
if (!el)
7772
return
7873
this._realObserver(el)?.unobserve(el)
79-
const { src, error, lifecycle, delay } = this._valueFormatter(typeof binding === 'string' ? binding : binding.value)
80-
this._initIntersectionObserver(el, src, error, lifecycle, delay)
74+
const { src, loading, error, lifecycle, delay } = this._valueFormatter(typeof binding === 'string' ? binding : binding.value)
75+
this._tryInitIntersectionObserver(el, src, loading, error, lifecycle, delay);
8176
}
8277

8378
/**
@@ -92,52 +87,73 @@ export default class Lazy {
9287
this._realObserver(el)?.unobserve(el)
9388
this._images.delete(el)
9489
}
90+
91+
private _tryLoadImage(el: HTMLElement, src: string, onSuccess((this: GlobalEventHandlers, ev: Event) => any) | null, onError: OnErrorEventHandler) {
92+
const img = new Image()
93+
img.src = src
94+
95+
const _onSuccess = el ? () => { this._setImageSrc(el, src); if (onSuccess) onSuccess(); } : onSuccess
96+
97+
this._listenImageStatus(img, _onSuccess, onError);
98+
99+
return img
100+
}
95101

96102
/**
97-
* force loading
103+
* update image with full lifecycles
98104
*
99105
* @param {HTMLElement} el
100106
* @param {string} src
101107
* @memberof Lazy
102108
*/
103-
public loadImages(el: HTMLElement, src: string, error?: string, lifecycle?: Lifecycle): void {
104-
this._setImageSrc(el, src, error, lifecycle)
109+
public loadImages(el: HTMLElement, src: string, loading?:string, error?: string, lifecycle?: Lifecycle): void {
110+
const onSuccess = () => {
111+
this._lifecycle(LifecycleEnum.LOADED, lifecycle, el);
112+
}
113+
114+
const onError = () => {
115+
this._listenImageStatus(el, null, null);
116+
this._realObserver(el)?.unobserve(el)
117+
118+
this._lifecycle(LifecycleEnum.ERROR, lifecycle, el);
119+
if (error || DEFAULT_ERROR)
120+
this._setImageSrc(el, error || DEFAULT_ERROR)
121+
122+
this._log(() => {
123+
throw new Error(`Image failed to load! And failed src was: ${src} `);
124+
});
125+
}
126+
127+
// loading state
128+
this._lifecycle(LifecycleEnum.LOADING, lifecycle, el)
129+
this._setImageSrc(el, loading || DEFAULT_LOADING)
130+
131+
this._tryLoadImage(el, src, onSuccess, onError)
105132
}
106133

107134
/**
108-
* set img tag src
135+
* set img src
109136
*
110137
* @private
111138
* @param {HTMLElement} el
112139
* @param {string} src
113140
* @memberof Lazy
114141
*/
115-
private _setImageSrc(el: HTMLElement, src: string, error?: string, lifecycle?: Lifecycle): void {
116-
if (el.tagName.toLowerCase() === 'img') {
117-
if (src) {
118-
const preSrc = el.getAttribute('src')
119-
if (preSrc !== src)
120-
el.setAttribute('src', src)
121-
}
122-
this._listenImageStatus(el as HTMLImageElement, () => {
123-
this._lifecycle(LifecycleEnum.LOADED, lifecycle, el)
124-
}, () => {
125-
// Fix onload trigger twice, clear onload event
126-
// Reload on update
127-
el.onload = null
128-
this._lifecycle(LifecycleEnum.ERROR, lifecycle, el)
129-
this._realObserver(el)?.disconnect()
130-
if (error) {
131-
const newImageSrc = el.getAttribute('src')
132-
if (newImageSrc !== error)
133-
el.setAttribute('src', error)
134-
}
135-
this._log(() => { throw new Error(`Image failed to load!And failed src was: ${src} `) })
136-
})
137-
}
138-
else {
139-
el.style.backgroundImage = `url('${src}')`
142+
private _setImageSrc(el: HTMLElement, src: string): void {
143+
if (el.tagName.toLowerCase() === "img")
144+
el.setAttribute('src', src)
145+
else
146+
el.style.backgroundImage = `url('${src}')`;
147+
}
148+
149+
private _tryInitIntersectionObserver(el: HTMLElement, src: string, loading?: string, error?: string, lifecycle?: Lifecycle, delay?: number): void {
150+
if (!hasIntersectionObserver) {
151+
this.loadImage(el, src, loading, error, lifecycle);
152+
this._log(() => {
153+
throw new Error("Not support IntersectionObserver!");
154+
});
140155
}
156+
this._initIntersectionObserver(el, src, loading, error, lifecycle, delay);
141157
}
142158

143159
/**
@@ -148,33 +164,33 @@ export default class Lazy {
148164
* @param {string} src
149165
* @memberof Lazy
150166
*/
151-
private _initIntersectionObserver(el: HTMLElement, src: string, error?: string, lifecycle?: Lifecycle, delay?: number): void {
167+
private _initIntersectionObserver(el: HTMLElement, src: string, loading?: string, error?: string, lifecycle?: Lifecycle, delay?: number): void {
152168
const observerOptions = this.options.observerOptions
153169
this._images.set(el, new IntersectionObserver((entries) => {
154170
Array.prototype.forEach.call(entries, (entry) => {
155171
if (delay && delay > 0)
156-
this._delayedIntersectionCallback(el, entry, delay, src, error, lifecycle)
172+
this._delayedIntersectionCallback(el, entry, delay, src, loading, error, lifecycle)
157173
else
158-
this._intersectionCallback(el, entry, src, error, lifecycle)
174+
this._intersectionCallback(el, entry, src, loading, error, lifecycle)
159175
})
160176
}, observerOptions))
161177
this._realObserver(el)?.observe(el)
162178
}
163179

164-
private _intersectionCallback(el: HTMLElement, entry: IntersectionObserverEntry, src: string, error?: string, lifecycle?: Lifecycle): void {
180+
private _intersectionCallback(el: HTMLElement, entry: IntersectionObserverEntry, src: string, loading?: string, error?: string, lifecycle?: Lifecycle): void {
165181
if (entry.isIntersecting) {
166182
this._realObserver(el)?.unobserve(entry.target)
167-
this._setImageSrc(el, src, error, lifecycle)
183+
this.loadImage(el, src, loading, error, lifecycle)
168184
}
169185
}
170186

171-
private _delayedIntersectionCallback(el: HTMLElement, entry: IntersectionObserverEntry, delay: number, src: string, error?: string, lifecycle?: Lifecycle): void {
187+
private _delayedIntersectionCallback(el: HTMLElement, entry: IntersectionObserverEntry, delay: number, src: string, loading?: string, error?: string, lifecycle?: Lifecycle): void {
172188
if (entry.isIntersecting) {
173189
if (entry.target.hasAttribute(TIMEOUT_ID_DATA_ATTR))
174190
return
175191

176192
const timeoutId = setTimeout(() => {
177-
this._intersectionCallback(el, entry, src, error, lifecycle)
193+
this._intersectionCallback(el, entry, src, loading, error, lifecycle)
178194
entry.target.removeAttribute(TIMEOUT_ID_DATA_ATTR)
179195
}, delay)
180196
entry.target.setAttribute(TIMEOUT_ID_DATA_ATTR, String(timeoutId))

0 commit comments

Comments
 (0)