这篇“怎么使用react native reanimated实现动画”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“怎么使用react native reanimated实现动画”文章吧。
动画拆分
首先,从最外层来看,动画有一个抖动效果:先向左,再向右。可以利用 rotate 旋转属性来实现。
其次,中间的文字部分有一个缩放动画,可以通过 scale 实现。
最后,当文字最小化时,会改变内容,这个需要配合 JS 来实现。
实现抖动
首先通过 useSharedValue
定义一个共享值 rotation
,共享值和 useRef
类似,区别是共享值有一个 value
属性而不是 current
。
我们使用共享值改变的样式通过 useAnimatedStyle
包装一下,再赋值给 Animated.View
,这和使用普通的 React Native 样式有点区别:
import Animated from 'react-native-reanimated';const rotation = useSharedValue(0);const shakeStyle = useAnimatedStyle(() => { return { transform: [ { rotateZ: `${rotation.value}deg`, }, ], }; }, []);<Animated.View style={[styles.btn, shakeStyle]}>...</Animated.View>
定义动画
每个动画可以使用 withTiming
更新共享值,并设置动画的具体参数。它会启动基于时间的动画曲线,如执行时间 duration,缓动函数 easing 等。
抖动的过程有三个步骤:向左旋转,向右旋转,保持水平。我们使用 withSequence
来编排动画的顺序。
最后,我们使用 withRepeat
让以上三个步骤无限循环。它接受三个参数:
第一参数是动画函数;
第二个参数是执行的次数,-1 表示无限次;
第三个参数表示动画是否反向执行。
注意,在恢复水平后,按钮仍保持一段时间的静止,我们可以用到 withDelay
来延迟执行下一个动作。
const SCOPE = 2;useEffect(() => { const turnL = withDelay(1400, withTiming(-SCOPE, { duration: 100, easing: Easing.linear })); // 向左 const turnR = withTiming(SCOPE, { duration: 100, easing: Easing.linear }); // 向右 const holden = withTiming(0, { duration: 100, easing: Easing.linear }); // 水平const rotateAnimations = withSequence(turnL, turnR, holden); // 编排动画顺序 rotation.value = withRepeat(rotateAnimations, -1); // 重复执行动画 return () => { cancelAnimation(rotation); };}, []);
实现缩放动画
实现缩放动画的思路与上面基本相似。这里需要注意的是,需要根据实际需求,调整动画之间的节奏关系。比如缩放开始,抖动开始;缩放结束,抖动也就结束。
const scaleSize = useSharedValue(0.2);const scaleStyle = useAnimatedStyle(() => {return { transform: [ { scale: scaleSize.value, }, ],};}, []);useEffect(() => {...const zoomOut = withDelay(1600, withTiming(0.2, { duration: 100, easing: Easing.linear }));const restoreSize = withTiming(1, { duration: 100, easing: Easing.linear });const scaleAnimations = withSequence(restoreSize, zoomOut);scaleSize.value = withRepeat(scaleAnimations, -1);return () => {...cancelAnimation(scaleSize);};}, [])<Animated.View style={[styles.textWrapper, scaleStyle]}>...<Animated.View>
改变内容
当我们依赖共享值的变化,需要进一步操作时,可以使用 useAnimatedReaction
,它第一参数中定义依赖的值,第二个参数接受第一个参数的返回值,并进行自定义的操作。
注意,共享值变化不会触发 JS 线程中的组件更新,改变文案的状态需要用到 useState
,因为文案改变是在 JS 线程中处理的,可以通过 runOnJS
可以让函数在 JS 线程中执行。
import { useAnimatedReaction, runOnJS } from 'react-native-reanimation'; ...const [status, setStatus] = useState(true);const scaleSize = useSharedValue(0.2);const toggle = useCallback(() => { setStatus((s) => !s);}, []);useAnimatedReaction( () => { return scaleSize.value; }, (next) => { if (next <= 0.2) { runOnJS(toggle)(); } });...<Text>{status ? '参与话题' : '赚点赞次数'}</Text>
Reanimated 原理浅析
在开发过程中,我们的动画代码和状态代码都是用 JavaScript 写在同一个文件中的,你可能会认为你写的动画部分的 JavaScript 和状态部分的 JavaScript 都是运行在同一个线程中的, 但其实并不是这样的。
React Native 有两个常用的线程:一个是 React Native 的 JavaScript 线程,另一个是 UI 主线程。
一方面,JavaScript 线程和 UI 主线程是异步通信的,这也意味着,如果是由 JavaScript 线程发起动画的执行,UI 线程并不能同步地收到该命令并且立刻执行。
另一方面,JavaScript 线程处理的事件很多,包括所有的业务逻辑、React Diff、事件响应等,容易抢占动画的执行资源。
Reanimated 是如何优化?答案就是:把动画代码放到 UI 主线程来执行性能更好、不易卡顿。
它把动画相关的 JavaScript 函数及其上下文传给了 UI 主线程。由于 UI 主线程没有能够运行 JavaScript 的环境,于是 Reanimated 又创建了一个 JavaScript 虚拟机来运行传过来的 JavaScript 函数。
在 JavaScript 线程中包括了三个动画相关的函数或值, useSharedValue
、 useAnimatedStyle
和 useAnimatedGestureHandler
。
这三部分的代码会在其底层,将相关的回调函数标记为worklet
,被标记的worklet
函数或值会被放在一个由 Reanimated 创建的 JavaScript 虚拟机中执行。而这个由 Reanimated 创建的 JavaScript 虚拟机,会在 UI 线程中执行传过来的worklet
函数,并且执行的函数还可以同步地操作 UI。
Reanimated 动画性能好的原因就在于:React Native 的 JavaScript 线程是性能瓶颈点,而在真正执行动画时,已经把所有与动画相关 JavaScript 函数都放到了 UI 线程中独立的 JavaScript 虚拟机中了,并不会和 JavaScript 线程抢占硬件资源。
以上就是关于“怎么使用react native reanimated实现动画”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注编程网行业资讯频道。