react-native-fast-image
Version:
FastImage, performant React Native image component.
270 lines (269 loc) • 10.1 kB
JavaScript
Object.defineProperties(exports, {
__esModule: { value: true },
[Symbol.toStringTag]: { value: "Module" }
});
//#region \0rolldown/runtime.js
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
let react = require("react");
react = __toESM(react);
let react_native = require("react-native");
//#region src/constants.ts
const resizeMode = {
contain: "contain",
cover: "cover",
stretch: "stretch",
center: "center",
repeat: "repeat"
};
const priority = {
low: "low",
normal: "normal",
high: "high"
};
const cacheControl = {
immutable: "immutable",
web: "web",
cacheOnly: "cacheOnly"
};
//#endregion
//#region src/index.web.tsx
const notSupported = {
ok: false,
error: "Not supported on the web"
};
function FastImageBase({ source, defaultSource, tintColor, blurRadius, onLoadStart, onLoad, onError, onLoadEnd, style, children, resizeMode: mode = "cover", forwardedRef, onLayout, pointerEvents, fallback: _fallback, recyclingKey: _recyclingKey, loop: _loop, imageRendering: _imageRendering, paused: _paused, transition: _transition, downsample: _downsample, onProgress: _onProgress, ...props }) {
const result = (0, react.useRef)(void 0);
const sent = (0, react.useRef)(Promise.resolve());
const image = (0, react.useRef)(null);
const sizes = Array.isArray(source) && source.length > 1 ? source : void 0;
const single = Array.isArray(source) ? source[0] : source;
const uri = typeof single === "object" && single ? single.uri : void 0;
const webSource = typeof single === "number" ? single : uri ? { uri } : void 0;
return /* @__PURE__ */ react.default.createElement(react_native.View, {
style: [styles.container, style],
onLayout,
pointerEvents,
ref: forwardedRef
}, sizes ? /* @__PURE__ */ react.default.createElement(react_native.View, {
...props,
style: styles.image
}, /* @__PURE__ */ react.default.createElement(ImageSizes, {
sources: sizes,
resizeMode: mode,
blurRadius,
onLoadStart,
onLoad,
onError,
onLoadEnd
})) : /* @__PURE__ */ react.default.createElement(react_native.Image, {
...props,
style: styles.image,
source: webSource,
defaultSource,
resizeMode: mode,
tintColor,
blurRadius,
ref: image,
onLoadStart,
onLoad: (event) => {
var _event$nativeEvent;
sent.current = loadedSize(event === null || event === void 0 || (_event$nativeEvent = event.nativeEvent) === null || _event$nativeEvent === void 0 ? void 0 : _event$nativeEvent.target, image.current, uri).then(({ width, height }) => {
result.current = {
ok: true,
width,
height
};
onLoad === null || onLoad === void 0 || onLoad({ nativeEvent: {
width,
height
} });
});
},
onError: (event) => {
var _event$nativeEvent$er, _event$nativeEvent2;
const error = String((_event$nativeEvent$er = event === null || event === void 0 || (_event$nativeEvent2 = event.nativeEvent) === null || _event$nativeEvent2 === void 0 ? void 0 : _event$nativeEvent2.error) !== null && _event$nativeEvent$er !== void 0 ? _event$nativeEvent$er : "Failed to load the image");
result.current = {
ok: false,
error
};
sent.current = Promise.resolve();
onError === null || onError === void 0 || onError({ nativeEvent: { error } });
},
onLoadEnd: () => {
sent.current.then(() => {
var _result$current;
onLoadEnd === null || onLoadEnd === void 0 || onLoadEnd((_result$current = result.current) !== null && _result$current !== void 0 ? _result$current : {
ok: false,
error: "Failed to load the image"
});
result.current = void 0;
});
}
}), children);
}
function ImageSizes({ sources, resizeMode: mode, blurRadius, onLoadStart, onLoad, onError, onLoadEnd }) {
var _largest$uri, _sources$find;
const sized = sources.filter((source) => (source === null || source === void 0 ? void 0 : source.uri) && source.width).map((source) => {
var _source$width, _source$scale;
return {
uri: source.uri,
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))
};
});
const largest = sized.reduce((best, source) => !best || source.width > best.width ? source : best, void 0);
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;
const srcSet = sized.length > 0 ? sized.map((source) => `${source.uri} ${source.width}w`).join(", ") : void 0;
(0, react.useEffect)(() => {
onLoadStart === null || onLoadStart === void 0 || onLoadStart();
}, [src, srcSet]);
return /* @__PURE__ */ react.default.createElement("img", {
src,
srcSet,
sizes: srcSet ? "auto, 100vw" : void 0,
loading: "lazy",
alt: "",
draggable: false,
style: {
position: "absolute",
width: "100%",
height: "100%",
objectFit: OBJECT_FIT[mode !== null && mode !== void 0 ? mode : "cover"],
filter: blurRadius ? `blur(${blurRadius}px)` : void 0
},
onLoad: (event) => {
var _picked$width;
const image = event.currentTarget;
const picked = srcSet ? sized.find((source) => new URL(source.uri, image.currentSrc).href === image.currentSrc) : void 0;
const loadedWidth = (_picked$width = picked === null || picked === void 0 ? void 0 : picked.width) !== null && _picked$width !== void 0 ? _picked$width : image.naturalWidth;
const loadedHeight = picked ? Math.round(image.naturalHeight * picked.width / image.naturalWidth) : image.naturalHeight;
onLoad === null || onLoad === void 0 || onLoad({ nativeEvent: {
width: loadedWidth,
height: loadedHeight
} });
onLoadEnd === null || onLoadEnd === void 0 || onLoadEnd({
ok: true,
width: loadedWidth,
height: loadedHeight
});
},
onError: () => {
const error = "Failed to load the image";
onError === null || onError === void 0 || onError({ nativeEvent: { error } });
onLoadEnd === null || onLoadEnd === void 0 || onLoadEnd({
ok: false,
error
});
}
});
}
const OBJECT_FIT = {
cover: "cover",
contain: "contain",
stretch: "fill",
center: "scale-down",
repeat: "cover"
};
const FastImageMemo = (0, react.memo)(FastImageBase);
const FastImageComponent = (0, react.forwardRef)((props, ref) => /* @__PURE__ */ react.default.createElement(FastImageMemo, {
forwardedRef: ref,
...props
}));
FastImageComponent.displayName = "FastImage";
async function loadedSize(loaded, view, uri) {
var _view$querySelector;
if (loaded === null || loaded === void 0 ? void 0 : loaded.naturalWidth) return {
width: loaded.naturalWidth,
height: loaded.naturalHeight
};
await new Promise((resolve) => setTimeout(resolve, 0));
const shown = view === null || view === void 0 || (_view$querySelector = view.querySelector) === null || _view$querySelector === void 0 ? void 0 : _view$querySelector.call(view, "img");
if (shown === null || shown === void 0 ? void 0 : shown.naturalWidth) return {
width: shown.naturalWidth,
height: shown.naturalHeight
};
const src = uri !== null && uri !== void 0 ? uri : shown === null || shown === void 0 ? void 0 : shown.src;
return src ? getSize(src) : {
width: 0,
height: 0
};
}
function getSize(uri) {
return new Promise((resolve) => {
react_native.Image.getSize(uri, (width, height) => resolve({
width,
height
}), () => resolve({
width: 0,
height: 0
}));
});
}
const FastImage = FastImageComponent;
FastImage.resizeMode = resizeMode;
FastImage.cacheControl = cacheControl;
FastImage.priority = priority;
FastImage.preload = (sources) => Promise.all(sources.map((source) => {
const uri = source ? source.uri : void 0;
if (typeof uri !== "string" || !uri) return Promise.resolve({
ok: false,
error: "Invalid source: no uri",
uri
});
return react_native.Image.prefetch(uri).then(() => getSize(uri).then((size) => ({
ok: true,
uri,
...size
})), () => ({
ok: false,
error: "Failed to load the image",
uri
}));
}));
FastImage.clearMemoryCache = () => Promise.resolve(notSupported);
FastImage.clearDiskCache = () => Promise.resolve(notSupported);
FastImage.configureCache = () => Promise.resolve({});
FastImage.writeToCache = () => Promise.resolve(notSupported);
FastImage.getCachePath = () => Promise.resolve(notSupported);
const styles = react_native.StyleSheet.create({
container: { overflow: "hidden" },
image: {
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%"
}
});
const FastImageWithRef = FastImageComponent;
const FastImageBackground = (0, react.forwardRef)(({ style, imageStyle, imageRef, children, ...props }, ref) => /* @__PURE__ */ react.default.createElement(react_native.View, {
style,
ref
}, /* @__PURE__ */ react.default.createElement(FastImageWithRef, {
...props,
style: [react_native.StyleSheet.absoluteFill, imageStyle],
ref: imageRef
}), children));
FastImageBackground.displayName = "FastImageBackground";
//#endregion
exports.FastImageBackground = FastImageBackground;
exports.default = FastImage;