Skip to content

Commit

Permalink
Merge pull request #7 from intergalacticspacehighway/zoom-in-list
Browse files Browse the repository at this point in the history
Zoom in list
  • Loading branch information
intergalacticspacehighway authored Jun 18, 2022
2 parents c03f25c + 620968b commit fd54604
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 24 deletions.
74 changes: 57 additions & 17 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,66 @@
import * as React from 'react';
import { Image } from 'react-native';
import { FlatList, Image, useWindowDimensions, View } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { Zoom } from 'react-native-reanimated-zoom';

const data = [
'https://images.unsplash.com/photo-1536152470836-b943b246224c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1038&q=80',
'https://images.unsplash.com/photo-1469474968028-56623f02e42e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3274&q=80',
'https://images.unsplash.com/photo-1439853949127-fa647821eba0?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1374&q=80',
'https://images.unsplash.com/photo-1444464666168-49d633b86797?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3269&q=80',
];

export default function App() {
return (
<GestureHandlerRootView
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'black',
}}
>
<Zoom maximumZoomScale={2}>
<Image
source={{
uri: 'https://images.unsplash.com/photo-1536152470836-b943b246224c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1038&q=80',
}}
style={{ width: 300, height: 400 }}
/>
</Zoom>
<GestureHandlerRootView style={{ flex: 1 }}>
<View
style={{
flex: 1,
}}
>
<FlatListExample />
</View>
</GestureHandlerRootView>
);
}

const FlatListExample = () => {
const dimension = useWindowDimensions();
const itemPadding = 10;

const renderItem = React.useCallback(
({ item }) => {
return (
<View
style={{
padding: itemPadding,
justifyContent: 'center',
}}
>
<Zoom>
<Image
source={{
uri: item,
}}
style={{
width: dimension.width - itemPadding * 2,
height: dimension.width,
}}
/>
</Zoom>
</View>
);
},
[dimension]
);

return (
<FlatList
data={data}
pagingEnabled
horizontal
keyExtractor={(item) => item}
renderItem={renderItem}
/>
);
};
44 changes: 37 additions & 7 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import type { ViewProps } from 'react-native';
import Animated, {
useSharedValue,
useAnimatedStyle,
useDerivedValue,
withTiming,
cancelAnimation,
runOnJS,
withSpring,
} from 'react-native-reanimated';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';

Expand All @@ -22,6 +24,7 @@ export function Zoom(props: Props) {
style: propStyle,
onLayout,
} = props;
const [panEnabled, setPanEnabled] = useState(false);

const translationX = useSharedValue(0);
const translationY = useSharedValue(0);
Expand All @@ -44,6 +47,7 @@ export function Zoom(props: Props) {
const gesture = useMemo(() => {
// we only activate pan handler when the image is zoomed or user is not pinching
const pan = Gesture.Pan()
.enabled(panEnabled)
.onStart(() => {
if (isPinching.value || !isZoomed.value) return;

Expand All @@ -55,15 +59,39 @@ export function Zoom(props: Props) {
prevTranslationY.value = translationY.value;
})
.onUpdate((e) => {
// track translate when not panning so we can subtract it
if (isPinching.value || !isZoomed.value) {
panTranslateX.value = e.translationX;
panTranslateY.value = e.translationY;
} else {
translationX.value =
// imagine what happens to pixels when we zoom in. (they get multiplied by x times scale)
const maxTranslateX =
(viewWidth.value / 2) * scale.value - viewWidth.value / 2;
const minTranslateX = -maxTranslateX;

const maxTranslateY =
(viewHeight.value / 2) * scale.value - viewHeight.value / 2;
const minTranslateY = -maxTranslateY;

const nextTranslateX =
prevTranslationX.value + e.translationX - panTranslateX.value;
translationY.value =
const nextTranslateY =
prevTranslationY.value + e.translationY - panTranslateY.value;

if (nextTranslateX > maxTranslateX) {
translationX.value = withSpring(maxTranslateX);
} else if (nextTranslateX < minTranslateX) {
translationX.value = withSpring(minTranslateX);
} else {
translationX.value = nextTranslateX;
}

if (nextTranslateY > maxTranslateY) {
translationY.value = withSpring(maxTranslateX);
} else if (nextTranslateY < minTranslateY) {
translationY.value = withSpring(minTranslateY);
} else {
translationY.value = nextTranslateY;
}
}
})
.onEnd(() => {
Expand Down Expand Up @@ -160,13 +188,15 @@ export function Zoom(props: Props) {

// only add prop dependencies
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [maximumZoomScale, minimumZoomScale]);
}, [maximumZoomScale, minimumZoomScale, panEnabled]);

useDerivedValue(() => {
if (scale.value > 1) {
if (scale.value > 1 && !isZoomed.value) {
isZoomed.value = true;
} else if (scale.value === 1) {
runOnJS(setPanEnabled)(true);
} else if (scale.value === 1 && isZoomed.value) {
isZoomed.value = false;
runOnJS(setPanEnabled)(false);
}
}, []);

Expand Down

0 comments on commit fd54604

Please sign in to comment.