@@ -57,13 +57,8 @@ export default class Lazy {
57
57
const { src, loading, error, lifecycle, delay } = this . _valueFormatter ( typeof binding === 'string' ? binding : binding . value )
58
58
this . _lifecycle ( LifecycleEnum . LOADING , lifecycle , el )
59
59
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
+
67
62
}
68
63
69
64
/**
@@ -76,8 +71,8 @@ export default class Lazy {
76
71
if ( ! el )
77
72
return
78
73
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 ) ;
81
76
}
82
77
83
78
/**
@@ -92,52 +87,73 @@ export default class Lazy {
92
87
this . _realObserver ( el ) ?. unobserve ( el )
93
88
this . _images . delete ( el )
94
89
}
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
+ }
95
101
96
102
/**
97
- * force loading
103
+ * update image with full lifecycles
98
104
*
99
105
* @param {HTMLElement } el
100
106
* @param {string } src
101
107
* @memberof Lazy
102
108
*/
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 )
105
132
}
106
133
107
134
/**
108
- * set img tag src
135
+ * set img src
109
136
*
110
137
* @private
111
138
* @param {HTMLElement } el
112
139
* @param {string } src
113
140
* @memberof Lazy
114
141
*/
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
+ } ) ;
140
155
}
156
+ this . _initIntersectionObserver ( el , src , loading , error , lifecycle , delay ) ;
141
157
}
142
158
143
159
/**
@@ -148,33 +164,33 @@ export default class Lazy {
148
164
* @param {string } src
149
165
* @memberof Lazy
150
166
*/
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 {
152
168
const observerOptions = this . options . observerOptions
153
169
this . _images . set ( el , new IntersectionObserver ( ( entries ) => {
154
170
Array . prototype . forEach . call ( entries , ( entry ) => {
155
171
if ( delay && delay > 0 )
156
- this . _delayedIntersectionCallback ( el , entry , delay , src , error , lifecycle )
172
+ this . _delayedIntersectionCallback ( el , entry , delay , src , loading , error , lifecycle )
157
173
else
158
- this . _intersectionCallback ( el , entry , src , error , lifecycle )
174
+ this . _intersectionCallback ( el , entry , src , loading , error , lifecycle )
159
175
} )
160
176
} , observerOptions ) )
161
177
this . _realObserver ( el ) ?. observe ( el )
162
178
}
163
179
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 {
165
181
if ( entry . isIntersecting ) {
166
182
this . _realObserver ( el ) ?. unobserve ( entry . target )
167
- this . _setImageSrc ( el , src , error , lifecycle )
183
+ this . loadImage ( el , src , loading , error , lifecycle )
168
184
}
169
185
}
170
186
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 {
172
188
if ( entry . isIntersecting ) {
173
189
if ( entry . target . hasAttribute ( TIMEOUT_ID_DATA_ATTR ) )
174
190
return
175
191
176
192
const timeoutId = setTimeout ( ( ) => {
177
- this . _intersectionCallback ( el , entry , src , error , lifecycle )
193
+ this . _intersectionCallback ( el , entry , src , loading , error , lifecycle )
178
194
entry . target . removeAttribute ( TIMEOUT_ID_DATA_ATTR )
179
195
} , delay )
180
196
entry . target . setAttribute ( TIMEOUT_ID_DATA_ATTR , String ( timeoutId ) )
0 commit comments