mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53860 Fixes https://github.com/facebook/react-native/issues/53639 I've realized that our docs and validation logic states that we do support 3x3 matrixes (for 2d transforms). However they're not properly processed as the values are just copied into a 4x4 matrix. This can be verified by applying the 3x3 identity matrix on any transform. I'm fixing it by correctly populating the 4x4 matrix getting the values from the 3x3 matrix in input. Changelog: [General] [Fixed] - 9-element (2d) transform matrix are not correctly working Reviewed By: christophpurrer Differential Revision: D82836192 fbshipit-source-id: 12029b37ffd8375fff48ea7f9386b849dfe96a62
452 lines
9.9 KiB
JavaScript
452 lines
9.9 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
|
|
* @format
|
|
*/
|
|
|
|
import type {RNTesterModuleExample} from '../../types/RNTesterTypes';
|
|
|
|
import * as React from 'react';
|
|
import {useEffect, useRef, useState} from 'react';
|
|
import {Animated, Easing, StyleSheet, Text, View} from 'react-native';
|
|
|
|
function AnimateTransformSingleProp() {
|
|
const [theta] = useState(new Animated.Value(45));
|
|
const animate = () => {
|
|
theta.setValue(0);
|
|
Animated.timing(theta, {
|
|
toValue: 100,
|
|
duration: 3000,
|
|
useNativeDriver: false,
|
|
}).start(animate);
|
|
};
|
|
|
|
useEffect(() => {
|
|
animate();
|
|
});
|
|
|
|
return (
|
|
<View style={styles.flipCardContainer}>
|
|
<Animated.View
|
|
style={[
|
|
styles.box6,
|
|
{
|
|
transform: [
|
|
{
|
|
rotate: theta.interpolate({
|
|
inputRange: [0, 100],
|
|
outputRange: ['0deg', '360deg'],
|
|
}),
|
|
},
|
|
],
|
|
},
|
|
]}>
|
|
<Text style={styles.flipText}>This text is flipping great.</Text>
|
|
</Animated.View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
function TransformOriginExample() {
|
|
const rotateAnim = useRef(new Animated.Value(0)).current;
|
|
|
|
useEffect(() => {
|
|
Animated.loop(
|
|
Animated.timing(rotateAnim, {
|
|
toValue: 1,
|
|
duration: 5000,
|
|
easing: Easing.linear,
|
|
useNativeDriver: true,
|
|
}),
|
|
).start();
|
|
}, [rotateAnim]);
|
|
|
|
const spin = rotateAnim.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: ['0deg', '360deg'],
|
|
});
|
|
|
|
return (
|
|
<View style={styles.transformOriginWrapper}>
|
|
<Animated.View
|
|
style={[
|
|
styles.transformOriginView,
|
|
{
|
|
transform: [{rotate: spin}],
|
|
},
|
|
]}
|
|
/>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
function Flip() {
|
|
const [theta] = useState(new Animated.Value(45));
|
|
const animate = () => {
|
|
theta.setValue(0);
|
|
Animated.timing(theta, {
|
|
toValue: 360,
|
|
duration: 5000,
|
|
useNativeDriver: false,
|
|
}).start(animate);
|
|
};
|
|
|
|
useEffect(() => {
|
|
animate();
|
|
});
|
|
|
|
return (
|
|
<View style={styles.flipCardContainer}>
|
|
<Animated.View
|
|
style={[
|
|
styles.flipCard,
|
|
{
|
|
transform: [
|
|
{perspective: 850},
|
|
{
|
|
rotateX: theta.interpolate({
|
|
inputRange: [0, 180],
|
|
outputRange: ['0deg', '180deg'],
|
|
}),
|
|
},
|
|
],
|
|
},
|
|
]}>
|
|
<Text style={styles.flipText}>This text is flipping great.</Text>
|
|
</Animated.View>
|
|
<Animated.View
|
|
style={[
|
|
styles.flipCard,
|
|
styles.flipCard1,
|
|
{
|
|
transform: [
|
|
{perspective: 850},
|
|
{
|
|
rotateX: theta.interpolate({
|
|
inputRange: [0, 180],
|
|
outputRange: ['180deg', '360deg'],
|
|
}),
|
|
},
|
|
],
|
|
},
|
|
]}>
|
|
<Text style={styles.flipText}>On the flip side...</Text>
|
|
</Animated.View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
function TranslatePercentage() {
|
|
return <View style={styles.translatePercentageView} />;
|
|
}
|
|
|
|
function TranslateMatrix2D() {
|
|
return <View style={styles.translateMatrix2D} />;
|
|
}
|
|
function TranslateMatrix3D() {
|
|
return <View style={styles.translateMatrix3D} />;
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
height: 500,
|
|
},
|
|
box1: {
|
|
left: 0,
|
|
backgroundColor: 'green',
|
|
height: 50,
|
|
position: 'absolute',
|
|
top: 0,
|
|
transform: [
|
|
{translateX: 100},
|
|
{translateY: 50},
|
|
{rotate: '30deg'},
|
|
{scaleX: 2},
|
|
{scaleY: 2},
|
|
],
|
|
width: 50,
|
|
},
|
|
box2: {
|
|
left: 0,
|
|
backgroundColor: 'purple',
|
|
height: 50,
|
|
position: 'absolute',
|
|
top: 0,
|
|
transform: [
|
|
{scaleX: 2},
|
|
{scaleY: 2},
|
|
{translateX: 100},
|
|
{translateY: 50},
|
|
{rotate: '30deg'},
|
|
],
|
|
width: 50,
|
|
},
|
|
box3step1: {
|
|
left: 0,
|
|
backgroundColor: 'lightpink',
|
|
height: 50,
|
|
position: 'absolute',
|
|
top: 0,
|
|
transform: [{rotate: '30deg'}],
|
|
width: 50,
|
|
},
|
|
box3step2: {
|
|
left: 0,
|
|
backgroundColor: 'hotpink',
|
|
height: 50,
|
|
opacity: 0.5,
|
|
position: 'absolute',
|
|
top: 0,
|
|
transform: [{rotate: '30deg'}, {scaleX: 2}, {scaleY: 2}],
|
|
width: 50,
|
|
},
|
|
box3step3: {
|
|
left: 0,
|
|
backgroundColor: 'deeppink',
|
|
height: 50,
|
|
opacity: 0.5,
|
|
position: 'absolute',
|
|
top: 0,
|
|
transform: [
|
|
{rotate: '30deg'},
|
|
{scaleX: 2},
|
|
{scaleY: 2},
|
|
{translateX: 100},
|
|
{translateY: 50},
|
|
],
|
|
width: 50,
|
|
},
|
|
box4: {
|
|
left: 0,
|
|
backgroundColor: 'darkorange',
|
|
height: 50,
|
|
position: 'absolute',
|
|
top: 0,
|
|
transform: [{translate: [200, 350]}, {scale: 2.5}, {rotate: '-0.2rad'}],
|
|
width: 100,
|
|
},
|
|
box5: {
|
|
backgroundColor: 'maroon',
|
|
height: 50,
|
|
position: 'absolute',
|
|
right: 0,
|
|
top: 0,
|
|
width: 50,
|
|
},
|
|
box5Transform: {
|
|
transform: [{translate: [-50, 35]}, {rotate: '50deg'}, {scale: 2}],
|
|
},
|
|
box6: {
|
|
backgroundColor: 'salmon',
|
|
alignSelf: 'center',
|
|
},
|
|
box7: {
|
|
backgroundColor: 'lightseagreen',
|
|
height: 50,
|
|
position: 'absolute',
|
|
right: 0,
|
|
top: 0,
|
|
width: 50,
|
|
},
|
|
box7Transform: {
|
|
transform: 'translate(-50px, 35px) rotate(50deg) scale(2)',
|
|
},
|
|
flipCardContainer: {
|
|
marginVertical: 40,
|
|
flex: 1,
|
|
alignSelf: 'center',
|
|
zIndex: 0,
|
|
},
|
|
flipCard: {
|
|
width: 200,
|
|
height: 200,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
backgroundColor: 'blue',
|
|
backfaceVisibility: 'hidden',
|
|
},
|
|
flipCard1: {
|
|
position: 'absolute',
|
|
top: 0,
|
|
backgroundColor: 'red',
|
|
},
|
|
flipText: {
|
|
width: 90,
|
|
fontSize: 20,
|
|
color: 'white',
|
|
fontWeight: 'bold',
|
|
},
|
|
transformOriginWrapper: {
|
|
alignItems: 'center',
|
|
},
|
|
transformOriginView: {
|
|
backgroundColor: 'pink',
|
|
width: 100,
|
|
height: 100,
|
|
transformOrigin: 'top left',
|
|
},
|
|
translatePercentageView: {
|
|
transform: 'translate(50%)',
|
|
padding: 50,
|
|
alignSelf: 'flex-start',
|
|
backgroundColor: 'lightblue',
|
|
},
|
|
translateMatrix2D: {
|
|
transform: [{matrix: [1, 0, 0, 0, 1, 0, 0, 0, 1]}],
|
|
width: 50,
|
|
height: 50,
|
|
backgroundColor: 'red',
|
|
},
|
|
translateMatrix3D: {
|
|
transform: [{matrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]}],
|
|
height: 50,
|
|
width: 50,
|
|
backgroundColor: 'green',
|
|
},
|
|
});
|
|
|
|
exports.title = 'Transforms';
|
|
exports.category = 'UI';
|
|
exports.documentationURL = 'https://reactnative.dev/docs/transforms';
|
|
exports.description = 'View transforms';
|
|
exports.examples = [
|
|
{
|
|
title: 'Perspective, Rotate, Animation',
|
|
description: 'perspective: 850, rotateX: Animated.timing(0 -> 360)',
|
|
render(): React.Node {
|
|
return <Flip />;
|
|
},
|
|
},
|
|
{
|
|
title: 'Translate, Rotate, Scale',
|
|
description:
|
|
"translateX: 100, translateY: 50, rotate: '30deg', scaleX: 2, scaleY: 2",
|
|
render(): React.Node {
|
|
return (
|
|
<View style={styles.container}>
|
|
<View style={styles.box1} />
|
|
</View>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
title: 'Scale, Translate, Rotate, ',
|
|
description:
|
|
"scaleX: 2, scaleY: 2, translateX: 100, translateY: 50, rotate: '30deg'",
|
|
render(): React.Node {
|
|
return (
|
|
<View style={styles.container}>
|
|
<View style={styles.box2} />
|
|
</View>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
title: 'Rotate',
|
|
description: "rotate: '30deg'",
|
|
render(): React.Node {
|
|
return (
|
|
<View style={styles.container}>
|
|
<View style={styles.box3step1} />
|
|
</View>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
title: 'Rotate, Scale',
|
|
description: "rotate: '30deg', scaleX: 2, scaleY: 2",
|
|
render(): React.Node {
|
|
return (
|
|
<View style={styles.container}>
|
|
<View style={styles.box3step2} />
|
|
</View>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
title: 'Rotate, Scale, Translate ',
|
|
description:
|
|
"rotate: '30deg', scaleX: 2, scaleY: 2, translateX: 100, translateY: 50",
|
|
render(): React.Node {
|
|
return (
|
|
<View style={styles.container}>
|
|
<View style={styles.box3step3} />
|
|
</View>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
title: 'Translate, Scale, Rotate',
|
|
description: "translate: [200, 350], scale: 2.5, rotate: '-0.2rad'",
|
|
render(): React.Node {
|
|
return (
|
|
<View style={styles.container}>
|
|
<View style={styles.box4} />
|
|
</View>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
title: 'Translate, Rotate, Scale',
|
|
description: "translate: [-50, 35], rotate: '50deg', scale: 2",
|
|
render(): React.Node {
|
|
return (
|
|
<View style={styles.container}>
|
|
<View style={[styles.box5, styles.box5Transform]} />
|
|
</View>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
title: 'Animate Translate single prop',
|
|
description: "rotate: '360deg'",
|
|
render(): React.Node {
|
|
return <AnimateTransformSingleProp />;
|
|
},
|
|
},
|
|
{
|
|
title: 'Transform using a string',
|
|
description: "transform: 'translate(-50px, 35px) rotate(50deg) scale(2)'",
|
|
render(): React.Node {
|
|
return (
|
|
<View style={styles.container}>
|
|
<View style={[styles.box7, styles.box7Transform]} />
|
|
</View>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
title: 'Transform origin',
|
|
description: "transformOrigin: 'top left'",
|
|
render(): React.Node {
|
|
return <TransformOriginExample />;
|
|
},
|
|
},
|
|
{
|
|
title: 'Translate Percentage',
|
|
description: "transform: 'translate(50%)'",
|
|
render(): React.Node {
|
|
return <TranslatePercentage />;
|
|
},
|
|
},
|
|
{
|
|
title: 'Transform Matrix 2D',
|
|
description: "transform: 'matrix(1, 0, 0, 0, 1, 0, 0, 0, 1)'",
|
|
render(): React.Node {
|
|
return <TranslateMatrix2D />;
|
|
},
|
|
},
|
|
{
|
|
title: 'Transform Matrix 3D',
|
|
description:
|
|
"transform: 'matrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)'",
|
|
render(): React.Node {
|
|
return <TranslateMatrix3D />;
|
|
},
|
|
},
|
|
] as Array<RNTesterModuleExample>;
|