mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
84de8a075e
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/51416 Deletes `oncall` annotations from the `facebook/react-native` repository. Changelog: [Internal] Reviewed By: javache Differential Revision: D74902524 fbshipit-source-id: 32a6a5b2ff27281792d572f151e2b094d9a79029
256 lines
6.5 KiB
JavaScript
256 lines
6.5 KiB
JavaScript
/**
|
|
* 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 (
|
|
<View pointerEvents="none">
|
|
<Text style={styles.text}>{children}</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
function AnimatedEventExample({
|
|
containerPageXY,
|
|
useNativeDriver,
|
|
}: $ReadOnly<{
|
|
containerPageXY: $ReadOnly<{x: number, y: number}>,
|
|
useNativeDriver: boolean,
|
|
}>): React.Node {
|
|
const boxRef = useRef<?React.ElementRef<typeof Animated.View>>();
|
|
|
|
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.View
|
|
ref={boxRef}
|
|
onTouchMove={Animated.event(
|
|
[
|
|
{
|
|
nativeEvent: {
|
|
pageX: pointerPageXY.x,
|
|
pageY: pointerPageXY.y,
|
|
},
|
|
},
|
|
],
|
|
{useNativeDriver},
|
|
)}
|
|
onTouchStart={Animated.event(
|
|
[
|
|
{
|
|
nativeEvent: {
|
|
pageX: pointerPageXY.x,
|
|
pageY: pointerPageXY.y,
|
|
locationX: dragStartOffsetXY.x,
|
|
locationY: dragStartOffsetXY.y,
|
|
},
|
|
},
|
|
],
|
|
{useNativeDriver},
|
|
)}
|
|
onTouchEnd={() => {
|
|
// 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,
|
|
},
|
|
],
|
|
},
|
|
]}>
|
|
<TextBox>Use {useNativeDriver ? 'Native' : 'JS'} Animated.event</TextBox>
|
|
</Animated.View>
|
|
);
|
|
}
|
|
|
|
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 (
|
|
<Animated.View
|
|
{...panResponder.panHandlers}
|
|
style={[
|
|
styles.box,
|
|
{
|
|
backgroundColor: useNativeDriver ? 'pink' : 'cyan',
|
|
transform: [
|
|
{
|
|
translateX: finalOffsetXY.x,
|
|
},
|
|
{
|
|
translateY: finalOffsetXY.y,
|
|
},
|
|
],
|
|
},
|
|
]}>
|
|
<TextBox>
|
|
Use PanResponder{' '}
|
|
{`+ ${useNativeDriver ? 'Native Animated value' : 'JS Animated value'}`}
|
|
</TextBox>
|
|
</Animated.View>
|
|
);
|
|
}
|
|
|
|
function PanGestureExample(): React.Node {
|
|
const [busy, setBusy] = useState(false);
|
|
const [useNativeDriver, setUseNativeDriver] = useState(false);
|
|
|
|
const containerRef = useRef<?React.ElementRef<typeof View>>();
|
|
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 (
|
|
<View style={styles.container}>
|
|
<ToggleNativeDriver
|
|
value={useNativeDriver}
|
|
onValueChange={setUseNativeDriver}
|
|
/>
|
|
<Button
|
|
title={busy ? 'js thread blocked...' : 'Block js thread for 5s'}
|
|
onPress={() => {
|
|
sleep(5000);
|
|
}}
|
|
/>
|
|
<View style={styles.examplesContainer} ref={containerRef}>
|
|
{containerPageXY != null ? (
|
|
<AnimatedEventExample
|
|
useNativeDriver={useNativeDriver}
|
|
containerPageXY={containerPageXY}
|
|
/>
|
|
) : null}
|
|
<PanResponderExample useNativeDriver={useNativeDriver} />
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
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: () => <PanGestureExample />,
|
|
}: RNTesterModuleExample);
|