react native reanimated实现动画示例详解

目录
  • 背景
  • 动画拆分
  • 实现抖动
    • 定义动画
  • 实现缩放动画
  • 改变内容
  • Reanimated 原理浅析
  • 总结

背景

在一次 App 迭代中,UI 想要给按钮添加一个动画效果,在对接的过程中,UI 表示直接用 .gif 就好,因为感觉开发出来的效果应该不会很好。

听到这里,一个技术人的自尊心仿佛被踩在了地上,我当即表示想用 react-native-reanimated(下文简称 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 线程中包括了三个动画相关的函数或值, useSharedValueuseAnimatedStyleuseAnimatedGestureHandler

这三部分的代码会在其底层,将相关的回调函数标记为worklet ,被标记的worklet函数或值会被放在一个由 Reanimated 创建的 JavaScript 虚拟机中执行。而这个由 Reanimated 创建的 JavaScript 虚拟机,会在 UI 线程中执行传过来的worklet 函数,并且执行的函数还可以同步地操作 UI。

Reanimated 动画性能好的原因就在于:React Native 的 JavaScript 线程是性能瓶颈点,而在真正执行动画时,已经把所有与动画相关 JavaScript 函数都放到了 UI 线程中独立的 JavaScript 虚拟机中了,并不会和 JavaScript 线程抢占硬件资源。

总结

Reanimated 处理动画的方法非常巧妙,并且性能极佳,是目前 React Native 社区中主流的动画处理方案,很多开源库都在使用。虽然官方文档有些缺陷,比如 API 没有 demo 不够直观、目前只有英文文档,对英文差的同学不够友好。

未来我们将深入学习和使用 Reanimated,来提升用户体验,实现媲美原生的交互效果。

以上就是react native reanimated实现动画示例详解的详细内容,更多关于react native reanimated 动画的资料请关注我们其它相关文章!

(0)

相关推荐

  • ReactNative 状态管理redux使用详解

    目录 正文 安装和配置开发环境 定义数据结构 然后创建行为处理函数 todoReducer 创建 UI 组件: 正文 有同学反馈开发 ReactNative 应用时状态管理不是很明白,接下来几篇文章我们来对比下 React 及 ReactNative 状态管理常用的几种框架的使用和优缺点. 首先来看下 redux 怎么使用. 以下是使用 React 和 Redux 创建 todo list 的一般过程,完整代码见文章末尾: 安装和配置开发环境 安装 Node.js 和 create-react-

  • 一文详解ReactNative状态管理rematch使用

    目录 引言 React 和 rematch 创建Todo List App 创建一个 models.ts 文件 创建一个 todo.ts 文件 使用 rematch 的 init 函数创建 store RematchTodoApp 要创建UI 组件 总结 引言 有同学反馈开发 ReactNative 应用时状态管理不是很明白,接下来几篇文章我们来对比下 React 及 ReactNative 状态管理常用的几种框架的使用和优缺点. 上一篇文章介绍了 redux 的升级版 redux-toolki

  • 一文详解ReactNative状态管理redux-toolkit使用

    目录 正文 React 和 Redux-Toolkit 创建 Todo List App 新建一个React应用 创建一个 todoSlice.ts 文件 创建 store 上层组件通过 Provider 分发给组件树 通过useSelector 和 useDispatch 获取数据和分发行为 正文 有同学反馈开发 ReactNative 应用时状态管理不是很明白,接下来几篇文章我们来对比下 React 及 ReactNative 状态管理常用的几种框架的使用和优缺点. 上一篇文章介绍了 red

  • ReactNative错误采集原理在Android中实现详解

    目录 引言 1 JS错误 1.1 Error 1.2 常见的错误 SyntaxError:语法错误 ReferenceError:引用错误 TypeError:类型错误 RangeError:边界错误 URIError:URI错误 1.3 自定义错误 2 RN错误处理 2.1 JS部分 2.1.1 MessageQueue 2.1.2 ErrorUtils 2.1.3 ExceptionsManager 2.2 Native部分 2.2.1 ExceptionsManagerModule 2.2

  • react后台系统最佳实践示例详解

    目录 一.中后台系统的技术栈选型 1. 要做什么 2. 要求 3. 技术栈怎么选 二.hooks时代状态管理库的选型 context redux recoil zustand MobX 三.hooks的使用问题与解决方案 总结 一.中后台系统的技术栈选型 本文主要讲三块内容:中后台系统的技术栈选型.hooks时代状态管理库的选型以及hooks的使用问题与解决方案. 1. 要做什么 我们的目标是搭建一个适用于公司内部中后台系统的前端项目最佳实践. 2. 要求 由于业务需求比较多,一名开发人员需要负

  • react redux及redux持久化示例详解

    目录 一.react-redux 二.redux持久化 一.react-redux react-redux依赖于redux工作. 运行安装命令:npm i react-redux: 使用: 将Provider套在入口组件处,并且将自己的store传进去: import FilmRouter from './FilmRouter/index' import {Provider} from 'react-redux' import store from './FilmRouter/views/red

  • react编写可编辑标题示例详解

    目录 需求 初始需求 方案设计 方案一 span + contentEditable 思路 代码如下 在这个方案中遇到的问题 存在的问题 方案二 直接用input处理展示和编辑 踩到的坑 需求 因为自己换工作到了新公司,上周入职,以前没有使用过react框架,虽然前面有学习过react,但是并没有实践经验 这个需求最终的效果是和石墨标题修改实现一样的效果 初始需求 文案支持可编辑 用户点击位置即光标定位处 超过50字读的时候,超出部分进行截断 当用户把所有内容删除时,失去焦点时文案设置为 “无文

  • React特征Form 单向数据流示例详解

    目录 引言 集中状态管理 双向数据流 那为何不选择双向数据流 小结 引言 今天将之前的内容做个系统整理,结合 React Form 案例, 来了解下为何React推荐单向数据流,如果采用双向管理,可能的问题 (关于React Form案例,可参考相关文章 - 学习React的特征(二) - React Form 集中状态管理 首先来看之前的React Form, 若采用单向数据流 import * as React from 'react'; const Useremail = props =>

  • React less 实现纵横柱状图示例详解

    目录 引言 主要设计来源 display 布局 display 布局 动态位置使用绝对定位 style JS 引言 之前的文章,咱们介绍过横向和竖向,具体的内容,请看 React + CSS 绘制横向柱状图 React + CSS 绘制竖状柱状图 这次,结合起来,横向和竖向,一起画 主要设计来源 三个部分 <ul className="vertical"> <li className="vertical_li">100</li>

  • React Native系列之Recyclerlistview使用详解

    目录 recyclerlistview的介绍与使用 1.安装 2.概述和功能 3. RecyclerListView的使用 1.dataProvider 2.LayoutProvider 3.rowRenderer 4.onEndReached 5.onEndReachedThreshold 6.extendedState 7.scrollViewProps RecyclerListView所有属性 recyclerlistview的介绍与使用 1.安装 npm install --save r

  • React Native 脚手架的基本使用详解

    构建项目 在相应的路径下执行命令行:react-native init 项目名 (名称不可使用连接符等特殊字符,命名可以参考APP应用名称 比如 FaceBook) react-native --v //查看版本 react-native init demo --version 0.48.0//安装指定的版本 react-native init demo --verbose --version 0.48.0 //verbose是初始化的时候显示安装详情的,安装什么模块以及进度 npm view

  • C# Winform中如何绘制动画示例详解

    前言 这里介绍一个.net自身携带的类ImageAnimator,这个类类似于控制动画的时间轴,使用ImageAnimator.CanAnimate可以判断一个图片是否为动画,调用ImageAnimator.Animate可以开始播放动画,即每经过一帧的时间触发一次OnFrameChanged委托,我们只要在该委托中将Image的活动帧选至下一帧再迫使界面重绘就可以实现动画效果了. 为了方便以后的使用,我将这些代码整合到了一起,形成一个AnimateImage类,该类提供了CanAnimate.

  • React特征学习Form数据管理示例详解

    目录 Form数据管理 重置Form状态 form验证 小结 Form数据管理 有时会遇到多个位置需要用户输入的情况,若每个状态都配置state或handler会很繁琐,可以尝试下面的方法 import * as React from 'react'; const LoginForm = () => { // 将多个状态合并为对象 const [state, setState] = React.useState({ email: '', password: '', }); // 通过单个hand

  • React Native 中实现确认码组件示例详解

    目录 正文 实现原理 开源方案 正文 确认码控件也是一个较为常见的组件了,乍一看,貌似较难实现,但实则主要是障眼法. 实现原理 上图 CodeInput 组件的 UI 结构如下: <View style={[styles.container]}> <TextInput autoFocus={true} /> <View style={[styles.cover, StyleSheet.absoluteFillObject]} pointerEvents="none&

随机推荐