Add custom AnimatedImage component
This also fixes the white flashing when loading images Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
18
package-lock.json
generated
18
package-lock.json
generated
@@ -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",
|
||||
|
@@ -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",
|
||||
|
56
src/components/animatedImage.tsx
Normal file
56
src/components/animatedImage.tsx
Normal file
@@ -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<typeof Image>) => {
|
||||
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 (
|
||||
<GestureDetector gesture={gesture}>
|
||||
<Animated.Image {...props} style={[props.style, animatedStyles]} />
|
||||
</GestureDetector>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnimatedImage;
|
@@ -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';
|
||||
|
@@ -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';
|
||||
|
||||
|
@@ -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}
|
||||
/>
|
||||
) : (
|
||||
<ImageZoom
|
||||
<AnimatedImage
|
||||
source={{ uri }}
|
||||
style={
|
||||
dimensions.aspectRatio > width / (height - 128)
|
||||
@@ -61,7 +59,6 @@ const MemeViewItem = ({ meme }: { meme: Meme }) => {
|
||||
height: height - 128,
|
||||
}
|
||||
}
|
||||
minScale={0.5}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
|
Reference in New Issue
Block a user