Skip to content

Commit 00b439b

Browse files
authored
Support expo image (#968)
* support expo-image * add image example * refactor * refactor * add expoImage component * update expo image * update expo image * update blurhash example * make transition as flat data instead of object * update * update expo image example * update example * remove duplicate style of expo image * update example
1 parent 8d34ab2 commit 00b439b

File tree

7 files changed

+260
-0
lines changed

7 files changed

+260
-0
lines changed

example/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import ThemeExample from "./ThemeExample";
7676
import LoadingIndicatorExample from "./LoadingIndicatorExample";
7777
import TimerExample from "./TimerExample";
7878
import LottieAnimationExample from "./LottieAnimationExample";
79+
import ExpoImageExample from "./ExpoImageExample";
7980

8081
const ROUTES = {
8182
LottieAnimationExample: LottieAnimationExample,
@@ -120,6 +121,7 @@ const ROUTES = {
120121
VideoPlayer: VideoPlayerExample,
121122
PinInput: PinInputExample,
122123
KeyboardAvoidingView: KeyboardAvoidingViewExample,
124+
ExpoImage: ExpoImageExample,
123125
};
124126

125127
let customFonts = {

example/src/ExpoImageExample.tsx

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import * as React from "react";
2+
import { View, StyleSheet, Text } from "react-native";
3+
import { ExpoImage, withTheme } from "@draftbit/ui";
4+
import Section, { Container } from "./Section";
5+
6+
interface WrapperProps {
7+
label: string;
8+
children: React.ReactNode;
9+
}
10+
11+
const Wrapper: React.FC<WrapperProps> = ({ label, children }) => {
12+
return (
13+
<View style={styles.wrapper}>
14+
<View style={styles.boxLabel}>
15+
<Text>{label}</Text>
16+
</View>
17+
<View>{children}</View>
18+
</View>
19+
);
20+
};
21+
22+
const ExpoImageExample: React.FC = () => {
23+
return (
24+
<Container style={{}}>
25+
<Section title="Image Examples" style={{}}>
26+
<View style={{ flexDirection: "row" }}>
27+
<Wrapper label="Basic remote image">
28+
<ExpoImage
29+
source={{
30+
uri: "https://picsum.photos/1100",
31+
}}
32+
style={{
33+
width: 200,
34+
height: 200,
35+
}}
36+
/>
37+
</Wrapper>
38+
<Wrapper label="Local image">
39+
<ExpoImage
40+
source={require("./assets/images/hamburger.png")}
41+
style={{
42+
width: 200,
43+
height: 200,
44+
}}
45+
/>
46+
</Wrapper>
47+
</View>
48+
<View style={{ flexDirection: "row" }}>
49+
<Wrapper label="Content fit: contain">
50+
<ExpoImage
51+
source={{
52+
uri: "https://picsum.photos/1300",
53+
}}
54+
contentFit="contain"
55+
style={{
56+
width: 200,
57+
height: 200,
58+
backgroundColor: "#f0f0f0",
59+
}}
60+
/>
61+
</Wrapper>
62+
<Wrapper label="With blur hash">
63+
<ExpoImage
64+
source={{
65+
uri: "https://picsum.photos/seed/696/3000/2000",
66+
}}
67+
blurhash="LEHLk~WB2yk8pyo0adR*.7kCMdnj"
68+
style={{
69+
width: 200,
70+
height: 200,
71+
}}
72+
/>
73+
</Wrapper>
74+
</View>
75+
</Section>
76+
<Section title="SVG Image" style={styles.wrapper}>
77+
<Wrapper label="Remote SVG Image">
78+
<ExpoImage
79+
source={{
80+
uri: "https://upload.wikimedia.org/wikipedia/commons/3/30/Vector-based_example.svg",
81+
}}
82+
style={{
83+
width: 200,
84+
height: 200,
85+
}}
86+
/>
87+
</Wrapper>
88+
<Wrapper label="Local SVG Image">
89+
<ExpoImage
90+
source={require("./assets/images/example.svg")}
91+
style={{
92+
width: 200,
93+
height: 200,
94+
}}
95+
/>
96+
</Wrapper>
97+
</Section>
98+
<Section title="Transition Effects" style={{}}>
99+
<View style={{ flexDirection: "row", flexWrap: "wrap" }}>
100+
<Wrapper label="Cross Dissolve - Ease In Out">
101+
<ExpoImage
102+
source={{ uri: "https://picsum.photos/1400" }}
103+
transitionDuration={3000}
104+
transitionEffect="cross-dissolve"
105+
transitionTiming="ease-in-out"
106+
style={{ width: 150, height: 150 }}
107+
/>
108+
</Wrapper>
109+
<Wrapper label="Flip from Top - Ease Out">
110+
<ExpoImage
111+
source={{ uri: "https://picsum.photos/1500" }}
112+
transitionDuration={3000}
113+
transitionEffect="flip-from-top"
114+
transitionTiming="ease-out"
115+
style={{ width: 150, height: 150 }}
116+
/>
117+
</Wrapper>
118+
</View>
119+
</Section>
120+
<Section title="Transition Timing" style={{}}>
121+
<View style={{ flexDirection: "row", flexWrap: "wrap" }}>
122+
<Wrapper label="Curl Up - Linear">
123+
<ExpoImage
124+
source={{ uri: "https://picsum.photos/1600" }}
125+
transitionDuration={3000}
126+
transitionEffect="curl-up"
127+
transitionTiming="linear"
128+
style={{ width: 150, height: 150 }}
129+
/>
130+
</Wrapper>
131+
<Wrapper label="Cross Dissolve - Ease In Out">
132+
<ExpoImage
133+
source={{ uri: "https://picsum.photos/1700" }}
134+
transitionDuration={3000}
135+
transitionEffect="cross-dissolve"
136+
transitionTiming="ease-in-out"
137+
style={{ width: 150, height: 150 }}
138+
/>
139+
</Wrapper>
140+
</View>
141+
</Section>
142+
</Container>
143+
);
144+
};
145+
146+
const styles = StyleSheet.create({
147+
wrapper: {
148+
flex: 1,
149+
display: "flex",
150+
flexDirection: "column",
151+
flexWrap: "wrap",
152+
justifyContent: "center",
153+
alignItems: "center",
154+
},
155+
boxLabel: {
156+
margin: 10,
157+
flex: 1,
158+
},
159+
});
160+
161+
export default withTheme(ExpoImageExample);

packages/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"date-fns": "^2.16.1",
5656
"dateformat": "^3.0.3",
5757
"expo-av": "~13.10.6",
58+
"expo-image": "1.10.6",
5859
"lodash.isequal": "^4.5.0",
5960
"lodash.isnumber": "^3.0.3",
6061
"lodash.omit": "^4.5.0",
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import React from "react";
2+
import {
3+
Image,
4+
ImageContentPosition,
5+
ImageProps as ExpoImageProps,
6+
ImageContentFit,
7+
} from "expo-image";
8+
import Config from "./Config";
9+
10+
interface ExtendedImageProps extends ExpoImageProps {
11+
transitionDuration?: number;
12+
transitionEffect?:
13+
| "cross-dissolve"
14+
| "flip-from-top"
15+
| "flip-from-right"
16+
| "flip-from-bottom"
17+
| "flip-from-left"
18+
| "curl-up"
19+
| "curl-down";
20+
transitionTiming?: "ease-in-out" | "ease-in" | "ease-out" | "linear";
21+
contentFit?: "cover" | "contain" | "fill" | "none" | "scale-down";
22+
contentPosition?: ImageContentPosition;
23+
cachePolicy?: "none" | "disk" | "memory" | "memory-disk";
24+
allowDownscaling?: boolean;
25+
blurRadius?: number;
26+
blurhash?: string;
27+
}
28+
29+
const resizeModeToContentFit = (
30+
resizeMode: "cover" | "contain" | "stretch" | "repeat" | "center"
31+
): ImageContentFit => {
32+
const mapping: Record<typeof resizeMode, ImageContentFit> = {
33+
cover: "cover",
34+
contain: "contain",
35+
stretch: "fill",
36+
repeat: "none",
37+
center: "scale-down",
38+
} as const;
39+
return mapping[resizeMode] ?? "cover";
40+
};
41+
42+
const ExpoImage: React.FC<ExtendedImageProps> = ({
43+
source,
44+
resizeMode = "cover",
45+
style,
46+
transitionDuration = 300,
47+
transitionEffect = "cross-dissolve",
48+
transitionTiming = "ease-in-out",
49+
contentFit = "cover",
50+
contentPosition = "center",
51+
cachePolicy = "memory-disk",
52+
allowDownscaling = true,
53+
blurRadius,
54+
blurhash,
55+
...props
56+
}) => {
57+
const imageSource = source ?? Config.placeholderImageURL;
58+
const finalContentFit = resizeMode
59+
? resizeModeToContentFit(resizeMode)
60+
: contentFit;
61+
62+
const transition = {
63+
timing: transitionTiming,
64+
duration: transitionDuration,
65+
effect: transitionEffect,
66+
};
67+
68+
return (
69+
<Image
70+
{...props}
71+
source={imageSource}
72+
contentFit={finalContentFit}
73+
placeholder={{
74+
blurhash,
75+
}}
76+
transition={transition}
77+
contentPosition={contentPosition}
78+
cachePolicy={cachePolicy}
79+
allowDownscaling={allowDownscaling}
80+
blurRadius={blurRadius}
81+
style={style}
82+
/>
83+
);
84+
};
85+
86+
export default ExpoImage;

packages/core/src/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export { default as SimpleStyleSwipeableList } from "./components/SimpleStyleScr
8282
export { default as LoadingIndicator } from "./components/LoadingIndicator";
8383
export { default as LottieAnimation } from "./components/LottieAnimation";
8484
export { default as Timer } from "./components/Timer";
85+
export { default as ExpoImage } from "./components/ExpoImage";
8586

8687
/* Deprecated: Fix or Delete! */
8788
export { default as AccordionItem } from "./deprecated-components/AccordionItem";

packages/ui/src/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ export {
7878
LoadingIndicator,
7979
LottieAnimation,
8080
Timer,
81+
Image,
82+
ExpoImage,
8183
} from "@draftbit/core";
8284

8385
export {

yarn.lock

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7529,6 +7529,13 @@ expo-font@~11.10.3:
75297529
dependencies:
75307530
fontfaceobserver "^2.1.0"
75317531

7532+
7533+
version "1.10.6"
7534+
resolved "https://registry.yarnpkg.com/expo-image/-/expo-image-1.10.6.tgz#b0e54d31d97742505296c076a5f18d094ba9a8cc"
7535+
integrity sha512-vcnAIym1eU8vQgV1re1E7rVQZStJimBa4aPDhjFfzMzbddAF7heJuagyewiUkTzbZUwYzPaZAie6VJPyWx9Ueg==
7536+
dependencies:
7537+
"@react-native/assets-registry" "~0.73.1"
7538+
75327539
expo-keep-awake@~12.8.2:
75337540
version "12.8.2"
75347541
resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-12.8.2.tgz#6cfdf8ad02b5fa130f99d4a1eb98e459d5b4332e"

0 commit comments

Comments
 (0)