@@ -75,12 +75,15 @@ const requests = {};
75
75
76
76
const ImageLoader = {
77
77
abort ( requestId : number ) {
78
- let image = requests [ `${ requestId } ` ] ;
79
- if ( image ) {
78
+ const request = requests [ requestId ] ;
79
+ if ( request ) {
80
+ const { image, cleanup } = request ;
81
+ if ( cleanup ) cleanup ( ) ;
82
+
80
83
image . onerror = null ;
81
84
image . onload = null ;
82
- image = null ;
83
- delete requests [ ` ${ requestId } ` ] ;
85
+ image . src = '' ;
86
+ delete requests [ requestId ] ;
84
87
}
85
88
} ,
86
89
getSize (
@@ -93,7 +96,7 @@ const ImageLoader = {
93
96
const requestId = ImageLoader . load ( uri , callback , errorCallback ) ;
94
97
95
98
function callback ( ) {
96
- const image = requests [ ` ${ requestId } ` ] ;
99
+ const { image } = requests [ requestId ] || { } ;
97
100
if ( image ) {
98
101
const { naturalHeight, naturalWidth } = image ;
99
102
if ( naturalHeight && naturalWidth ) {
@@ -118,24 +121,62 @@ const ImageLoader = {
118
121
has ( uri : string ) : boolean {
119
122
return ImageUriCache . has ( uri ) ;
120
123
} ,
121
- load ( uri : string , onLoad : Function , onError : Function ) : number {
124
+ load (
125
+ source : ImageSource ,
126
+ onLoad : ( ImageResult ) => void ,
127
+ onError : Function
128
+ ) : number {
122
129
id += 1 ;
123
130
const image = new window . Image ( ) ;
124
- image . onerror = onError ;
125
- image . onload = ( e ) => {
131
+
132
+ const handleLoad = ( e ) => {
126
133
// avoid blocking the main thread
127
- const onDecode = ( ) => onLoad ( { nativeEvent : e } ) ;
128
- if ( typeof image . decode === 'function' ) {
129
- // Safari currently throws exceptions when decoding svgs.
130
- // We want to catch that error and allow the load handler
131
- // to be forwarded to the onLoad handler in this case
132
- image . decode ( ) . then ( onDecode , onDecode ) ;
133
- } else {
134
- setTimeout ( onDecode , 0 ) ;
135
- }
134
+ const onDecode = ( ) =>
135
+ onLoad ( {
136
+ nativeEvent : e ,
137
+ uri : image . src ,
138
+ width : image . naturalWidth ,
139
+ height : image . naturalHeight
140
+ } ) ;
141
+
142
+ // Safari currently throws exceptions when decoding svgs.
143
+ // We want to catch that error and allow the load handler
144
+ // to be forwarded to the onLoad handler in this case
145
+ image . decode ( ) . then ( onDecode , onDecode ) ;
136
146
} ;
137
- image . src = uri ;
138
- requests [ `${ id } ` ] = image ;
147
+
148
+ image . onerror = onError ;
149
+ image . onload = handleLoad ;
150
+ requests [ id ] = { image } ;
151
+
152
+ // When headers are supplied we can't load the image through `image.src`, but we `fetch` it as an AJAX request
153
+ if ( source . headers ) {
154
+ const abortCtrl = new AbortController ( ) ;
155
+ const request = new Request ( source . uri , {
156
+ headers : source . headers ,
157
+ signal : abortCtrl . signal
158
+ } ) ;
159
+ request . headers . append ( 'accept' , 'image/*' ) ;
160
+
161
+ requests [ id ] . cleanup = ( ) => {
162
+ abortCtrl . abort ( ) ;
163
+ URL . revokeObjectURL ( image . src ) ;
164
+ } ;
165
+
166
+ fetch ( request )
167
+ . then ( ( response ) => response . blob ( ) )
168
+ . then ( ( blob ) => {
169
+ image . src = URL . createObjectURL ( blob ) ;
170
+ } )
171
+ . catch ( ( error ) => {
172
+ if ( error . name !== 'AbortError' ) onError ( error ) ;
173
+ } ) ;
174
+ } else {
175
+ // For simple request we load the image through `image.src` because it has wider support
176
+ // like better cross-origin support and progressive loading
177
+ image . src = source . uri ;
178
+ }
179
+
139
180
return id ;
140
181
} ,
141
182
prefetch ( uri : string ) : Promise < void > {
@@ -164,4 +205,13 @@ const ImageLoader = {
164
205
}
165
206
} ;
166
207
208
+ type ImageSource = { uri : string , headers ?: Record < string , string> } ;
209
+
210
+ type ImageResult = {
211
+ uri : string ,
212
+ width : number ,
213
+ height : number ,
214
+ nativeEvent : Event
215
+ } ;
216
+
167
217
export default ImageLoader ;
0 commit comments