react-native-fast-image 8.25.0 → 8.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -1
- package/android/build.gradle +3 -0
- package/android/consumer-rules.pro +5 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageOkHttpProgressGlideModule.java +6 -1
- package/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java +5 -2
- package/android/src/main/java/com/dylanvann/fastimage/FastImageSource.java +17 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageSvg.java +161 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageSvgRenderer.java +86 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewManager.java +6 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java +112 -3
- package/dist/index.cjs.js +20 -5
- package/dist/index.cjs.js.flow +4 -1
- package/dist/index.cjs.web.js +78 -3
- package/dist/index.cjs.web.js.flow +4 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +20 -5
- package/dist/index.js.flow +4 -1
- package/dist/index.web.js +79 -4
- package/dist/index.web.js.flow +4 -1
- package/ios/FastImage/FFFastImageSource.h +7 -0
- package/ios/FastImage/FFFastImageSource.m +46 -0
- package/ios/FastImage/FFFastImageView.h +3 -0
- package/ios/FastImage/FFFastImageView.m +111 -12
- package/ios/FastImage/FFFastImageViewManager.m +3 -2
- package/ios/FastImage/RCTConvert+FFFastImage.h +1 -0
- package/ios/FastImage/RCTConvert+FFFastImage.m +2 -0
- package/package.json +1 -1
package/dist/index.cjs.js.flow
CHANGED
|
@@ -104,6 +104,9 @@ export type FastImageSource = {
|
|
|
104
104
|
cache?: CacheControls,
|
|
105
105
|
cacheKey?: string,
|
|
106
106
|
memoryCache?: boolean,
|
|
107
|
+
width?: number,
|
|
108
|
+
height?: number,
|
|
109
|
+
scale?: number,
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
export type FastImageProps = $ReadOnly<{|
|
|
@@ -114,7 +117,7 @@ export type FastImageProps = $ReadOnly<{|
|
|
|
114
117
|
onLoadStart?: ?() => void,
|
|
115
118
|
onProgress?: ?(event: OnProgressEvent) => void,
|
|
116
119
|
|
|
117
|
-
source?: ?(FastImageSource | number),
|
|
120
|
+
source?: ?(FastImageSource | Array<FastImageSource> | number),
|
|
118
121
|
defaultSource?: ?number,
|
|
119
122
|
|
|
120
123
|
tintColor?: number | string,
|
package/dist/index.cjs.web.js
CHANGED
|
@@ -55,14 +55,27 @@ function FastImageBase({ source, defaultSource, tintColor, blurRadius, onLoadSta
|
|
|
55
55
|
const result = (0, react.useRef)(void 0);
|
|
56
56
|
const sent = (0, react.useRef)(Promise.resolve());
|
|
57
57
|
const image = (0, react.useRef)(null);
|
|
58
|
-
const
|
|
59
|
-
const
|
|
58
|
+
const sizes = Array.isArray(source) && source.length > 1 ? source : void 0;
|
|
59
|
+
const single = Array.isArray(source) ? source[0] : source;
|
|
60
|
+
const uri = typeof single === "object" && single ? single.uri : void 0;
|
|
61
|
+
const webSource = typeof single === "number" ? single : uri ? { uri } : void 0;
|
|
60
62
|
return /* @__PURE__ */ react.default.createElement(react_native.View, {
|
|
61
63
|
style: [styles.container, style],
|
|
62
64
|
onLayout,
|
|
63
65
|
pointerEvents,
|
|
64
66
|
ref: forwardedRef
|
|
65
|
-
}, /* @__PURE__ */ react.default.createElement(react_native.
|
|
67
|
+
}, sizes ? /* @__PURE__ */ react.default.createElement(react_native.View, {
|
|
68
|
+
...props,
|
|
69
|
+
style: styles.image
|
|
70
|
+
}, /* @__PURE__ */ react.default.createElement(ImageSizes, {
|
|
71
|
+
sources: sizes,
|
|
72
|
+
resizeMode: mode,
|
|
73
|
+
blurRadius,
|
|
74
|
+
onLoadStart,
|
|
75
|
+
onLoad,
|
|
76
|
+
onError,
|
|
77
|
+
onLoadEnd
|
|
78
|
+
})) : /* @__PURE__ */ react.default.createElement(react_native.Image, {
|
|
66
79
|
...props,
|
|
67
80
|
style: styles.image,
|
|
68
81
|
source: webSource,
|
|
@@ -108,6 +121,68 @@ function FastImageBase({ source, defaultSource, tintColor, blurRadius, onLoadSta
|
|
|
108
121
|
}
|
|
109
122
|
}), children);
|
|
110
123
|
}
|
|
124
|
+
function ImageSizes({ sources, resizeMode: mode, blurRadius, onLoadStart, onLoad, onError, onLoadEnd }) {
|
|
125
|
+
var _largest$uri, _sources$find;
|
|
126
|
+
const sized = sources.filter((source) => (source === null || source === void 0 ? void 0 : source.uri) && source.width).map((source) => {
|
|
127
|
+
var _source$width, _source$scale;
|
|
128
|
+
return {
|
|
129
|
+
uri: source.uri,
|
|
130
|
+
width: Math.round(((_source$width = source.width) !== null && _source$width !== void 0 ? _source$width : 0) * ((_source$scale = source.scale) !== null && _source$scale !== void 0 ? _source$scale : 1))
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
const largest = sized.reduce((best, source) => !best || source.width > best.width ? source : best, void 0);
|
|
134
|
+
const src = (_largest$uri = largest === null || largest === void 0 ? void 0 : largest.uri) !== null && _largest$uri !== void 0 ? _largest$uri : (_sources$find = sources.find((source) => source === null || source === void 0 ? void 0 : source.uri)) === null || _sources$find === void 0 ? void 0 : _sources$find.uri;
|
|
135
|
+
const srcSet = sized.length > 0 ? sized.map((source) => `${source.uri} ${source.width}w`).join(", ") : void 0;
|
|
136
|
+
(0, react.useEffect)(() => {
|
|
137
|
+
onLoadStart === null || onLoadStart === void 0 || onLoadStart();
|
|
138
|
+
}, [src, srcSet]);
|
|
139
|
+
return /* @__PURE__ */ react.default.createElement("img", {
|
|
140
|
+
src,
|
|
141
|
+
srcSet,
|
|
142
|
+
sizes: srcSet ? "auto, 100vw" : void 0,
|
|
143
|
+
loading: "lazy",
|
|
144
|
+
alt: "",
|
|
145
|
+
draggable: false,
|
|
146
|
+
style: {
|
|
147
|
+
position: "absolute",
|
|
148
|
+
width: "100%",
|
|
149
|
+
height: "100%",
|
|
150
|
+
objectFit: OBJECT_FIT[mode !== null && mode !== void 0 ? mode : "cover"],
|
|
151
|
+
filter: blurRadius ? `blur(${blurRadius}px)` : void 0
|
|
152
|
+
},
|
|
153
|
+
onLoad: (event) => {
|
|
154
|
+
var _picked$width;
|
|
155
|
+
const image = event.currentTarget;
|
|
156
|
+
const picked = srcSet ? sized.find((source) => new URL(source.uri, image.currentSrc).href === image.currentSrc) : void 0;
|
|
157
|
+
const loadedWidth = (_picked$width = picked === null || picked === void 0 ? void 0 : picked.width) !== null && _picked$width !== void 0 ? _picked$width : image.naturalWidth;
|
|
158
|
+
const loadedHeight = picked ? Math.round(image.naturalHeight * picked.width / image.naturalWidth) : image.naturalHeight;
|
|
159
|
+
onLoad === null || onLoad === void 0 || onLoad({ nativeEvent: {
|
|
160
|
+
width: loadedWidth,
|
|
161
|
+
height: loadedHeight
|
|
162
|
+
} });
|
|
163
|
+
onLoadEnd === null || onLoadEnd === void 0 || onLoadEnd({
|
|
164
|
+
ok: true,
|
|
165
|
+
width: loadedWidth,
|
|
166
|
+
height: loadedHeight
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
onError: () => {
|
|
170
|
+
const error = "Failed to load the image";
|
|
171
|
+
onError === null || onError === void 0 || onError({ nativeEvent: { error } });
|
|
172
|
+
onLoadEnd === null || onLoadEnd === void 0 || onLoadEnd({
|
|
173
|
+
ok: false,
|
|
174
|
+
error
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
const OBJECT_FIT = {
|
|
180
|
+
cover: "cover",
|
|
181
|
+
contain: "contain",
|
|
182
|
+
stretch: "fill",
|
|
183
|
+
center: "scale-down",
|
|
184
|
+
repeat: "cover"
|
|
185
|
+
};
|
|
111
186
|
const FastImageMemo = (0, react.memo)(FastImageBase);
|
|
112
187
|
const FastImageComponent = (0, react.forwardRef)((props, ref) => /* @__PURE__ */ react.default.createElement(FastImageMemo, {
|
|
113
188
|
forwardedRef: ref,
|
|
@@ -104,6 +104,9 @@ export type FastImageSource = {
|
|
|
104
104
|
cache?: CacheControls,
|
|
105
105
|
cacheKey?: string,
|
|
106
106
|
memoryCache?: boolean,
|
|
107
|
+
width?: number,
|
|
108
|
+
height?: number,
|
|
109
|
+
scale?: number,
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
export type FastImageProps = $ReadOnly<{|
|
|
@@ -114,7 +117,7 @@ export type FastImageProps = $ReadOnly<{|
|
|
|
114
117
|
onLoadStart?: ?() => void,
|
|
115
118
|
onProgress?: ?(event: OnProgressEvent) => void,
|
|
116
119
|
|
|
117
|
-
source?: ?(FastImageSource | number),
|
|
120
|
+
source?: ?(FastImageSource | Array<FastImageSource> | number),
|
|
118
121
|
defaultSource?: ?number,
|
|
119
122
|
|
|
120
123
|
tintColor?: number | string,
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,14 @@ export type Source = {
|
|
|
26
26
|
* list scrolled back decodes them again.
|
|
27
27
|
*/
|
|
28
28
|
memoryCache?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* With several sources (`source` as an array), the image's size at this
|
|
31
|
+
* uri in pixels (width and height, times `scale` if given): the view
|
|
32
|
+
* loads the source closest to its own size.
|
|
33
|
+
*/
|
|
34
|
+
width?: number;
|
|
35
|
+
height?: number;
|
|
36
|
+
scale?: number;
|
|
29
37
|
};
|
|
30
38
|
export type Transition = {
|
|
31
39
|
/**
|
|
@@ -116,7 +124,12 @@ export interface ImageStyle extends ViewStyle {
|
|
|
116
124
|
opacity?: number;
|
|
117
125
|
}
|
|
118
126
|
export interface FastImageProps extends AccessibilityProps, ViewProps {
|
|
119
|
-
|
|
127
|
+
/**
|
|
128
|
+
* The image: a source, a `require()`d image, or several sources of the
|
|
129
|
+
* same image at different sizes (each with its `width` and `height` in
|
|
130
|
+
* pixels), of which the view loads the one closest to its size.
|
|
131
|
+
*/
|
|
132
|
+
source?: Source | Source[] | ImageRequireSource;
|
|
120
133
|
defaultSource?: ImageRequireSource;
|
|
121
134
|
resizeMode?: ResizeMode;
|
|
122
135
|
fallback?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -98,7 +98,7 @@ function FastImageBase({ source, defaultSource, tintColor, onLoadStart, onProgre
|
|
|
98
98
|
};
|
|
99
99
|
const resolvedTintColor = tintColor != null ? tintColor : tintColorFromStyle(style);
|
|
100
100
|
if (fallback) {
|
|
101
|
-
const cleanedSource = typeof source === "number" ? source : withoutCache(source);
|
|
101
|
+
const cleanedSource = typeof source === "number" ? source : Array.isArray(source) ? source.map(withoutCache) : withoutCache(source);
|
|
102
102
|
const resolvedSource = Image.resolveAssetSource(cleanedSource);
|
|
103
103
|
return /* @__PURE__ */ React.createElement(View, {
|
|
104
104
|
style: [styles.imageContainer, style],
|
|
@@ -137,7 +137,9 @@ function FastImageBase({ source, defaultSource, tintColor, onLoadStart, onProgre
|
|
|
137
137
|
resizeMode
|
|
138
138
|
}), children);
|
|
139
139
|
}
|
|
140
|
-
const
|
|
140
|
+
const sources = Array.isArray(source) ? source.length === 1 ? void 0 : source : void 0;
|
|
141
|
+
const single = Array.isArray(source) ? source[0] : source;
|
|
142
|
+
const resolvedSource = sources ? void 0 : Image.resolveAssetSource(single);
|
|
141
143
|
const resolvedDefaultSource = resolveDefaultSource(defaultSource);
|
|
142
144
|
return /* @__PURE__ */ React.createElement(View, {
|
|
143
145
|
style: [styles.imageContainer, style],
|
|
@@ -150,6 +152,7 @@ function FastImageBase({ source, defaultSource, tintColor, onLoadStart, onProgre
|
|
|
150
152
|
...transitionProps(transition),
|
|
151
153
|
style: StyleSheet.absoluteFill,
|
|
152
154
|
source: resolvedSource,
|
|
155
|
+
sources,
|
|
153
156
|
defaultSource: resolvedDefaultSource,
|
|
154
157
|
onFastImageLoadStart: onLoadStart,
|
|
155
158
|
onFastImageProgress: withProgress(onProgress),
|
|
@@ -173,8 +176,14 @@ const FastImage = FastImageComponent;
|
|
|
173
176
|
FastImage.resizeMode = resizeMode;
|
|
174
177
|
FastImage.cacheControl = cacheControl;
|
|
175
178
|
FastImage.priority = priority;
|
|
176
|
-
|
|
179
|
+
const ONE_SOURCE = "Takes one source, not an array of sizes: pass the size to use";
|
|
180
|
+
FastImage.preload = (sources) => Promise.resolve(NativeModules.FastImageView.preload(sources.map((s) => s && !Array.isArray(s) ? s : {}))).then((results) => sources.map((source, i) => {
|
|
177
181
|
var _results$i;
|
|
182
|
+
if (Array.isArray(source)) return {
|
|
183
|
+
ok: false,
|
|
184
|
+
error: ONE_SOURCE,
|
|
185
|
+
uri: void 0
|
|
186
|
+
};
|
|
178
187
|
const uri = source ? source.uri : void 0;
|
|
179
188
|
const result = (_results$i = results === null || results === void 0 ? void 0 : results[i]) !== null && _results$i !== void 0 ? _results$i : noResult;
|
|
180
189
|
if (!result.ok) return {
|
|
@@ -193,8 +202,14 @@ FastImage.preload = (sources) => Promise.resolve(NativeModules.FastImageView.pre
|
|
|
193
202
|
FastImage.clearMemoryCache = () => NativeModules.FastImageView.clearMemoryCache();
|
|
194
203
|
FastImage.clearDiskCache = () => NativeModules.FastImageView.clearDiskCache();
|
|
195
204
|
FastImage.configureCache = (limits = {}) => Promise.resolve(NativeModules.FastImageView.configureCache(limits));
|
|
196
|
-
FastImage.writeToCache = (source, file) =>
|
|
197
|
-
|
|
205
|
+
FastImage.writeToCache = (source, file) => Array.isArray(source) ? Promise.resolve({
|
|
206
|
+
ok: false,
|
|
207
|
+
error: ONE_SOURCE
|
|
208
|
+
}) : Promise.resolve(NativeModules.FastImageView.writeToCache(source || {}, file));
|
|
209
|
+
FastImage.getCachePath = (source) => Array.isArray(source) ? Promise.resolve({
|
|
210
|
+
ok: false,
|
|
211
|
+
error: ONE_SOURCE
|
|
212
|
+
}) : Promise.resolve(NativeModules.FastImageView.getCachePath(source || {}));
|
|
198
213
|
const styles = StyleSheet.create({
|
|
199
214
|
fallbackImage: {
|
|
200
215
|
position: "absolute",
|
package/dist/index.js.flow
CHANGED
|
@@ -104,6 +104,9 @@ export type FastImageSource = {
|
|
|
104
104
|
cache?: CacheControls,
|
|
105
105
|
cacheKey?: string,
|
|
106
106
|
memoryCache?: boolean,
|
|
107
|
+
width?: number,
|
|
108
|
+
height?: number,
|
|
109
|
+
scale?: number,
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
export type FastImageProps = $ReadOnly<{|
|
|
@@ -114,7 +117,7 @@ export type FastImageProps = $ReadOnly<{|
|
|
|
114
117
|
onLoadStart?: ?() => void,
|
|
115
118
|
onProgress?: ?(event: OnProgressEvent) => void,
|
|
116
119
|
|
|
117
|
-
source?: ?(FastImageSource | number),
|
|
120
|
+
source?: ?(FastImageSource | Array<FastImageSource> | number),
|
|
118
121
|
defaultSource?: ?number,
|
|
119
122
|
|
|
120
123
|
tintColor?: number | string,
|
package/dist/index.web.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { forwardRef, memo, useRef } from "react";
|
|
1
|
+
import React, { forwardRef, memo, useEffect, useRef } from "react";
|
|
2
2
|
import { Image, StyleSheet, View } from "react-native";
|
|
3
3
|
//#region src/constants.ts
|
|
4
4
|
const resizeMode = {
|
|
@@ -28,14 +28,27 @@ function FastImageBase({ source, defaultSource, tintColor, blurRadius, onLoadSta
|
|
|
28
28
|
const result = useRef(void 0);
|
|
29
29
|
const sent = useRef(Promise.resolve());
|
|
30
30
|
const image = useRef(null);
|
|
31
|
-
const
|
|
32
|
-
const
|
|
31
|
+
const sizes = Array.isArray(source) && source.length > 1 ? source : void 0;
|
|
32
|
+
const single = Array.isArray(source) ? source[0] : source;
|
|
33
|
+
const uri = typeof single === "object" && single ? single.uri : void 0;
|
|
34
|
+
const webSource = typeof single === "number" ? single : uri ? { uri } : void 0;
|
|
33
35
|
return /* @__PURE__ */ React.createElement(View, {
|
|
34
36
|
style: [styles.container, style],
|
|
35
37
|
onLayout,
|
|
36
38
|
pointerEvents,
|
|
37
39
|
ref: forwardedRef
|
|
38
|
-
}, /* @__PURE__ */ React.createElement(
|
|
40
|
+
}, sizes ? /* @__PURE__ */ React.createElement(View, {
|
|
41
|
+
...props,
|
|
42
|
+
style: styles.image
|
|
43
|
+
}, /* @__PURE__ */ React.createElement(ImageSizes, {
|
|
44
|
+
sources: sizes,
|
|
45
|
+
resizeMode: mode,
|
|
46
|
+
blurRadius,
|
|
47
|
+
onLoadStart,
|
|
48
|
+
onLoad,
|
|
49
|
+
onError,
|
|
50
|
+
onLoadEnd
|
|
51
|
+
})) : /* @__PURE__ */ React.createElement(Image, {
|
|
39
52
|
...props,
|
|
40
53
|
style: styles.image,
|
|
41
54
|
source: webSource,
|
|
@@ -81,6 +94,68 @@ function FastImageBase({ source, defaultSource, tintColor, blurRadius, onLoadSta
|
|
|
81
94
|
}
|
|
82
95
|
}), children);
|
|
83
96
|
}
|
|
97
|
+
function ImageSizes({ sources, resizeMode: mode, blurRadius, onLoadStart, onLoad, onError, onLoadEnd }) {
|
|
98
|
+
var _largest$uri, _sources$find;
|
|
99
|
+
const sized = sources.filter((source) => (source === null || source === void 0 ? void 0 : source.uri) && source.width).map((source) => {
|
|
100
|
+
var _source$width, _source$scale;
|
|
101
|
+
return {
|
|
102
|
+
uri: source.uri,
|
|
103
|
+
width: Math.round(((_source$width = source.width) !== null && _source$width !== void 0 ? _source$width : 0) * ((_source$scale = source.scale) !== null && _source$scale !== void 0 ? _source$scale : 1))
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
const largest = sized.reduce((best, source) => !best || source.width > best.width ? source : best, void 0);
|
|
107
|
+
const src = (_largest$uri = largest === null || largest === void 0 ? void 0 : largest.uri) !== null && _largest$uri !== void 0 ? _largest$uri : (_sources$find = sources.find((source) => source === null || source === void 0 ? void 0 : source.uri)) === null || _sources$find === void 0 ? void 0 : _sources$find.uri;
|
|
108
|
+
const srcSet = sized.length > 0 ? sized.map((source) => `${source.uri} ${source.width}w`).join(", ") : void 0;
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
onLoadStart === null || onLoadStart === void 0 || onLoadStart();
|
|
111
|
+
}, [src, srcSet]);
|
|
112
|
+
return /* @__PURE__ */ React.createElement("img", {
|
|
113
|
+
src,
|
|
114
|
+
srcSet,
|
|
115
|
+
sizes: srcSet ? "auto, 100vw" : void 0,
|
|
116
|
+
loading: "lazy",
|
|
117
|
+
alt: "",
|
|
118
|
+
draggable: false,
|
|
119
|
+
style: {
|
|
120
|
+
position: "absolute",
|
|
121
|
+
width: "100%",
|
|
122
|
+
height: "100%",
|
|
123
|
+
objectFit: OBJECT_FIT[mode !== null && mode !== void 0 ? mode : "cover"],
|
|
124
|
+
filter: blurRadius ? `blur(${blurRadius}px)` : void 0
|
|
125
|
+
},
|
|
126
|
+
onLoad: (event) => {
|
|
127
|
+
var _picked$width;
|
|
128
|
+
const image = event.currentTarget;
|
|
129
|
+
const picked = srcSet ? sized.find((source) => new URL(source.uri, image.currentSrc).href === image.currentSrc) : void 0;
|
|
130
|
+
const loadedWidth = (_picked$width = picked === null || picked === void 0 ? void 0 : picked.width) !== null && _picked$width !== void 0 ? _picked$width : image.naturalWidth;
|
|
131
|
+
const loadedHeight = picked ? Math.round(image.naturalHeight * picked.width / image.naturalWidth) : image.naturalHeight;
|
|
132
|
+
onLoad === null || onLoad === void 0 || onLoad({ nativeEvent: {
|
|
133
|
+
width: loadedWidth,
|
|
134
|
+
height: loadedHeight
|
|
135
|
+
} });
|
|
136
|
+
onLoadEnd === null || onLoadEnd === void 0 || onLoadEnd({
|
|
137
|
+
ok: true,
|
|
138
|
+
width: loadedWidth,
|
|
139
|
+
height: loadedHeight
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
onError: () => {
|
|
143
|
+
const error = "Failed to load the image";
|
|
144
|
+
onError === null || onError === void 0 || onError({ nativeEvent: { error } });
|
|
145
|
+
onLoadEnd === null || onLoadEnd === void 0 || onLoadEnd({
|
|
146
|
+
ok: false,
|
|
147
|
+
error
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
const OBJECT_FIT = {
|
|
153
|
+
cover: "cover",
|
|
154
|
+
contain: "contain",
|
|
155
|
+
stretch: "fill",
|
|
156
|
+
center: "scale-down",
|
|
157
|
+
repeat: "cover"
|
|
158
|
+
};
|
|
84
159
|
const FastImageMemo = memo(FastImageBase);
|
|
85
160
|
const FastImageComponent = forwardRef((props, ref) => /* @__PURE__ */ React.createElement(FastImageMemo, {
|
|
86
161
|
forwardedRef: ref,
|
package/dist/index.web.js.flow
CHANGED
|
@@ -104,6 +104,9 @@ export type FastImageSource = {
|
|
|
104
104
|
cache?: CacheControls,
|
|
105
105
|
cacheKey?: string,
|
|
106
106
|
memoryCache?: boolean,
|
|
107
|
+
width?: number,
|
|
108
|
+
height?: number,
|
|
109
|
+
scale?: number,
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
export type FastImageProps = $ReadOnly<{|
|
|
@@ -114,7 +117,7 @@ export type FastImageProps = $ReadOnly<{|
|
|
|
114
117
|
onLoadStart?: ?() => void,
|
|
115
118
|
onProgress?: ?(event: OnProgressEvent) => void,
|
|
116
119
|
|
|
117
|
-
source?: ?(FastImageSource | number),
|
|
120
|
+
source?: ?(FastImageSource | Array<FastImageSource> | number),
|
|
118
121
|
defaultSource?: ?number,
|
|
119
122
|
|
|
120
123
|
tintColor?: number | string,
|
|
@@ -32,6 +32,9 @@ typedef NS_ENUM(NSInteger, FFFCacheControl) {
|
|
|
32
32
|
@property (nonatomic) FFFCacheControl cacheControl;
|
|
33
33
|
// The key to cache the image under instead of its url, or nil.
|
|
34
34
|
@property (nonatomic, copy) NSString *cacheKey;
|
|
35
|
+
// With several sources, the image's size at this url in pixels (width ×
|
|
36
|
+
// height × scale²), to pick the one closest to the view's size; 0 if unknown.
|
|
37
|
+
@property (nonatomic) CGFloat pixelCount;
|
|
35
38
|
// Whether the decoded image is kept in the memory cache (`memoryCache`,
|
|
36
39
|
// default YES). Without it, it's only kept on disk: views don't leave it in
|
|
37
40
|
// memory, and preload doesn't decode it.
|
|
@@ -61,6 +64,10 @@ typedef NS_ENUM(NSInteger, FFFCacheControl) {
|
|
|
61
64
|
// which is decoded at about the view's size.
|
|
62
65
|
- (BOOL)isPhotoLibrary;
|
|
63
66
|
|
|
67
|
+
// The error's message, or for an SVG image that couldn't be decoded because
|
|
68
|
+
// the app doesn't have SDWebImageSVGCoder, what to add.
|
|
69
|
+
- (NSString *)errorMessage:(NSError *)error;
|
|
70
|
+
|
|
64
71
|
// A photo library image's own size in pixels (for onLoad when it comes from
|
|
65
72
|
// a cache, where the size it was decoded from isn't recorded), or zero.
|
|
66
73
|
- (CGSize)photoPixelSize;
|
|
@@ -5,8 +5,12 @@
|
|
|
5
5
|
#import <SDWebImage/SDWebImageError.h>
|
|
6
6
|
#import <SDWebImage/SDWebImageDownloaderOperation.h>
|
|
7
7
|
#import <SDWebImage/NSData+ImageContentType.h>
|
|
8
|
+
#import <SDWebImage/SDImageCodersManager.h>
|
|
8
9
|
#import <objc/message.h>
|
|
9
10
|
|
|
11
|
+
// In FFFastImageView.m.
|
|
12
|
+
FOUNDATION_EXTERN NSString *FFFErrorMessage(NSError *error);
|
|
13
|
+
|
|
10
14
|
static NSUInteger const FFFWebCacheSize = 50 * 1024 * 1024;
|
|
11
15
|
|
|
12
16
|
// The photo's own size in pixels, looked up by its identifier (the url after
|
|
@@ -173,8 +177,41 @@ static CGSize FFFPhotoPixelSize(NSURL *url)
|
|
|
173
177
|
|
|
174
178
|
@end
|
|
175
179
|
|
|
180
|
+
// SDWebImageSVGCoder's coder, when the app has the pod (SVG images): found at
|
|
181
|
+
// runtime, so FastImage doesn't depend on it. Nil otherwise.
|
|
182
|
+
static id<SDImageCoder> FFFSVGCoder;
|
|
183
|
+
|
|
176
184
|
@implementation FFFastImageSource
|
|
177
185
|
|
|
186
|
+
+ (void)initialize
|
|
187
|
+
{
|
|
188
|
+
if (self != [FFFastImageSource class]) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
// Registered with SDWebImage's coders, as the pod's setup does (unless
|
|
192
|
+
// the app has already): SDWebImage then picks it for SVG data, including
|
|
193
|
+
// for FastImage's animated and downsampled image classes. Giving the
|
|
194
|
+
// coder per load instead (SDWebImageContextImageCoder) would make it the
|
|
195
|
+
// only coder for everything in that load. So the app's own SDWebImage
|
|
196
|
+
// use can decode SVG too, which is what the pod is for.
|
|
197
|
+
Class coderClass = NSClassFromString(@"SDImageSVGCoder");
|
|
198
|
+
SEL shared = NSSelectorFromString(@"sharedCoder");
|
|
199
|
+
if (!coderClass || ![coderClass respondsToSelector:shared]) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
id coder = ((id (*)(id, SEL))objc_msgSend)(coderClass, shared);
|
|
203
|
+
if (![coder conformsToProtocol:@protocol(SDImageCoder)]) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
FFFSVGCoder = coder;
|
|
207
|
+
for (id<SDImageCoder> registered in SDImageCodersManager.sharedManager.coders) {
|
|
208
|
+
if ([registered isKindOfClass:coderClass]) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
[SDImageCodersManager.sharedManager addCoder:coder];
|
|
213
|
+
}
|
|
214
|
+
|
|
178
215
|
- (instancetype)initWithURL:(NSURL *)url
|
|
179
216
|
priority:(FFFPriority)priority
|
|
180
217
|
headers:(NSDictionary *)headers
|
|
@@ -216,6 +253,15 @@ static CGSize FFFPhotoPixelSize(NSURL *url)
|
|
|
216
253
|
return 0;
|
|
217
254
|
}
|
|
218
255
|
|
|
256
|
+
- (NSString *)errorMessage:(NSError *)error
|
|
257
|
+
{
|
|
258
|
+
if (!FFFSVGCoder && [error.domain isEqualToString:SDWebImageErrorDomain] && error.code == SDWebImageErrorBadImageData &&
|
|
259
|
+
[_url.path.lowercaseString hasSuffix:@".svg"]) {
|
|
260
|
+
return @"SVG images need SDWebImageSVGCoder: add pod 'SDWebImageSVGCoder' to the app's Podfile";
|
|
261
|
+
}
|
|
262
|
+
return FFFErrorMessage(error);
|
|
263
|
+
}
|
|
264
|
+
|
|
219
265
|
- (BOOL)isPhotoLibrary
|
|
220
266
|
{
|
|
221
267
|
return [_url.scheme isEqualToString:@"ph"];
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
@property (nonatomic, copy) RCTDirectEventBlock onFastImageLoadEnd;
|
|
18
18
|
@property (nonatomic, assign) RCTResizeMode resizeMode;
|
|
19
19
|
@property (nonatomic, strong) FFFastImageSource *source;
|
|
20
|
+
// Several sources (2 or more) of the same image at different sizes: the view
|
|
21
|
+
// loads the one whose size is closest to its own.
|
|
22
|
+
@property (nonatomic, copy) NSArray<FFFastImageSource *> *sources;
|
|
20
23
|
@property (nonatomic, strong) UIImage *defaultSource;
|
|
21
24
|
@property (nonatomic, strong) UIColor *imageColor;
|
|
22
25
|
// When it changes, the next image doesn't replace the current one: the view
|