react-native-fast-image 10.1.1 → 10.3.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 +107 -14
- package/android/src/main/java/com/fastimage/FastImageBlur.java +3 -2
- package/android/src/main/java/com/fastimage/FastImageThumbHash.java +246 -0
- package/android/src/main/java/com/fastimage/FastImageViewConverter.java +1 -1
- package/android/src/main/java/com/fastimage/FastImageViewManager.java +20 -5
- package/android/src/main/java/com/fastimage/FastImageViewWithUrl.java +125 -48
- package/dist/index.cjs.js +47 -9
- package/dist/index.cjs.js.flow +11 -0
- package/dist/index.cjs.web.js +67 -14
- package/dist/index.cjs.web.js.flow +11 -0
- package/dist/index.d.ts +77 -13
- package/dist/index.js +48 -10
- package/dist/index.js.flow +11 -0
- package/dist/index.web.js +68 -15
- package/dist/index.web.js.flow +11 -0
- package/dist/placeholders.d.ts +4 -0
- package/dist/specs/FastImageViewNativeComponent.d.ts +2 -1
- package/dist/thumbhash.cjs.js +241 -0
- package/dist/thumbhash.d.ts +12 -0
- package/dist/thumbhash.js +240 -0
- package/ios/FastImage/FFFBoxBlur.h +7 -0
- package/ios/FastImage/FFFBoxBlur.m +29 -0
- package/ios/FastImage/FFFThumbHash.h +24 -0
- package/ios/FastImage/FFFThumbHash.m +269 -0
- package/ios/FastImage/FFFastImageView.h +7 -1
- package/ios/FastImage/FFFastImageView.m +93 -46
- package/ios/FastImage/FFFastImageViewComponentView.mm +11 -4
- package/package.json +4 -2
- package/src/index.js.flow +11 -0
- package/src/index.tsx +166 -31
- package/src/index.web.tsx +166 -33
- package/src/placeholders.ts +45 -0
- package/src/specs/FastImageViewNativeComponent.ts +8 -2
- package/src/thumbhash.ts +363 -0
- package/thumbhash/package.json +8 -0
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,8 @@ export type Source = {
|
|
|
35
35
|
* - `'normal'`: the default.
|
|
36
36
|
* - `'high'`: e.g. the image the screen is about.
|
|
37
37
|
*
|
|
38
|
+
* On the web, it's the image's `fetchpriority`.
|
|
39
|
+
*
|
|
38
40
|
* @default 'normal'
|
|
39
41
|
*/
|
|
40
42
|
priority?: Priority;
|
|
@@ -111,6 +113,32 @@ export type Source = {
|
|
|
111
113
|
* @default true
|
|
112
114
|
*/
|
|
113
115
|
diskCache?: boolean;
|
|
116
|
+
/**
|
|
117
|
+
* A [ThumbHash](https://evanw.github.io/thumbhash/) of the image, shown
|
|
118
|
+
* blurred while the image loads, and if it fails to load. It's a short
|
|
119
|
+
* string you make when the image is uploaded (e.g. with the `thumbhash`
|
|
120
|
+
* package) and send with the image's url.
|
|
121
|
+
*
|
|
122
|
+
* On the web, it needs `import 'react-native-fast-image/thumbhash'` (see
|
|
123
|
+
* [ThumbHashes on the web](#thumbhashes-on-the-web)).
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```jsx
|
|
127
|
+
* <FastImage source={{ uri: photo.url, thumbhash: photo.thumbhash }} />
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
thumbhash?: string;
|
|
131
|
+
/**
|
|
132
|
+
* A tiny version of the image (e.g. 8 to 20 px) as a data uri, shown
|
|
133
|
+
* blurred while the image loads, and if it fails to load. On the web it
|
|
134
|
+
* shows without any JavaScript, e.g. in a server-rendered page.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```jsx
|
|
138
|
+
* <FastImage source={{ uri: photo.url, blurDataUri: photo.lqip }} />
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
blurDataUri?: string;
|
|
114
142
|
/**
|
|
115
143
|
* With several sources (`source` as an array), the image's width at this
|
|
116
144
|
* uri, in pixels (times `scale`, if given).
|
|
@@ -124,6 +152,8 @@ export type Source = {
|
|
|
124
152
|
/** With several sources, the scale `width` and `height` are multiplied by. */
|
|
125
153
|
scale?: number;
|
|
126
154
|
};
|
|
155
|
+
/** A `thumbhash` or `blurDataUri` as `placeholder`. */
|
|
156
|
+
export type SourcePlaceholder = Pick<Source, 'thumbhash' | 'blurDataUri'>;
|
|
127
157
|
/** How the image fades in: see [`transition`](#transition). */
|
|
128
158
|
export type Transition = {
|
|
129
159
|
/**
|
|
@@ -135,7 +165,7 @@ export type Transition = {
|
|
|
135
165
|
/**
|
|
136
166
|
* Also fades between images: a new `source` replacing an image that's
|
|
137
167
|
* showing cross-dissolves from it. Otherwise only an image that appears
|
|
138
|
-
* over nothing (or over
|
|
168
|
+
* over nothing (or over a placeholder) fades in, and a new source
|
|
139
169
|
* replaces the image showing at once, once it has loaded.
|
|
140
170
|
* @default false
|
|
141
171
|
*/
|
|
@@ -303,14 +333,29 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
|
|
|
303
333
|
*/
|
|
304
334
|
source?: Source | Source[] | ImageRequireSource;
|
|
305
335
|
/**
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
* `
|
|
336
|
+
* A placeholder shown while the image loads, and if it fails to load:
|
|
337
|
+
*
|
|
338
|
+
* - An image loaded with `require()`: `placeholder={require('./gray.png')}`.
|
|
339
|
+
* - A data uri: `placeholder="data:image/png;base64,…"`.
|
|
340
|
+
* - The name of an image bundled with the app (an iOS asset catalog image
|
|
341
|
+
* or an Android drawable): `placeholder="gray"`.
|
|
342
|
+
* - A [ThumbHash](#sourcethumbhash), shown blurred:
|
|
343
|
+
* `placeholder={{ thumbhash }}`.
|
|
344
|
+
* - A tiny image as a data uri, shown blurred:
|
|
345
|
+
* `placeholder={{ blurDataUri }}`.
|
|
346
|
+
*
|
|
347
|
+
* A source's own `thumbhash` or `blurDataUri` is shown instead, if it has
|
|
348
|
+
* one.
|
|
310
349
|
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
350
|
+
* Notes:
|
|
351
|
+
*
|
|
352
|
+
* - A url isn't loaded: it's ignored, with a warning in development.
|
|
353
|
+
* - On Android, a `require()`d image doesn't show in debug builds, where
|
|
354
|
+
* the dev server serves it. Release builds show it.
|
|
355
|
+
*/
|
|
356
|
+
placeholder?: ImageRequireSource | string | SourcePlaceholder;
|
|
357
|
+
/**
|
|
358
|
+
* @deprecated Use `placeholder`, which does the same.
|
|
314
359
|
*/
|
|
315
360
|
defaultSource?: ImageRequireSource;
|
|
316
361
|
/**
|
|
@@ -353,8 +398,8 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
|
|
|
353
398
|
* `{ right: 10, bottom: '20%' }`. A missing axis is centered.
|
|
354
399
|
*
|
|
355
400
|
* Points are dp on Android. An invalid value is ignored (the image is
|
|
356
|
-
* centered), with a warning in development.
|
|
357
|
-
*
|
|
401
|
+
* centered), with a warning in development. Placeholders are placed the
|
|
402
|
+
* same way.
|
|
358
403
|
*
|
|
359
404
|
* @default '50% 50%'
|
|
360
405
|
* @example
|
|
@@ -367,7 +412,7 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
|
|
|
367
412
|
* For views that get reused for other content, such as rows in FlashList
|
|
368
413
|
* or recyclerlistview. Set it to something that identifies the content,
|
|
369
414
|
* e.g. the item's id. When it changes, the image is cleared right away (to
|
|
370
|
-
* `
|
|
415
|
+
* `placeholder`, or blank) instead of staying until the new one has
|
|
371
416
|
* loaded. Unlike changing `key`, this keeps the view, which is what list
|
|
372
417
|
* recycling is for.
|
|
373
418
|
*
|
|
@@ -409,6 +454,25 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
|
|
|
409
454
|
* @default 'auto'
|
|
410
455
|
*/
|
|
411
456
|
imageRendering?: 'auto' | 'smooth' | 'pixelated';
|
|
457
|
+
/**
|
|
458
|
+
* Whether the image loads as it nears the screen (`'lazy'`) or at once
|
|
459
|
+
* (`'eager'`).
|
|
460
|
+
*
|
|
461
|
+
* @default 'lazy'
|
|
462
|
+
* @platform web
|
|
463
|
+
*/
|
|
464
|
+
loading?: 'lazy' | 'eager';
|
|
465
|
+
/**
|
|
466
|
+
* With several sources, the width the image is shown at, as the image's
|
|
467
|
+
* [`sizes`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/img#sizes),
|
|
468
|
+
* so the browser loads the size that fits. The default is the width the
|
|
469
|
+
* image is laid out at, which needs `loading="lazy"` (otherwise, or in
|
|
470
|
+
* browsers without it, the window's width).
|
|
471
|
+
*
|
|
472
|
+
* @default 'auto, 100vw'
|
|
473
|
+
* @platform web
|
|
474
|
+
*/
|
|
475
|
+
sizes?: string;
|
|
412
476
|
/**
|
|
413
477
|
* Pauses an animated image (GIF, animated WebP, APNG, or animated AVIF) on
|
|
414
478
|
* the frame it's showing; `false` plays it again from there
|
|
@@ -477,7 +541,7 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
|
|
|
477
541
|
* blurring a photo behind a sheet). Each change blurs the image again on
|
|
478
542
|
* the CPU, so don't animate it.
|
|
479
543
|
*
|
|
480
|
-
* - Only the loaded image is blurred, not
|
|
544
|
+
* - Only the loaded image is blurred, not a placeholder.
|
|
481
545
|
* - An animated image (GIF, animated WebP, APNG or AVIF) shows its first frame,
|
|
482
546
|
* blurred, and doesn't animate.
|
|
483
547
|
* - With `tintColor`, the blurred image is tinted.
|
|
@@ -559,7 +623,7 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
|
|
|
559
623
|
style?: StyleProp<ImageStyle>;
|
|
560
624
|
/**
|
|
561
625
|
* If supplied, changes the color of all the non-transparent pixels to the
|
|
562
|
-
* given color.
|
|
626
|
+
* given color, including the placeholder.
|
|
563
627
|
*/
|
|
564
628
|
tintColor?: ColorValue;
|
|
565
629
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { Children, forwardRef, memo } from "react";
|
|
1
|
+
import React, { Children, forwardRef, memo, useMemo } from "react";
|
|
2
2
|
import { Image, Platform, StyleSheet, TurboModuleRegistry, View, codegenNativeComponent } from "react-native";
|
|
3
3
|
//#region src/constants.ts
|
|
4
4
|
const resizeMode = {
|
|
@@ -233,10 +233,39 @@ var FastImageViewNativeComponent_default = codegenNativeComponent("FastImageView
|
|
|
233
233
|
//#region src/specs/NativeFastImageModule.ts
|
|
234
234
|
var NativeFastImageModule_default = TurboModuleRegistry.get("FastImageModule");
|
|
235
235
|
//#endregion
|
|
236
|
+
//#region src/placeholders.ts
|
|
237
|
+
function isImageName(value) {
|
|
238
|
+
return !/^[a-z][a-z\d+.-]*:/i.test(value) && !value.startsWith("/");
|
|
239
|
+
}
|
|
240
|
+
function hasSourcePlaceholder(value) {
|
|
241
|
+
return !!value && typeof value === "object" && ("thumbhash" in value || "blurDataUri" in value);
|
|
242
|
+
}
|
|
243
|
+
function dataUri(value) {
|
|
244
|
+
if (typeof value !== "string" || value === "") return void 0;
|
|
245
|
+
if (/^data:/i.test(value)) return value;
|
|
246
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) warnOnce("placeholder-url", `react-native-fast-image: \`placeholder\` and \`blurDataUri\` don't load urls: "${value.slice(0, 60)}" is ignored. Use a data uri (data:image/…), or for \`placeholder\`, a require()d image or the name of an image bundled with the app.`, "warn");
|
|
247
|
+
}
|
|
248
|
+
//#endregion
|
|
236
249
|
//#region src/index.tsx
|
|
237
|
-
|
|
238
|
-
if (!
|
|
239
|
-
const
|
|
250
|
+
function withoutPlaceholders(source) {
|
|
251
|
+
if (!hasSourcePlaceholder(source)) return source;
|
|
252
|
+
const { thumbhash: _thumbhash, blurDataUri: _blurDataUri, ...rest } = source;
|
|
253
|
+
return rest;
|
|
254
|
+
}
|
|
255
|
+
function sourcePlaceholder(source) {
|
|
256
|
+
if (!source || typeof source !== "object") return "";
|
|
257
|
+
const { thumbhash, blurDataUri } = source;
|
|
258
|
+
return thumbhash || dataUri(blurDataUri) || "";
|
|
259
|
+
}
|
|
260
|
+
const resolvePlaceholder = (placeholder) => {
|
|
261
|
+
if (typeof placeholder === "string") {
|
|
262
|
+
if (isImageName(placeholder)) return Platform.OS === "android" ? placeholder : { uri: placeholder };
|
|
263
|
+
const uri = dataUri(placeholder);
|
|
264
|
+
if (!uri) return null;
|
|
265
|
+
return Platform.OS === "android" ? uri : { uri };
|
|
266
|
+
}
|
|
267
|
+
if (!placeholder) return null;
|
|
268
|
+
const resolved = Image.resolveAssetSource(placeholder);
|
|
240
269
|
if (!resolved) return null;
|
|
241
270
|
return Platform.OS === "android" ? resolved.uri : resolved;
|
|
242
271
|
};
|
|
@@ -291,7 +320,7 @@ function builtInTransition() {
|
|
|
291
320
|
return resolveTransition(Platform.OS === "android", NO_TRANSITION);
|
|
292
321
|
}
|
|
293
322
|
let defaultTransition;
|
|
294
|
-
function FastImageBase({ source, defaultSource, tintColor, onLoadStart, onProgress, onLoad, onError, onLoadEnd, style, children, objectFit, objectPosition, loop, transition, forwardedRef, ...viewProps }) {
|
|
323
|
+
function FastImageBase({ source, placeholder, defaultSource, tintColor, onLoadStart, onProgress, onLoad, onError, onLoadEnd, style, children, objectFit, objectPosition, loop, transition, loading: _loading, sizes: _sizes, forwardedRef, ...viewProps }) {
|
|
295
324
|
var _props$recyclingKey, _defaultTransition;
|
|
296
325
|
const { fallback, resizeMode: _resizeMode, ...props } = viewProps;
|
|
297
326
|
if (__DEV__ && fallback !== void 0) warnOnce("fallback", "react-native-fast-image: `fallback` was removed in 10.0, and does nothing: images always load natively. Remove it, or use React Native's Image for those images.", "warn");
|
|
@@ -299,10 +328,18 @@ function FastImageBase({ source, defaultSource, tintColor, onLoadStart, onProgre
|
|
|
299
328
|
const resolvedTintColor = tintColor != null ? tintColor : fromStyle(style, "tintColor");
|
|
300
329
|
const fit = resolveObjectFit(objectFit, style);
|
|
301
330
|
const position = objectPositionOrCentered(objectPosition);
|
|
302
|
-
const
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
331
|
+
const shared = hasSourcePlaceholder(placeholder) ? sourcePlaceholder(placeholder) : "";
|
|
332
|
+
const { resolvedSource, sources, sourcePlaceholders } = useMemo(() => {
|
|
333
|
+
const sources = Array.isArray(source) ? source.length === 1 ? void 0 : source : void 0;
|
|
334
|
+
const single = Array.isArray(source) ? source[0] : source;
|
|
335
|
+
const placeholders = (sources !== null && sources !== void 0 ? sources : [single]).map((entry) => sourcePlaceholder(entry) || shared);
|
|
336
|
+
return {
|
|
337
|
+
resolvedSource: sources ? void 0 : Image.resolveAssetSource(withoutPlaceholders(single)),
|
|
338
|
+
sources: (sources === null || sources === void 0 ? void 0 : sources.some(hasSourcePlaceholder)) ? sources.map(withoutPlaceholders) : sources,
|
|
339
|
+
sourcePlaceholders: placeholders.some(Boolean) ? placeholders : void 0
|
|
340
|
+
};
|
|
341
|
+
}, [source, shared]);
|
|
342
|
+
const resolvedPlaceholder = resolvePlaceholder(hasSourcePlaceholder(placeholder) ? defaultSource : placeholder !== null && placeholder !== void 0 ? placeholder : defaultSource);
|
|
306
343
|
return /* @__PURE__ */ React.createElement(FastImageViewNativeComponent_default, {
|
|
307
344
|
...props,
|
|
308
345
|
ref: forwardedRef,
|
|
@@ -312,7 +349,8 @@ function FastImageBase({ source, defaultSource, tintColor, onLoadStart, onProgre
|
|
|
312
349
|
...resolveTransition(transition, (_defaultTransition = defaultTransition) !== null && _defaultTransition !== void 0 ? _defaultTransition : builtInTransition()),
|
|
313
350
|
source: resolvedSource,
|
|
314
351
|
sources,
|
|
315
|
-
|
|
352
|
+
sourcePlaceholders,
|
|
353
|
+
placeholder: resolvedPlaceholder,
|
|
316
354
|
onFastImageLoadStart: onLoadStart,
|
|
317
355
|
onFastImageProgress: onProgress,
|
|
318
356
|
handledEvents: handledEvents({
|
package/dist/index.js.flow
CHANGED
|
@@ -110,6 +110,11 @@ export type PreloadResult = PreloadSuccess | PreloadFailure
|
|
|
110
110
|
export type PreloadFn = (
|
|
111
111
|
sources: Array<FastImageSource>,
|
|
112
112
|
) => Promise<Array<PreloadResult>>
|
|
113
|
+
export type SourcePlaceholder = {
|
|
114
|
+
thumbhash?: string,
|
|
115
|
+
blurDataUri?: string,
|
|
116
|
+
}
|
|
117
|
+
|
|
113
118
|
export type FastImageSource = {
|
|
114
119
|
uri?: string,
|
|
115
120
|
headers?: Object,
|
|
@@ -118,6 +123,8 @@ export type FastImageSource = {
|
|
|
118
123
|
cacheKey?: string,
|
|
119
124
|
memoryCache?: boolean,
|
|
120
125
|
diskCache?: boolean,
|
|
126
|
+
thumbhash?: string,
|
|
127
|
+
blurDataUri?: string,
|
|
121
128
|
width?: number,
|
|
122
129
|
height?: number,
|
|
123
130
|
scale?: number,
|
|
@@ -132,6 +139,8 @@ export type FastImageProps = $ReadOnly<{|
|
|
|
132
139
|
onProgress?: ?(event: OnProgressEvent) => void,
|
|
133
140
|
|
|
134
141
|
source?: ?(FastImageSource | Array<FastImageSource> | number),
|
|
142
|
+
placeholder?: ?(number | string | SourcePlaceholder),
|
|
143
|
+
// Deprecated: use placeholder, which is the same.
|
|
135
144
|
defaultSource?: ?number,
|
|
136
145
|
|
|
137
146
|
tintColor?: number | string,
|
|
@@ -140,6 +149,8 @@ export type FastImageProps = $ReadOnly<{|
|
|
|
140
149
|
recyclingKey?: ?string,
|
|
141
150
|
loop?: ?(boolean | number),
|
|
142
151
|
imageRendering?: ?('auto' | 'smooth' | 'pixelated'),
|
|
152
|
+
loading?: ?('lazy' | 'eager'),
|
|
153
|
+
sizes?: ?string,
|
|
143
154
|
paused?: ?boolean,
|
|
144
155
|
transition?: ?(boolean | number | Transition),
|
|
145
156
|
downsample?: ?boolean,
|
package/dist/index.web.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { Children, forwardRef, memo, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
1
|
+
import React, { Children, forwardRef, memo, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
|
|
2
2
|
import { Image, PixelRatio, StyleSheet, View } from "react-native";
|
|
3
3
|
import { getAssetByID } from "react-native-web/dist/modules/AssetRegistry";
|
|
4
4
|
//#region src/constants.ts
|
|
@@ -240,17 +240,24 @@ function cssPosition({ x, y }) {
|
|
|
240
240
|
return `${axis(x)} ${axis(y)}`;
|
|
241
241
|
}
|
|
242
242
|
//#endregion
|
|
243
|
+
//#region src/placeholders.ts
|
|
244
|
+
function hasSourcePlaceholder(value) {
|
|
245
|
+
return !!value && typeof value === "object" && ("thumbhash" in value || "blurDataUri" in value);
|
|
246
|
+
}
|
|
247
|
+
function dataUri(value) {
|
|
248
|
+
if (typeof value !== "string" || value === "") return void 0;
|
|
249
|
+
if (/^data:/i.test(value)) return value;
|
|
250
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) warnOnce("placeholder-url", `react-native-fast-image: \`placeholder\` and \`blurDataUri\` don't load urls: "${value.slice(0, 60)}" is ignored. Use a data uri (data:image/…), or for \`placeholder\`, a require()d image or the name of an image bundled with the app.`, "warn");
|
|
251
|
+
}
|
|
252
|
+
//#endregion
|
|
243
253
|
//#region src/index.web.tsx
|
|
244
|
-
var _useId;
|
|
245
254
|
const notSupported = {
|
|
246
255
|
ok: false,
|
|
247
256
|
error: "Not supported on the web"
|
|
248
257
|
};
|
|
249
|
-
let ids = 0;
|
|
250
|
-
const useCounterId = () => useState(() => `${++ids}`)[0];
|
|
251
|
-
const useSvgId = (_useId = React.useId) !== null && _useId !== void 0 ? _useId : useCounterId;
|
|
252
258
|
const useClientLayoutEffect = globalThis.document === void 0 ? useEffect : useLayoutEffect;
|
|
253
|
-
function FastImageBase({ source, defaultSource, tintColor, blurRadius, onLoadStart, onLoad, onError, onLoadEnd, style, children, objectFit, objectPosition, forwardedRef, onLayout, pointerEvents, recyclingKey: _recyclingKey, loop: _loop, imageRendering: _imageRendering, paused: _paused, transition: _transition, downsample: _downsample, onProgress: _onProgress, ...props }) {
|
|
259
|
+
function FastImageBase({ source, placeholder, defaultSource, tintColor, blurRadius, onLoadStart, onLoad, onError, onLoadEnd, style, children, objectFit, objectPosition, loading, sizes, forwardedRef, onLayout, pointerEvents, recyclingKey: _recyclingKey, loop: _loop, imageRendering: _imageRendering, paused: _paused, transition: _transition, downsample: _downsample, onProgress: _onProgress, ...props }) {
|
|
260
|
+
var _resolveUri;
|
|
254
261
|
if (typeof __DEV__ !== "undefined" && __DEV__) warnIfChildren(children);
|
|
255
262
|
const { accessibilityLabel, "aria-label": ariaLabel, resizeMode: _resizeMode, ...viewProps } = props;
|
|
256
263
|
const label = ariaLabel || accessibilityLabel || void 0;
|
|
@@ -259,10 +266,15 @@ function FastImageBase({ source, defaultSource, tintColor, blurRadius, onLoadSta
|
|
|
259
266
|
const src = sized ? sized.src : resolveUri(source);
|
|
260
267
|
const srcSet = sized === null || sized === void 0 ? void 0 : sized.srcSet;
|
|
261
268
|
const key = srcSet !== null && srcSet !== void 0 ? srcSet : src;
|
|
262
|
-
const
|
|
269
|
+
const priority = sourcePriority(source);
|
|
270
|
+
const preview = hasSourcePlaceholder(placeholder) ? placeholder : void 0;
|
|
271
|
+
const blurUri = (_resolveUri = resolveUri(dataUri(shared(source, "blurDataUri", preview)))) !== null && _resolveUri !== void 0 ? _resolveUri : thumbhashUri(shared(source, "thumbhash", preview));
|
|
272
|
+
const defaultUri = blurUri !== null && blurUri !== void 0 ? blurUri : resolveUri(preview ? defaultSource : placeholder !== null && placeholder !== void 0 ? placeholder : defaultSource);
|
|
263
273
|
const [result, setResult] = useState();
|
|
264
274
|
const ok = result && result.key === key ? result.ok : void 0;
|
|
265
|
-
const
|
|
275
|
+
const svgId = useId().replace(/[^\w-]/g, "");
|
|
276
|
+
const tintId = `fast-image-tint-${svgId}`;
|
|
277
|
+
const blurId = `fast-image-blur-${svgId}`;
|
|
266
278
|
const fit = resolveObjectFit(objectFit, style);
|
|
267
279
|
const position = cssPosition(objectPositionOrCentered(objectPosition));
|
|
268
280
|
const filter = [blurRadius ? `blur(${blurRadius}px)` : "", tintColor ? `url(#${tintId})` : ""].filter(Boolean).join(" ") || void 0;
|
|
@@ -280,15 +292,19 @@ function FastImageBase({ source, defaultSource, tintColor, blurRadius, onLoadSta
|
|
|
280
292
|
}, defaultUri && showDefault ? /* @__PURE__ */ React.createElement(Picture, {
|
|
281
293
|
key: `default ${defaultUri}`,
|
|
282
294
|
src: defaultUri,
|
|
283
|
-
|
|
295
|
+
loading,
|
|
296
|
+
priority,
|
|
297
|
+
fit: blurUri && (fit === "scale-down" || fit === "none") ? "contain" : fit,
|
|
284
298
|
position,
|
|
285
|
-
filter,
|
|
299
|
+
filter: blurUri ? [`url(#${blurId})`, tintColor ? `url(#${tintId})` : ""].filter(Boolean).join(" ") : filter,
|
|
286
300
|
alt: key ? "" : label !== null && label !== void 0 ? label : ""
|
|
287
301
|
}) : null, key && src ? /* @__PURE__ */ React.createElement(Picture, {
|
|
288
302
|
key: several ? "sizes" : key,
|
|
289
303
|
src,
|
|
290
304
|
srcSet,
|
|
291
|
-
|
|
305
|
+
sizes,
|
|
306
|
+
loading,
|
|
307
|
+
priority,
|
|
292
308
|
fit,
|
|
293
309
|
position,
|
|
294
310
|
filter,
|
|
@@ -325,7 +341,29 @@ function FastImageBase({ source, defaultSource, tintColor, blurRadius, onLoadSta
|
|
|
325
341
|
}) : null, tintColor ? /* @__PURE__ */ React.createElement(TintFilter, {
|
|
326
342
|
id: tintId,
|
|
327
343
|
color: tintColor
|
|
328
|
-
}) : null);
|
|
344
|
+
}) : null, blurUri && showDefault ? /* @__PURE__ */ React.createElement(PreviewBlur, { id: blurId }) : null);
|
|
345
|
+
}
|
|
346
|
+
function thumbhashUri(hash) {
|
|
347
|
+
if (!hash) return void 0;
|
|
348
|
+
const decode = globalThis[Symbol.for("react-native-fast-image/thumbhash")];
|
|
349
|
+
if (decode) return decode(hash);
|
|
350
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) warnOnce("thumbhash-web", "react-native-fast-image: on the web, a `thumbhash` shows only with `import 'react-native-fast-image/thumbhash'` (it decodes them). Or make it a `blurDataUri` before it reaches the browser (e.g. on your server) with that module's thumbHashToBlurDataUri, which needs no JavaScript in the browser.", "warn");
|
|
351
|
+
}
|
|
352
|
+
function sourcePriority(source) {
|
|
353
|
+
const values = (Array.isArray(source) ? source : [source]).map((entry) => entry && typeof entry === "object" ? entry.priority : void 0);
|
|
354
|
+
return [
|
|
355
|
+
"high",
|
|
356
|
+
"normal",
|
|
357
|
+
"low"
|
|
358
|
+
].find((priority) => values.includes(priority));
|
|
359
|
+
}
|
|
360
|
+
function shared(source, key, preview) {
|
|
361
|
+
const values = (Array.isArray(source) ? source : [source]).map((entry) => {
|
|
362
|
+
var _ref;
|
|
363
|
+
const own = entry && typeof entry === "object" ? entry : void 0;
|
|
364
|
+
return (_ref = (own === null || own === void 0 ? void 0 : own.thumbhash) || dataUri(own === null || own === void 0 ? void 0 : own.blurDataUri) ? own : preview) === null || _ref === void 0 ? void 0 : _ref[key];
|
|
365
|
+
});
|
|
366
|
+
return values.every((value) => value && value === values[0]) ? values[0] : void 0;
|
|
329
367
|
}
|
|
330
368
|
function resolveUri(source) {
|
|
331
369
|
let uri;
|
|
@@ -373,7 +411,7 @@ const fill = {
|
|
|
373
411
|
width: "100%",
|
|
374
412
|
height: "100%"
|
|
375
413
|
};
|
|
376
|
-
function Picture({ src, srcSet,
|
|
414
|
+
function Picture({ src, srcSet, sizes, loading, priority, fit, position, filter, hidden, alt, onLoad, onError }) {
|
|
377
415
|
const image = useRef(null);
|
|
378
416
|
const mounted = useRef(true);
|
|
379
417
|
const handled = useRef(void 0);
|
|
@@ -410,8 +448,9 @@ function Picture({ src, srcSet, lazy, fit, position, filter, hidden, alt, onLoad
|
|
|
410
448
|
ref: image,
|
|
411
449
|
src,
|
|
412
450
|
srcSet,
|
|
413
|
-
sizes: srcSet ? "auto, 100vw" : void 0,
|
|
414
|
-
loading:
|
|
451
|
+
sizes: srcSet ? sizes !== null && sizes !== void 0 ? sizes : "auto, 100vw" : void 0,
|
|
452
|
+
loading: loading === "eager" ? void 0 : "lazy",
|
|
453
|
+
fetchPriority: priority === "high" || priority === "low" ? priority : void 0,
|
|
415
454
|
alt,
|
|
416
455
|
draggable: false,
|
|
417
456
|
style: {
|
|
@@ -437,6 +476,20 @@ function TintFilter({ id, color }) {
|
|
|
437
476
|
operator: "in"
|
|
438
477
|
}))));
|
|
439
478
|
}
|
|
479
|
+
function PreviewBlur({ id }) {
|
|
480
|
+
return /* @__PURE__ */ React.createElement("svg", { style: tintStyle }, /* @__PURE__ */ React.createElement("defs", null, /* @__PURE__ */ React.createElement("filter", {
|
|
481
|
+
id,
|
|
482
|
+
x: "0",
|
|
483
|
+
y: "0",
|
|
484
|
+
width: "1",
|
|
485
|
+
height: "1",
|
|
486
|
+
primitiveUnits: "objectBoundingBox",
|
|
487
|
+
colorInterpolationFilters: "sRGB"
|
|
488
|
+
}, /* @__PURE__ */ React.createElement("feGaussianBlur", { stdDeviation: "0.06" }), /* @__PURE__ */ React.createElement("feComponentTransfer", null, /* @__PURE__ */ React.createElement("feFuncA", {
|
|
489
|
+
type: "table",
|
|
490
|
+
tableValues: "0 1 1 1 1"
|
|
491
|
+
})))));
|
|
492
|
+
}
|
|
440
493
|
const tintStyle = {
|
|
441
494
|
position: "absolute",
|
|
442
495
|
width: 0,
|
package/dist/index.web.js.flow
CHANGED
|
@@ -110,6 +110,11 @@ export type PreloadResult = PreloadSuccess | PreloadFailure
|
|
|
110
110
|
export type PreloadFn = (
|
|
111
111
|
sources: Array<FastImageSource>,
|
|
112
112
|
) => Promise<Array<PreloadResult>>
|
|
113
|
+
export type SourcePlaceholder = {
|
|
114
|
+
thumbhash?: string,
|
|
115
|
+
blurDataUri?: string,
|
|
116
|
+
}
|
|
117
|
+
|
|
113
118
|
export type FastImageSource = {
|
|
114
119
|
uri?: string,
|
|
115
120
|
headers?: Object,
|
|
@@ -118,6 +123,8 @@ export type FastImageSource = {
|
|
|
118
123
|
cacheKey?: string,
|
|
119
124
|
memoryCache?: boolean,
|
|
120
125
|
diskCache?: boolean,
|
|
126
|
+
thumbhash?: string,
|
|
127
|
+
blurDataUri?: string,
|
|
121
128
|
width?: number,
|
|
122
129
|
height?: number,
|
|
123
130
|
scale?: number,
|
|
@@ -132,6 +139,8 @@ export type FastImageProps = $ReadOnly<{|
|
|
|
132
139
|
onProgress?: ?(event: OnProgressEvent) => void,
|
|
133
140
|
|
|
134
141
|
source?: ?(FastImageSource | Array<FastImageSource> | number),
|
|
142
|
+
placeholder?: ?(number | string | SourcePlaceholder),
|
|
143
|
+
// Deprecated: use placeholder, which is the same.
|
|
135
144
|
defaultSource?: ?number,
|
|
136
145
|
|
|
137
146
|
tintColor?: number | string,
|
|
@@ -140,6 +149,8 @@ export type FastImageProps = $ReadOnly<{|
|
|
|
140
149
|
recyclingKey?: ?string,
|
|
141
150
|
loop?: ?(boolean | number),
|
|
142
151
|
imageRendering?: ?('auto' | 'smooth' | 'pixelated'),
|
|
152
|
+
loading?: ?('lazy' | 'eager'),
|
|
153
|
+
sizes?: ?string,
|
|
143
154
|
paused?: ?boolean,
|
|
144
155
|
transition?: ?(boolean | number | Transition),
|
|
145
156
|
downsample?: ?boolean,
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SourcePlaceholder } from './index';
|
|
2
|
+
export declare function isImageName(value: string): boolean;
|
|
3
|
+
export declare function hasSourcePlaceholder(value: unknown): value is SourcePlaceholder;
|
|
4
|
+
export declare function dataUri(value: unknown): string | undefined;
|
|
@@ -21,7 +21,8 @@ type LoadEndEvent = Readonly<{
|
|
|
21
21
|
export interface NativeProps extends ViewProps {
|
|
22
22
|
source?: CodegenTypes.UnsafeMixed;
|
|
23
23
|
sources?: CodegenTypes.UnsafeMixed;
|
|
24
|
-
|
|
24
|
+
placeholder?: CodegenTypes.UnsafeMixed;
|
|
25
|
+
sourcePlaceholders?: ReadonlyArray<string>;
|
|
25
26
|
objectFit?: CodegenTypes.WithDefault<string, 'cover'>;
|
|
26
27
|
objectPositionX?: CodegenTypes.WithDefault<CodegenTypes.Float, 0.5>;
|
|
27
28
|
objectPositionXOffset?: CodegenTypes.Float;
|