From 391e232bf74b2d68fa415e19165f6c365bb30b63 Mon Sep 17 00:00:00 2001 From: Nikolaos Karaolidis Date: Sat, 29 Jul 2023 20:56:37 +0300 Subject: [PATCH] Add custom AnimatedImage component This also fixes the white flashing when loading images Signed-off-by: Nikolaos Karaolidis --- package-lock.json | 18 --------- package.json | 1 - src/components/animatedImage.tsx | 56 +++++++++++++++++++++++++++ src/components/index.ts | 2 + src/components/memes/memeEditor.tsx | 3 +- src/components/memes/memeViewItem.tsx | 9 ++--- 6 files changed, 62 insertions(+), 27 deletions(-) create mode 100644 src/components/animatedImage.tsx diff --git a/package-lock.json b/package-lock.json index ebfa808..e8f3ec2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "hasInstallScript": true, "dependencies": { "@bankify/redux-persist-realm": "^0.1.3", - "@likashefqet/react-native-image-zoom": "^1.3.0", "@react-native-clipboard/clipboard": "^1.11.2", "@react-native-community/hooks": "^3.0.0", "@react-navigation/bottom-tabs": "^6.5.8", @@ -2991,17 +2990,6 @@ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, - "node_modules/@likashefqet/react-native-image-zoom": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@likashefqet/react-native-image-zoom/-/react-native-image-zoom-1.3.0.tgz", - "integrity": "sha512-PLRd1hNMHe9LUn8b4rmLt86282geuaqP4Qd2rFWIloxMS2ePNTIaNlEUu3T3LaO8Pg9vhVV97TxfFeU8F+tcYQ==", - "peerDependencies": { - "react": ">=16.x.x", - "react-native": ">=0.62.x", - "react-native-gesture-handler": ">=2.x.x", - "react-native-reanimated": ">=2.x.x" - } - }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -17940,12 +17928,6 @@ } } }, - "@likashefqet/react-native-image-zoom": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@likashefqet/react-native-image-zoom/-/react-native-image-zoom-1.3.0.tgz", - "integrity": "sha512-PLRd1hNMHe9LUn8b4rmLt86282geuaqP4Qd2rFWIloxMS2ePNTIaNlEUu3T3LaO8Pg9vhVV97TxfFeU8F+tcYQ==", - "requires": {} - }, "@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", diff --git a/package.json b/package.json index 9c8e06f..bf894a1 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ }, "dependencies": { "@bankify/redux-persist-realm": "^0.1.3", - "@likashefqet/react-native-image-zoom": "^1.3.0", "@react-native-clipboard/clipboard": "^1.11.2", "@react-native-community/hooks": "^3.0.0", "@react-navigation/bottom-tabs": "^6.5.8", diff --git a/src/components/animatedImage.tsx b/src/components/animatedImage.tsx new file mode 100644 index 0000000..4effec5 --- /dev/null +++ b/src/components/animatedImage.tsx @@ -0,0 +1,56 @@ +import React, { ComponentProps, useMemo } from 'react'; +import { Image } from 'react-native'; +import { Gesture, GestureDetector } from 'react-native-gesture-handler'; +import Animated, { + useAnimatedStyle, + useSharedValue, + withSpring, +} from 'react-native-reanimated'; + +const AnimatedImage = ({ ...props }: ComponentProps) => { + const scale = useSharedValue(1); + const translateX = useSharedValue(0); + const translateY = useSharedValue(0); + + const animatedStyles = useAnimatedStyle(() => { + return { + transform: [ + { translateX: translateX.value }, + { translateY: translateY.value }, + { scale: scale.value }, + ], + }; + }); + + const gesture = useMemo( + () => + Gesture.Simultaneous( + Gesture.Pinch() + .onUpdate(event => { + scale.value = event.scale; + }) + .onFinalize(() => { + scale.value = withSpring(1); + }), + Gesture.Pan() + .minPointers(2) + .onUpdate(event => { + translateX.value = event.translationX; + translateY.value = event.translationY; + }) + .onFinalize(() => { + translateX.value = withSpring(0); + translateY.value = withSpring(0); + }), + ), + [scale, translateX, translateY], + ); + + return ( + + + + ); +}; + +export default AnimatedImage; diff --git a/src/components/index.ts b/src/components/index.ts index f4db135..aaec59b 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,11 +1,13 @@ export { MemesList, MemeEditor, + MemeFail, MemesHeader, MemeTagSelector, MemeViewItem, } from './memes'; export { TagChip, TagEditor, TagPreview, TagRow, TagsHeader } from './tags'; +export { default as AnimatedImage } from './animatedImage'; export { default as FloatingActionButton } from './floatingActionButton'; export { default as HideableBottomNavigationBar } from './hideableBottomNavigationBar'; export { default as HideableHeader } from './hideableHeader'; diff --git a/src/components/memes/memeEditor.tsx b/src/components/memes/memeEditor.tsx index 185927f..0d14879 100644 --- a/src/components/memes/memeEditor.tsx +++ b/src/components/memes/memeEditor.tsx @@ -3,8 +3,7 @@ import { HelperText, Text, TextInput, useTheme } from 'react-native-paper'; import { Image } from 'react-native'; import { useSafeAreaFrame } from 'react-native-safe-area-context'; import { useImageDimensions } from '@react-native-community/hooks/lib/useImageDimensions'; -import LoadingView from '../loadingView'; -import { MemeFail, MemeTagSelector } from '.'; +import { MemeFail, MemeTagSelector, LoadingView } from '..'; import { Tag } from '../../database'; import { StringValidationResult, validateMemeTitle } from '../../utilities'; diff --git a/src/components/memes/memeViewItem.tsx b/src/components/memes/memeViewItem.tsx index 5216d34..05a314e 100644 --- a/src/components/memes/memeViewItem.tsx +++ b/src/components/memes/memeViewItem.tsx @@ -1,14 +1,12 @@ import React from 'react'; -import { ImageZoom } from '@likashefqet/react-native-image-zoom'; import { StyleSheet, View } from 'react-native'; import { useSafeAreaFrame } from 'react-native-safe-area-context'; import { useImageDimensions } from '@react-native-community/hooks'; import { AndroidScoped } from 'react-native-file-access'; -import LoadingView from '../loadingView'; -import { Meme } from '../../database'; -import MemeFail from './memeFail'; import { useSelector } from 'react-redux'; +import { Meme } from '../../database'; import { RootState } from '../../state'; +import { AnimatedImage, LoadingView, MemeFail } from '..'; const memeViewItemStyles = StyleSheet.create({ view: { @@ -47,7 +45,7 @@ const MemeViewItem = ({ meme }: { meme: Meme }) => { iconSize={50} /> ) : ( - width / (height - 128) @@ -61,7 +59,6 @@ const MemeViewItem = ({ meme }: { meme: Meme }) => { height: height - 128, } } - minScale={0.5} /> )}