diff --git a/TestsExample/App.js b/TestsExample/App.js
index dba8aabdb0..9c41f7d1b8 100644
--- a/TestsExample/App.js
+++ b/TestsExample/App.js
@@ -37,6 +37,7 @@ import Test852 from './src/Test852';
import Test861 from './src/Test861';
import Test865 from './src/Test865';
import Test881 from './src/Test881';
+import Test898 from './src/Test898';
export default function App() {
return ;
diff --git a/TestsExample/android/app/build.gradle b/TestsExample/android/app/build.gradle
index 59a85f981f..1f5359c242 100644
--- a/TestsExample/android/app/build.gradle
+++ b/TestsExample/android/app/build.gradle
@@ -78,7 +78,7 @@ import com.android.build.OutputFile
*/
project.ext.react = [
- enableHermes: false, // clean and rebuild if changing
+ enableHermes: true, // clean and rebuild if changing
]
apply from: "../../node_modules/react-native/react.gradle"
diff --git a/TestsExample/android/app/src/main/java/com/testsexample/MainApplication.java b/TestsExample/android/app/src/main/java/com/testsexample/MainApplication.java
index 87165af509..ca5acfe231 100644
--- a/TestsExample/android/app/src/main/java/com/testsexample/MainApplication.java
+++ b/TestsExample/android/app/src/main/java/com/testsexample/MainApplication.java
@@ -11,6 +11,8 @@
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import com.swmansion.rnscreens.RNScreensPackage;
+import com.facebook.react.bridge.JSIModulePackage;
+import com.swmansion.reanimated.ReanimatedJSIModulePackage;
public class MainApplication extends Application implements ReactApplication {
@@ -35,6 +37,11 @@ protected List getPackages() {
protected String getJSMainModuleName() {
return "index";
}
+
+ @Override
+ protected JSIModulePackage getJSIModulePackage() {
+ return new ReanimatedJSIModulePackage();
+ }
};
@Override
diff --git a/TestsExample/babel.config.js b/TestsExample/babel.config.js
index c5b2760932..dfd47830c0 100644
--- a/TestsExample/babel.config.js
+++ b/TestsExample/babel.config.js
@@ -9,5 +9,6 @@ module.exports = {
},
},
],
+ 'react-native-reanimated/plugin',
]
};
diff --git a/TestsExample/ios/Podfile.lock b/TestsExample/ios/Podfile.lock
index 30c1cd19d1..9cd8544b60 100644
--- a/TestsExample/ios/Podfile.lock
+++ b/TestsExample/ios/Podfile.lock
@@ -326,9 +326,36 @@ PODS:
- React
- RNGestureHandler (1.8.0):
- React
- - RNReanimated (1.13.1):
+ - RNReanimated (2.1.0):
+ - DoubleConversion
+ - FBLazyVector
+ - FBReactNativeSpec
+ - glog
+ - RCT-Folly
+ - RCTRequired
+ - RCTTypeSafety
- React
- - RNScreens (3.0.0):
+ - React-callinvoker
+ - React-Core
+ - React-Core/DevSupport
+ - React-Core/RCTWebSocket
+ - React-CoreModules
+ - React-cxxreact
+ - React-jsi
+ - React-jsiexecutor
+ - React-jsinspector
+ - React-RCTActionSheet
+ - React-RCTAnimation
+ - React-RCTBlob
+ - React-RCTImage
+ - React-RCTLinking
+ - React-RCTNetwork
+ - React-RCTSettings
+ - React-RCTText
+ - React-RCTVibration
+ - ReactCommon/turbomodule/core
+ - Yoga
+ - RNScreens (3.1.1):
- React-Core
- RNSearchBar (3.5.1):
- React
@@ -494,7 +521,7 @@ SPEC CHECKSUMS:
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: cde416483dac037923206447da6e1454df403714
FBLazyVector: 49cbe4b43e445b06bf29199b6ad2057649e4c8f5
- FBReactNativeSpec: 749bd5fcb59a424f4f71bebcc6aebf985d791535
+ FBReactNativeSpec: 5a7daebd6daac176b1060ad4b7f921db88c14acd
Flipper: d3da1aa199aad94455ae725e9f3aa43f3ec17021
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
Flipper-Folly: f7a3caafbd74bda4827954fd7a6e000e36355489
@@ -533,8 +560,8 @@ SPEC CHECKSUMS:
ReactCommon: cfe2b7fd20e0dbd2d1185cd7d8f99633fbc5ff05
RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f
RNGestureHandler: 7a5833d0f788dbd107fbb913e09aa0c1ff333c39
- RNReanimated: dd8c286ab5dd4ba36d3a7fef8bff7e08711b5476
- RNScreens: e8e8dd0588b5da0ab57dcca76ab9b2d8987757e0
+ RNReanimated: b8c8004b43446e3c2709fe64b2b41072f87428ad
+ RNScreens: bd1523c3bde7069b8e958e5a16e1fc7722ad0bdd
RNSearchBar: 9860431356b7d12a8449d2fddb2b5f3c78d1e99f
RNVectorIcons: bc69e6a278b14842063605de32bec61f0b251a59
Yoga: 8c8436d4171c87504c648ae23b1d81242bdf3bbf
diff --git a/TestsExample/package.json b/TestsExample/package.json
index 2ca79562bd..7b864f606e 100644
--- a/TestsExample/package.json
+++ b/TestsExample/package.json
@@ -25,7 +25,7 @@
"react-native-appearance": "^0.3.4",
"react-native-gesture-handler": "^1.8.0",
"react-native-paper": "^4.3.1",
- "react-native-reanimated": "^1.13.1",
+ "react-native-reanimated": "^2.1.0",
"react-native-redash": "^15.11.1",
"react-native-safe-area-context": "^3.1.9",
"react-native-search-bar": "^3.5.1",
diff --git a/TestsExample/src/Test898.tsx b/TestsExample/src/Test898.tsx
new file mode 100644
index 0000000000..1db1b428ab
--- /dev/null
+++ b/TestsExample/src/Test898.tsx
@@ -0,0 +1,353 @@
+import React, { useState, useEffect, RefObject } from 'react';
+import Animated, {
+ useSharedValue,
+ useAnimatedStyle,
+ useAnimatedGestureHandler,
+ interpolate,
+ Extrapolate,
+ withTiming,
+ Easing,
+ useAnimatedRef,
+ measure,
+ runOnJS,
+} from 'react-native-reanimated';
+import {
+ Dimensions,
+ StyleSheet,
+ View,
+ Image,
+ Platform,
+} from 'react-native';
+import {
+ ScrollView,
+ PanGestureHandler,
+ TapGestureHandler,
+ TapGestureHandlerGestureEvent,
+} from 'react-native-gesture-handler';
+import {createNativeStackNavigator} from 'react-native-screens/native-stack';
+import {NavigationContainer} from '@react-navigation/native';
+import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
+
+
+const AnimatedImage = Animated.createAnimatedComponent(Image);
+
+const dimensions = Dimensions.get('window');
+const GUTTER_WIDTH = 3;
+const NUMBER_OF_IMAGES = 4;
+const IMAGE_SIZE =
+ (dimensions.width - GUTTER_WIDTH * (NUMBER_OF_IMAGES - 1)) / NUMBER_OF_IMAGES;
+
+type ExampleImage = {
+ uri: string;
+ width: number;
+ height: number;
+};
+type ActiveExampleImageProperties = {
+ x: Animated.SharedValue;
+ y: Animated.SharedValue;
+ width: Animated.SharedValue;
+ height: Animated.SharedValue;
+ imageOpacity: Animated.SharedValue;
+};
+type ActiveExampleImage = ActiveExampleImageProperties & {
+ // @ts-ignore: FIXME AnimatedImage type
+ animatedRef: RefObject;
+ item: ExampleImage;
+};
+
+type onItemPressFn = (
+ animatedRef: RefObject,
+ item: ExampleImage,
+ svs: ActiveExampleImageProperties
+) => void;
+function ImageList({
+ images,
+ onItemPress,
+}: {
+ images: ExampleImage[];
+ onItemPress: onItemPressFn;
+}) {
+ return (
+
+ {images.map((item, i) => (
+
+ ))}
+
+ );
+}
+
+type ListItemProps = {
+ item: ExampleImage;
+ index: number;
+ onPress: onItemPressFn;
+};
+function ListItem({ item, index, onPress }: ListItemProps) {
+ // @ts-ignore: FIXME(TS) correct type for createAnimatedComponent
+ const ref = useAnimatedRef();
+ const opacity = useSharedValue(1);
+ const statusBarInset = useSafeAreaInsets().top; // inset of the status bar
+ const headerHeight = statusBarInset + 44;
+
+ const containerStyle = {
+ marginRight: (index + 1) % 4 === 0 ? 0 : GUTTER_WIDTH,
+ marginBottom: GUTTER_WIDTH,
+ };
+
+ const styles = useAnimatedStyle(() => {
+ return {
+ width: IMAGE_SIZE,
+ height: IMAGE_SIZE,
+ opacity: opacity.value,
+ };
+ });
+
+ const width = useSharedValue(0);
+ const height = useSharedValue(0);
+ const x = useSharedValue(0);
+ const y = useSharedValue(0);
+
+ function handlePress() {
+ onPress(ref, item, { imageOpacity: opacity, width, height, x, y });
+ }
+
+ const handler = useAnimatedGestureHandler({
+ onFinish: (_evt, _ctx, isCanceledOrFailed) => {
+ if (isCanceledOrFailed) {
+ return;
+ }
+
+ // measure the image
+ // width/height and position to animate from it to the full screen one
+ const measurements = measure(ref);
+
+ width.value = measurements.width;
+ height.value = measurements.height;
+ x.value = measurements.pageX;
+ y.value = measurements.pageY - headerHeight;
+
+ runOnJS(handlePress)();
+ },
+ });
+
+ return (
+
+
+
+
+
+ );
+}
+
+const timingConfig = {
+ duration: 240,
+ easing: Easing.bezier(0.33, 0.01, 0, 1),
+};
+
+function ImageTransition({
+ activeImage,
+ onClose,
+}: {
+ activeImage: ActiveExampleImage;
+ onClose: () => void;
+}) {
+ const { item, x, y, width, height, imageOpacity } = activeImage;
+ const { uri } = item;
+
+ const targetWidth = dimensions.width;
+ const scaleFactor = item.width / targetWidth;
+ const targetHeight = item.height / scaleFactor;
+
+ const statusBarInset = useSafeAreaInsets().top; // inset of the status bar
+ const headerHeight = statusBarInset + 44;
+
+ const animationProgress = useSharedValue(0);
+
+ const backdropOpacity = useSharedValue(0);
+ const scale = useSharedValue(1);
+
+ const targetX = useSharedValue(0);
+ const targetY = useSharedValue(
+ (dimensions.height - targetHeight) / 2 - headerHeight
+ );
+
+ const translateX = useSharedValue(0);
+ const translateY = useSharedValue(0);
+
+ const onPan = useAnimatedGestureHandler({
+ onActive: (event) => {
+ translateX.value = event.translationX;
+ translateY.value = event.translationY;
+
+ scale.value = interpolate(
+ translateY.value,
+ [-200, 0, 200],
+ [0.65, 1, 0.65],
+ Extrapolate.CLAMP
+ );
+
+ backdropOpacity.value = interpolate(
+ translateY.value,
+ [-100, 0, 100],
+ [0, 1, 0],
+ Extrapolate.CLAMP
+ );
+ },
+
+ onEnd: (_event, _ctx) => {
+ if (Math.abs(translateY.value) > 40) {
+ targetX.value = translateX.value - targetX.value * -1;
+ targetY.value = translateY.value - targetY.value * -1;
+
+ translateX.value = 0;
+ translateY.value = 0;
+
+ animationProgress.value = withTiming(0, timingConfig, () => {
+ imageOpacity.value = withTiming(
+ 1,
+ {
+ duration: 16,
+ },
+ () => {
+ runOnJS(onClose)();
+ }
+ );
+ });
+
+ backdropOpacity.value = withTiming(0, timingConfig);
+ } else {
+ backdropOpacity.value = withTiming(1, timingConfig);
+ translateX.value = withTiming(0, timingConfig);
+ translateY.value = withTiming(0, timingConfig);
+ }
+
+ scale.value = withTiming(1, timingConfig);
+ },
+ });
+
+ const imageStyles = useAnimatedStyle(() => {
+ const interpolateProgress = (range: [number, number]) =>
+ interpolate(animationProgress.value, [0, 1], range, Extrapolate.CLAMP);
+
+ const top =
+ translateY.value + interpolateProgress([y.value, targetY.value]);
+ const left =
+ translateX.value + interpolateProgress([x.value, targetX.value]);
+
+ return {
+ position: 'absolute',
+ top,
+ left,
+ width: interpolateProgress([width.value, targetWidth]),
+ height: interpolateProgress([height.value, targetHeight]),
+ transform: [
+ {
+ scale: scale.value,
+ },
+ ],
+ };
+ });
+
+ const backdropStyles = useAnimatedStyle(() => {
+ return {
+ opacity: backdropOpacity.value,
+ };
+ });
+
+ useEffect(() => {
+ // fixes flickering
+ requestAnimationFrame(() => {
+ imageOpacity.value = 0;
+ });
+
+ animationProgress.value = withTiming(1, timingConfig);
+ backdropOpacity.value = withTiming(1, timingConfig);
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const images: ExampleImage[] = Array.from({ length: 30 }, (_, index) => {
+ return {
+ uri: `https://picsum.photos/id/${index + 10}/400/400`,
+ width: dimensions.width,
+ height: 400,
+ };
+});
+
+function LightboxExample(): React.ReactElement {
+ const [activeImage, setActiveImage] = useState(
+ null
+ );
+
+ function onItemPress(
+ // @ts-ignore: FIXME AnimatedImage type
+ animatedRef: RefObject,
+ item: ExampleImage,
+ svs: ActiveExampleImageProperties
+ ) {
+ setActiveImage({
+ animatedRef,
+ item,
+ ...svs,
+ });
+ }
+
+ function onClose() {
+ setActiveImage(null);
+ }
+
+ const statusBarInset = useSafeAreaInsets().top; // inset of the status bar
+ const headerHeight = statusBarInset + 44;
+ const height =
+ Platform.OS === 'web' ? dimensions.height - headerHeight : undefined;
+
+ return (
+
+
+
+ {activeImage && (
+
+ )}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ paddingTop: 0,
+ },
+
+ scrollContainer: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ },
+
+ backdrop: {
+ ...StyleSheet.absoluteFillObject,
+ backgroundColor: 'black',
+ },
+});
+
+const Stack = createNativeStackNavigator();
+
+export default function App() {
+ return(
+
+
+
+
+
+
+
+ )
+}
diff --git a/TestsExample/yarn.lock b/TestsExample/yarn.lock
index a5bc49cf71..e6cba4e551 100644
--- a/TestsExample/yarn.lock
+++ b/TestsExample/yarn.lock
@@ -958,6 +958,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
+"@babel/plugin-transform-object-assign@^7.10.4":
+ version "7.12.13"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.12.13.tgz#d9b9200a69e03403a813e44a933ad9f4bddfd050"
+ integrity sha512-4QxDMc0lAOkIBSfCrnSGbAJ+4epDBF2XXwcLXuBcG1xl9u7LrktNVD4+LwhL47XuKVPQ7R25e/WdcV+h97HyZA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.12.13"
+
"@babel/plugin-transform-object-super@^7.0.0", "@babel/plugin-transform-object-super@^7.12.1":
version "7.12.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz#4ea08696b8d2e65841d0c7706482b048bed1066e"
@@ -2889,11 +2896,6 @@ core-js@^1.0.0:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=
-core-js@^2.4.1:
- version "2.6.11"
- resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
- integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
-
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@@ -2909,6 +2911,13 @@ cosmiconfig@^5.0.5, cosmiconfig@^5.1.0:
js-yaml "^3.13.1"
parse-json "^4.0.0"
+cross-fetch@^3.0.4:
+ version "3.1.4"
+ resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39"
+ integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==
+ dependencies:
+ node-fetch "2.6.1"
+
cross-spawn@^6.0.0, cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
@@ -3624,14 +3633,13 @@ fbjs@^0.8.4:
setimmediate "^1.0.5"
ua-parser-js "^0.7.18"
-fbjs@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-1.0.0.tgz#52c215e0883a3c86af2a7a776ed51525ae8e0a5a"
- integrity sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA==
+fbjs@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.0.tgz#0907067fb3f57a78f45d95f1eacffcacd623c165"
+ integrity sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg==
dependencies:
- core-js "^2.4.1"
+ cross-fetch "^3.0.4"
fbjs-css-vars "^1.0.0"
- isomorphic-fetch "^2.1.1"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
@@ -5726,6 +5734,11 @@ mkdirp@^0.5.1:
dependencies:
minimist "^1.2.5"
+mockdate@^3.0.2:
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/mockdate/-/mockdate-3.0.5.tgz#789be686deb3149e7df2b663d2bc4392bc3284fb"
+ integrity sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==
+
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -5795,6 +5808,11 @@ node-dir@^0.1.17:
dependencies:
minimatch "^3.0.2"
+node-fetch@2.6.1, node-fetch@^2.2.0, node-fetch@^2.6.0:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
+ integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
+
node-fetch@^1.0.1:
version "1.7.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
@@ -5803,11 +5821,6 @@ node-fetch@^1.0.1:
encoding "^0.1.11"
is-stream "^1.0.1"
-node-fetch@^2.2.0, node-fetch@^2.6.0:
- version "2.6.1"
- resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
- integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
-
node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
@@ -6479,12 +6492,15 @@ react-native-paper@^4.3.1:
color "^3.1.2"
react-native-safe-area-view "^0.14.9"
-react-native-reanimated@^1.13.1:
- version "1.13.1"
- resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-1.13.1.tgz#c370c32cc4d447ae896cb029bb9c6a2f7608c5b4"
- integrity sha512-3sF46jts9MbktgIasf0sTM8uhOYO5a5Q3YyQ4X1jjSE82n/fY2nW3XTFsLGfLEpK2ir4XSDhQWVgFHazaXZTww==
+react-native-reanimated@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.1.0.tgz#b9ad04aee490e1e030d0a6cdaa43a14895d9a54d"
+ integrity sha512-tlPvvcdf+X7HGQ7g/7npBFhwMznfdk7MHUc9gUB/kp2abSscXNe/kOVKlrNEOO4DS11rNOXc+llFxVFMuNk0zA==
dependencies:
- fbjs "^1.0.0"
+ "@babel/plugin-transform-object-assign" "^7.10.4"
+ fbjs "^3.0.0"
+ mockdate "^3.0.2"
+ string-hash-64 "^1.0.3"
react-native-redash@^15.11.1:
version "15.11.1"
@@ -7362,6 +7378,11 @@ strict-uri-encode@^2.0.0:
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
+string-hash-64@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/string-hash-64/-/string-hash-64-1.0.3.tgz#0deb56df58678640db5c479ccbbb597aaa0de322"
+ integrity sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw==
+
string-length@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a"