1import { ImageResizeMode, Image } from 'expo-image'; 2import React from 'react'; 3import { Dimensions, Image as RNImage, ScrollView, StyleSheet, Text, View } from 'react-native'; 4import { PanGestureHandler, PanGestureHandlerGestureEvent } from 'react-native-gesture-handler'; 5import Animated, { 6 useAnimatedGestureHandler, 7 useAnimatedStyle, 8 useSharedValue, 9} from 'react-native-reanimated'; 10 11import { FunctionParameter, useArguments } from '../../components/FunctionDemo'; 12import Configurator from '../../components/FunctionDemo/Configurator'; 13import { Colors } from '../../constants'; 14 15type CustomViewProps = React.PropsWithChildren<object>; 16 17type ContextType = { 18 x: number; 19 y: number; 20}; 21 22const PADDING = 20; 23const HANDLE_SIZE = 25; 24const HANDLE_SLOP = 10; 25const WINDOW_DIMENSIONS = Dimensions.get('window'); 26const MAX_WIDTH = WINDOW_DIMENSIONS.width - 2 * PADDING; 27const MAX_HEIGHT = WINDOW_DIMENSIONS.height - 330; 28 29const ResizableView: React.FC<CustomViewProps> = ({ children }) => { 30 const width = useSharedValue(300); 31 const height = useSharedValue(300); 32 33 const panGestureEvent = useAnimatedGestureHandler<PanGestureHandlerGestureEvent, ContextType>({ 34 onStart: (_, context) => { 35 context.x = width.value; 36 context.y = height.value; 37 }, 38 onActive: (event, context) => { 39 width.value = Math.max(HANDLE_SIZE, Math.min(event.translationX + context.x, MAX_WIDTH)); 40 height.value = Math.max(HANDLE_SIZE, Math.min(event.translationY + context.y, MAX_HEIGHT)); 41 }, 42 }); 43 44 const canvasStyle = useAnimatedStyle(() => { 45 return { 46 width: width.value, 47 height: height.value, 48 }; 49 }, [width, height]); 50 51 return ( 52 <View style={styles.resizableView}> 53 <Text style={styles.hintText}> 54 Move the handle above to resize the image canvas and see how it lays out in different 55 components, sizes and resize modes 56 </Text> 57 58 <Animated.View style={[styles.canvas, canvasStyle]}> 59 {children} 60 61 <PanGestureHandler onGestureEvent={panGestureEvent}> 62 <Animated.View style={styles.resizeHandle}> 63 <View style={styles.resizeHandleChild} /> 64 </Animated.View> 65 </PanGestureHandler> 66 </Animated.View> 67 </View> 68 ); 69}; 70 71const parameters: FunctionParameter[] = [ 72 { 73 name: 'Use React Native Image', 74 type: 'boolean', 75 initial: false, 76 }, 77 { 78 name: 'Size', 79 type: 'enum', 80 values: [ 81 { name: '1500x1000', value: '1500/1000' }, 82 { name: '1000x1500', value: '1000/1500' }, 83 { name: '100x100', value: '100/100' }, 84 ], 85 }, 86 { 87 name: 'Resize mode', 88 type: 'enum', 89 values: [ 90 { name: 'cover', value: ImageResizeMode.COVER }, 91 { name: 'contain', value: ImageResizeMode.CONTAIN }, 92 { name: 'center', value: ImageResizeMode.CENTER }, 93 { name: 'repeat', value: ImageResizeMode.REPEAT }, 94 { name: 'stretch', value: ImageResizeMode.STRETCH }, 95 ], 96 }, 97]; 98 99export default function ImageResizableScreen() { 100 const [seed] = React.useState(1 + Math.round(Math.random() * 10)); 101 const [args, updateArgument] = useArguments(parameters); 102 const [showReactNativeComponent, size, resizeMode] = args as [boolean, string, ImageResizeMode]; 103 const ImageComponent: React.ElementType = showReactNativeComponent ? RNImage : Image; 104 105 return ( 106 <ScrollView style={styles.container}> 107 <ResizableView> 108 <ImageComponent 109 style={styles.image} 110 source={{ uri: `https://picsum.photos/seed/${seed}/${size}` }} 111 resizeMode={resizeMode} 112 /> 113 </ResizableView> 114 115 <View style={styles.configurator}> 116 <Configurator parameters={parameters} onChange={updateArgument} value={args} /> 117 </View> 118 </ScrollView> 119 ); 120} 121 122const styles = StyleSheet.create({ 123 container: { 124 flex: 1, 125 }, 126 resizableView: { 127 margin: PADDING, 128 width: MAX_WIDTH, 129 height: MAX_HEIGHT, 130 borderWidth: 1, 131 borderColor: Colors.border, 132 backgroundColor: '#eef', 133 }, 134 configurator: { 135 flex: 1, 136 paddingHorizontal: 15, 137 }, 138 canvas: { 139 margin: -1, 140 minWidth: HANDLE_SIZE, 141 minHeight: HANDLE_SIZE, 142 maxWidth: MAX_WIDTH, 143 maxHeight: MAX_HEIGHT, 144 backgroundColor: '#00f2', 145 borderWidth: 2, 146 borderStyle: 'dotted', 147 borderColor: Colors.tintColor, 148 borderRadius: 3, 149 }, 150 resizeHandle: { 151 padding: HANDLE_SLOP, 152 position: 'absolute', 153 bottom: -HANDLE_SIZE / 2 - HANDLE_SLOP, 154 right: -HANDLE_SIZE / 2 - HANDLE_SLOP, 155 }, 156 resizeHandleChild: { 157 width: HANDLE_SIZE, 158 height: HANDLE_SIZE, 159 borderRadius: HANDLE_SIZE, 160 borderWidth: 3, 161 borderColor: Colors.tintColor, 162 backgroundColor: '#fff', 163 }, 164 hintText: { 165 color: Colors.secondaryText, 166 textAlign: 'center', 167 position: 'absolute', 168 right: 10, 169 left: 10, 170 bottom: 16, 171 }, 172 image: { 173 flex: 1, 174 }, 175}); 176