react-native-reanimated系列(一)
react-native-reanimated系列(二)
Shared Values
作用:携带可动画的数据,提供响应式和驱动式动画。
携带数据
特点一:共享内存,数据可以在线程间安全地读取和修改。
特点二:共享数据必须通过对象的.value
属性来访问和修改。
特点三:从UI线程执行的读写操作都是同步的,在React Native JS线程上进行的更新都是异步的。
- 从React Native JS线程异步更新值
import { useSharedValue } from 'react-native-reanimated';
function SomeComponent() {
const sharedVal = useSharedValue(0);
return (
<Button
onPress={() => (sharedVal.value = Math.random())}
title="Randomize"
/>
);
}
- 使用worklet同步更新值
import Animated, { useSharedValue, useAnimatedScrollHandler } from 'react-native-reanimated';
function SomeComponent({ children }) {
const scrollOffset = useSharedValue(0);
const scrollHandler = useAnimatedScrollHandler({
onScroll: event => {
scrollOffset.value = event.contentOffset.y;
},
});
return (
<Animated.ScrollView onScroll={scrollHandler}>
{children}
</Animated.ScrollView>
);
}
上面的例子中,srcoll handler就是一个worklet,并在UI线程上运行滚动事件逻辑。在该worklet中进行的更新是同步的。
响应性
对Shared Values的更新可以触发UI线程上相应的代码执行,从而导致启动动画、视图更新等。
当前有两种方法可以创建响应式的worklet,分别是useAnimatedStyle
和useDerivedValue
钩子。
import Animated, { useSharedValue, useAnimatedStyle } from 'react-native-reanimated';
function Box() {
const offset = useSharedValue(0);
const animatedStyles = useAnimatedStyle(() => {
return {
transform: [{ translateX: offset.value * 255 }],
};
});
return (
<>
<Animated.View style={[styles.box, animatedStyles]} />
<Button onPress={() => (offset.value = Math.random())} title="Move" />
</>
);
}
sample01.gif
驱动动画
Reanimated库内置了许多帮助运行和自定义动画的方法。启动动画的方式之一是对Shared Value开始动画过渡。
import { withTiming } from 'react-native-reanimated';
someSharedValue.value = withTiming(50);
在上面的代码中,偏移共享值,而不是立即设置为50,将使用基于时间的动画从当前值转换为50。
import Animated, { withSpring } from 'react-native-reanimated';
function Box() {
const offset = useSharedValue(0);
const animatedStyles = useAnimatedStyle(() => {
return {
transform: [{ translateX: offset.value * 255 }],
};
});
return (
<>
<Animated.View style={[styles.box, animatedStyles]} />
<Button
onPress={() => {
offset.value = withSpring(Math.random());
}}
title="Move"
/>
</>
);
}
在上面的代码中,使用withSpring
更新偏移量,将弹性的从当前值转换为更新值。因此,更新视图的移动动画将会变得平滑。
动画进度
开始Shared Value过渡动画后,.value
属性将与动画进度同步。也就是说,当初始值为0时,使用withTiming(50)
开始动画过渡,默认耗时300ms,期望.value
属性的读取值返回一个从0到50的数字,该数字将对应当前位置作为动画进度。
中断动画
由于Shared Value会保持其动画过渡的状态,因此我们可以使所有的动画完全中断。这意味着即使Shared Value当前正在运行动画,您也可以对其进行更新,而不必担心这会导致意外和突然的动画故障。在这种情况下覆盖该值将导致先前的动画被中断。如果新分配的值是一个数字(或任何静态值),则该新值将立即分配给Shared Value,并且先前运行的动画将被取消。如果新分配的值也是动画,则先前运行的动画将平滑过渡到新动画。诸如速度之类的动画参数也会这样平滑过渡,这在基于弹簧的动画中尤为重要。这样可以实现从一种动画到另一种动画的真正平滑的转换。
sample02.gif
取消动画
在某些情况下,我们希望停止当前正在运行的动画而不启动新动画。在Reanimated中,可以使用cancelAnimation
方法。
import { cancelAnimation } from 'react-native-reanimated'
cancelAnimation(someSharedValue);
从UI线程和React Native的JS线程中取消动画均可。
Shared Values与Animated.Value比较
特性 | Animated Value | Shared Value |
---|---|---|
携带数据类型 | 仅支持数字和字符串 | 任何原始或嵌套的数据结构(例如对象,数组,字符串,数字,布尔值) |
关联视图属性 | 通过直接传递Animated.Value 作为属性 |
不能直接与视图的属性关联,应该使用useAnimatedStyle 或useAnimatedProps
|
更新值 | 使用value.setValue 方法(当使用native driver时,更新就是异步调用) |
直接更新.value 属性(UI线程同步更新,React Native JS线程异步更新) |
读取值 | 通过value.addListener 注册监听来获取所有动画值更新 |
直接读取.value 属性 |
运行动画 | 使用Animated.spring ,Animated.timing (或其他),将Animated Value作为参数传递,并运行.start() 方法以启动动画 |
更新.value 属性,同时使用动画方法(例如withTiming )包装 |
停止动画 | 通过Animated.timing (或其他)返回动画对象的引用,并让它调用stopAnimation() 方法 |
把Shared Value作为参数传递给cancelAnimation 方法 |
插值 | 使用Animated Value的interpolate() 成员方法 |
使用interpolate 方法,如果需要跟踪值,则可以将其与useDerivedValue 一起使用 |
网友评论