如何使用状态动态更改 React Native 转换?
Posted
技术标签:
【中文标题】如何使用状态动态更改 React Native 转换?【英文标题】:How to dynamically change React Native transform with state? 【发布时间】:2019-11-03 21:05:14 【问题描述】:我正在构建一个自定义视图,它将根据设备方向旋转其内容。这个应用程序的方向锁定为纵向,我只想旋转一个视图。它获取当前设备方向,更新状态,然后使用更新后的style=transform: [rotate: 'xxxdeg']
呈现新组件。
我正在使用react-native-orientation-locker
来检测方向变化。
视图在第一次渲染时正确旋转。例如,如果屏幕在设备旋转时加载,它将渲染视图旋转。但是在更改设备或模拟器的方向时,视图不会旋转。它一直锁定在初始化时的 rotate
值。
似乎对transform
rotate
值的更新不会改变旋转。我已经验证在渲染过程中存在新的rotate
值。我已经验证方向更改正确地更新了状态。但是当方向改变时,视图永远不会在 UI 中旋转。就好像 React Native 在渲染期间没有注意到 rotate
值的变化。
我希望rotate
值的更新会相应地轮换View
,但似乎并非如此。是否有其他方法可以完成此操作,或者我在此代码中是否有错误?
编辑:rotate
是否必须是 Animated
值?
import React, useState, useEffect from 'react';
import View from 'react-native';
import Orientation from 'react-native-orientation-locker';
const RotateView = props =>
const getRotation = newOrientation =>
switch (newOrientation)
case 'LANDSCAPE-LEFT':
return '90deg';
case 'LANDSCAPE-RIGHT':
return '-90deg';
default:
return '0deg';
;
const [orientation, setOrientation] = useState(
// set orientation to the initial device orientation
Orientation.getInitialOrientation(),
);
const [rotate, setRotate] = useState(
// set rotation to the initial rotation value (xxdeg)
getRotation(Orientation.getInitialOrientation()),
);
useEffect(() =>
// Set up listeners for device orientation changes
Orientation.addDeviceOrientationListener(setOrientation);
return () => Orientation.removeDeviceOrientationListener(setOrientation);
, []);
useEffect(() =>
// when orientation changes, update the rotation
setRotate(getRotation(orientation));
, [orientation]);
// render the view with the current rotation value
return (
<View style=transform: [rotate]>
props.children
</View>
);
;
export default RotateView;
【问题讨论】:
【参考方案1】:我遇到了同样的问题,并通过使用 react-native-reanimated 中的 Animated.View 解决了它。 (标准 react-native 包中的 Animated.View 也可能有效,但我没有检查)。我不需要使用 Animated 值,我仍然只是使用状态中的实际值,并且它起作用了。
【讨论】:
效果很好!使用 react-native-reanimated 库中的 Animated.View 解决了这个问题,并允许我的视图在每次更新角度时旋转一个角度。我从标准的 react-native 测试了默认的 Animated.View 但这没有用,所以我认为主要的 react-native 库有问题。【参考方案2】:如果你直接从 react native 中使用Animated.Value
+ Animated.View
,你会没事的。
有同样的问题并使用 Animated.Value
类字段解决了它(在你的情况下,我猜你会使用 useState
来解决这个问题,因为功能性 + useEffect
在更改时设置 Animated.Value
的值props.rotation
),然后将其作为transform = [ rotate: animatedRotationValue ]
传递给Animated.View
这是作为 sn-p 的类组件形式:
interface Props
rotation: number;
class SomethingThatNeedsRotation extends React.PureComponent<Props>
rotation = new Animated.Value(0);
rotationValue = this.rotation.interpolate(
inputRange: [0, 2 * Math.PI],
outputRange: ['0deg', '360deg'],
);
render()
this.rotation.setValue(this.props.rotation);
const transform = [ rotate: this.rotationValue ];
return (
<Animated.View style= transform />
);
请注意,在我的示例中,我也有插值,因为我的输入是弧度,我希望它是度数。
【讨论】:
【参考方案3】:这是我完成的处理旋转的组件。当应用程序锁定为纵向时,它将根据设备方向旋转其子项。我确信这可以清理一些,但它适用于我的目的。
import React, useState, useEffect, useRef from 'react';
import Animated, Easing, View, StyleSheet from 'react-native';
import Orientation from '../utility/constants';
import OrientationManager from '../utility/orientation';
const OrientedView = (props) =>
const getRotation = useRef((newOrientation) =>
switch (newOrientation)
case Orientation.LANDSCAPE_LEFT:
return 90;
case Orientation.LANDSCAPE_RIGHT:
return -90;
default:
return 0;
);
const duration = 100, style = props;
const initialized = useRef(false);
const [orientation, setOrientation] = useState();
const [rotate, setRotate] = useState();
const [containerStyle, setContainerStyle] = useState(styles.containerStyle);
// Animation kept as a ref
const rotationAnim = useRef();
// listen for orientation changes and update state
useEffect(() =>
OrientationManager.getDeviceOrientation((initialOrientation) =>
const initialRotation = getRotation.current(initialOrientation);
// default the rotation based on initial orientation
setRotate(initialRotation);
rotationAnim.current = new Animated.Value(initialRotation);
setContainerStyle([
styles.containerStyle,
transform: [rotate: `$initialRotationdeg`],
,
]);
initialized.current = true;
// set orientation and trigger the first render
setOrientation(initialOrientation);
);
OrientationManager.addDeviceOrientationListener(setOrientation);
return () =>
OrientationManager.removeDeviceOrientationListener(setOrientation);
, []);
useEffect(() =>
if (initialized.current === true)
const rotation = getRotation.current(orientation);
setRotate(
rotationAnim.current.interpolate(
inputRange: [-90, 0, 90],
outputRange: ['-90deg', '0deg', '90deg'],
),
);
Animated.timing(rotationAnim.current,
toValue: rotation,
duration: duration,
easing: Easing.ease,
useNativeDriver: true,
).start();
, [duration, orientation]);
// FIXME: This is causing unnessary animation outside of the oriented view. Disabling removes the scale animation.
// useEffect(() =>
// applyLayoutAnimation.current();
// , [orientation]);
useEffect(() =>
if (initialized.current === true)
setContainerStyle([
styles.containerStyle,
transform: [rotate],
,
]);
, [rotate]);
if (initialized.current === false)
return <View style=[containerStyle, style] />;
return (
<Animated.View style=[containerStyle, style]>
props.children
</Animated.View>
);
;
const styles = StyleSheet.create(
containerStyle: flex: 0, justifyContent: 'center', alignItems: 'center',
);
export default OrientedView;
【讨论】:
【参考方案4】:这是一个错误,因为当rotate
的值更新时,旋转应该会改变。一种解决方法是将 View 的 key
属性也设置为旋转值。
例如:
return (
<View
key=rotate // <~~~ fix!
style=transform: [rotate]
>
props.children
</View>
)
我找到了这个解决方案here。
【讨论】:
以上是关于如何使用状态动态更改 React Native 转换?的主要内容,如果未能解决你的问题,请参考以下文章
尝试更改状态栏样式在 ios 中给了我错误 - React native