/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {RNTesterModuleExample} from '../../types/RNTesterTypes';
import ToggleNativeDriver from './utils/ToggleNativeDriver';
import * as React from 'react';
import {useLayoutEffect, useRef, useState} from 'react';
import {
Animated,
Button,
PanResponder,
StyleSheet,
Text,
View,
} from 'react-native';
function TextBox({children}: $ReadOnly<{children: React.Node}>): React.Node {
// Prevent touch from being hijacked by Text
return (
{children}
);
}
function AnimatedEventExample({
containerPageXY,
useNativeDriver,
}: $ReadOnly<{
containerPageXY: $ReadOnly<{x: number, y: number}>,
useNativeDriver: boolean,
}>): React.Node {
const boxRef = useRef>();
const pointerPageXY = useRef(
new Animated.ValueXY(
{
x: containerPageXY.x,
y: containerPageXY.y,
},
{useNativeDriver},
),
).current;
const dragStartOffsetXY = useRef(
new Animated.ValueXY({x: 0, y: 0}, {useNativeDriver}),
).current;
// We'll no longer have to subtract containerPageXY.x/y from offset, if we can animate left/top props natively
// TODO: T222117268 Adopt fabric driven c++ native animated in RNTester
const finalOffsetX = Animated.subtract(
Animated.subtract(pointerPageXY.x, dragStartOffsetXY.x),
containerPageXY.x,
);
const finalOffsetY = Animated.subtract(
Animated.subtract(pointerPageXY.y, dragStartOffsetXY.y),
containerPageXY.y,
);
const syncAnimationToHostView = () => {
boxRef.current?.setNativeProps({
transform: [
{translateX: finalOffsetX.__getValue()},
{translateY: finalOffsetY.__getValue()},
],
});
};
return (
{
// Animated change sometimes doesn't commit to Fabric, and box will jump back to offset before animation
// This is to make sure that finalOffsetX/Y are synced to native host view
// TODO: T222117268 Adopt fabric driven c++ native animated in RNTester
syncAnimationToHostView();
}}
style={[
styles.box,
{
backgroundColor: useNativeDriver ? 'orange' : 'violet',
transform: [
{
translateX: finalOffsetX,
},
{
translateY: finalOffsetY,
},
],
},
]}>
Use {useNativeDriver ? 'Native' : 'JS'} Animated.event
);
}
function PanResponderExample({
useNativeDriver,
}: $ReadOnly<{useNativeDriver: boolean}>): React.Node {
const finalOffsetXY = useRef(
new Animated.ValueXY({x: 0, y: 0}, {useNativeDriver}),
).current;
const dragStartOffsetXY = useRef({x: 0, y: 0}).current;
const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: (pressEvent, gestureState) => {
dragStartOffsetXY.x = finalOffsetXY.x.__getValue();
dragStartOffsetXY.y = finalOffsetXY.y.__getValue();
return true;
},
onPanResponderMove: (pressEvent, gestureState) => {
if (gestureState.dx !== 0) {
finalOffsetXY.x.setValue(dragStartOffsetXY.x + gestureState.dx);
}
if (gestureState.dy !== 0) {
finalOffsetXY.y.setValue(dragStartOffsetXY.y + gestureState.dy);
}
},
}),
).current;
return (
Use PanResponder{' '}
{`+ ${useNativeDriver ? 'Native Animated value' : 'JS Animated value'}`}
);
}
function PanGestureExample(): React.Node {
const [busy, setBusy] = useState(false);
const [useNativeDriver, setUseNativeDriver] = useState(false);
const containerRef = useRef>();
const [containerPageXY, setContainerPageXY] =
useState{x: number, y: number}>(null);
useLayoutEffect(() => {
containerRef.current?.measure((x, y, width, height, pageX, pageY) => {
setContainerPageXY({x: pageX, y: pageY});
});
}, []);
function sleep(t: number) {
setBusy(true);
setTimeout(() => {
const start = Date.now();
while (Date.now() - start < t) {
// sleeping
}
setBusy(false);
}, 1000);
}
return (
);
}
const styles = StyleSheet.create({
container: {
alignItems: 'stretch',
justifyContent: 'flex-start',
},
examplesContainer: {
flex: 1,
},
box: {
width: 160,
height: 160,
alignItems: 'center',
justifyContent: 'center',
padding: 12,
},
text: {
pointerEvents: 'none',
},
});
export default ({
title: 'Pan Gesture',
name: 'panGesture',
description: 'Animations driven by pan gesture.',
render: () => ,
}: RNTesterModuleExample);