Taro 一直以来都没有一个能兼容 RN 的动画方案,duxapp 中扩展了 createAnimation 方法,让这个方法兼容了 RN 端,下面让我们来看看实现思路

createAnimation方法

这个方法是用来创建一个动画实例的,使用方法像下面这样,每次 step 创建一组动画,每组动画同时执行,执行完一组继续执行下一组,直到所有的动画执行完

const an = createAnimation()
.translate(50, 50).rotate(180).step()
.translate(0, 0).rotate(0).step()
.export()

将创建的结果设置给 View 的 animation 属性,这个动画就能被执行了

在 Taro 里面这个方法目前兼容小程序 和 H5 端,我们要实现的就是让他兼容 RN,这样后面要给我们的组件加动画就更加简单了,不用在要加动画的组件 H5 写一套,RN 再写一套

RN的动画方案

RN 里面的动画和 createAnimation 这个方式可以说是天差地别,下面来看这个实现淡入动画的代码,这是一个官方示例

import React, { useRef, useEffect } from 'react';
import { Animated, Text, View } from 'react-native'; const FadeInView = (props) => {
const fadeAnim = useRef(new Animated.Value(0)).current // 透明度初始值设为0 React.useEffect(() => {
Animated.timing( // 随时间变化而执行动画
fadeAnim, // 动画中的变量值
{
toValue: 1, // 透明度最终变为1,即完全不透明
duration: 10000, // 让动画持续一段时间
}
).start(); // 开始执行动画
}, [fadeAnim]) return (
<Animated.View // 使用专门的可动画化的View组件
style={{
...props.style,
opacity: fadeAnim, // 将透明度绑定到动画变量值
}}
>
{props.children}
</Animated.View>
);
}

好在 RN 的动画库本身是很强大的,我们要做的是在 RN 端模拟实现一个 createAnimation, 还是有办法解决的

实现

要实现 RN,我们需要两步

  • 1、编写一个类,用来模拟 createAnimation 方法,通过这个类创建一些动画数据
  • 2、将这个类创建的数据传递给一个自定义组件,这个组件里面将这些数据解析成动画,并执行这些动画

创建 Animation 类

这个类就比较简单,模拟 createAnimation 每一个方法即可,并在 export() 之后生成一个数据并返回,因代码过长,下面是部分代码展示

export const create = option => new Animation(option)

class Animation {

  constructor(option = {}) {
if (!option.duration) {
option.duration = 400
}
if (!option.timingFunction) {
option.timingFunction = 'linear'
}
if (!option.delay) {
option.delay = 0
}
if (!option.transformOrigin) {
option.transformOrigin = '50% 50% 0'
}
this.defaultOption = option
} result = [] current = {} export() {
const res = this.result
this.result = []
return res
} step(option) {
if (Object.keys(this.current).length) {
this.result.push({
option: {
...this.defaultOption,
...option
},
action: this.current
})
this.current = {}
}
return this
} set(name, value) {
this.current[name] = value
return this
} translate(x, y) {
this.translateX(x)
return this.translateY(y)
} translate3D(x, y, z) {
this.translateX(x)
this.translateY(y)
return this.translateZ(z)
} translateX(val) {
return this.set('translateX', val)
} translateY(val) {
return this.set('translateY', val)
} translateZ(val) {
return this.set('translateZ', val)
}
}

创建组件实现动画

这个地方相对会复杂一些,其中的难点有几个

  • 在小程序上的动画,是会根据 css 的默认值去执行变化的,但是 RN 上的默认值需要在动画中设置,因此,需要获取这个默认值
  • 将动画拆分成适合 RN 端动画组件的 style 属性

此处代码过长,可以前往 github 查看,或者使用 duxapp 创建项目之后就能看到

使用

动画库写好之后我们就能给我们原有的一些组件进行改造了,例如 PullView,在这之前,这个组件是针对 RN 端和其他端写了两套代码的,现在只需要一套代码就实现了,下面的示例代码展示了这个组件动画实现方式

import { isValidElement, cloneElement, forwardRef, useState, useEffect, useRef, useImperativeHandle, useCallback } from 'react'
import { View } from '@tarojs/components'
import { asyncTimeOut, nextTick, px, pxNum, transformStyle, useBackHandler } from '@/duxapp/utils'
import { Absolute } from '../Absolute'
import { Animated } from '../Animated'
import './index.scss' export const PullView = forwardRef(({
side = 'bottom',
style,
overlayOpacity = 0.5,
children,
masking = true,
group,
onClose,
modal,
mask = modal,
duration = 200
}, ref) => { const [mainAn, setMainAn] = useState(Animated.defaultState) const [maskAn, setMaskAn] = useState(Animated.defaultState) const ans = useRef() const refs = useRef({})
refs.current.onClose = onClose
refs.current.overlayOpacity = overlayOpacity const translate = siteTranslates[side] const close = useCallback(async () => {
let an = ans.current.main
if (side === 'center' && process.env.TARO_ENV !== 'rn') {
an = an.translate('-50%', '-50%')
}
setMainAn(an[translate.key](pxNum(translate.value)).opacity(0).step(
process.env.TARO_ENV !== 'rn' ? {
transformOrigin: '25% 25% 0'
} : {}
).export())
setMaskAn(ans.current.mask.opacity(0).step().export())
await asyncTimeOut(duration)
refs.current.onClose?.()
}, [duration, side, translate.key, translate.value]) useBackHandler(close, !mask) useImperativeHandle(ref, () => {
return {
close
}
}) useEffect(() => {
nextTick(() => {
if (!ans.current) {
ans.current = {
main: Animated.create({
duration,
timingFunction: 'ease-in-out'
}),
mask: Animated.create({
duration,
timingFunction: 'ease-in-out'
})
}
}
if (side === 'center') {
let an = ans.current.main.scale(1).opacity(1)
if (process.env.TARO_ENV !== 'rn') {
an = an.translateX('-50%').translateY('-50%')
}
setMainAn(an.step().export())
} else {
setMainAn(ans.current.main.translateX(0).translateY(0).opacity(1).step().export())
}
setMaskAn(ans.current.mask.opacity(refs.current.overlayOpacity).step().export())
})
}, [duration, side]) return <Absolute group={group}>
{masking && <Animated.View
animation={maskAn}
className='PullView'
>
<View className='PullView__other'
onClick={() => {
if (mask) {
return
}
close()
}}
></View>
</Animated.View>}
<Animated.View
animation={mainAn}
className={`PullView__main PullView__main--${side}`}
style={{
...style,
transform: transformStyle(side === 'center' ? {
translateX: '-50%',
translateY: '-50%',
scaleX: 0.4,
scaleY: 0.4
} : {
[translate.key]: px(translate.value)
})
}}
>
{
isValidElement(children) ?
cloneElement(children, {
style: {
...children.props.style,
...(side === 'center'
? {}
: side === 'bottom' || side === 'top'
? { width: '100%' }
: { height: '100%' })
}
}) :
children
}
</Animated.View>
</Absolute>
}) const siteTranslates = {
top: { key: 'translateY', value: -200 },
bottom: { key: 'translateY', value: 200 },
left: { key: 'translateX', value: -200 },
right: { key: 'translateX', value: 200 },
center: { key: 'scale', value: 0.4 }
}

最后

当然这个动画也不是完美的,只是实现了一个基础的动画,甚至使用的时候还有诸多的限制,你可以点击下面的动画文档查看详细的使用方法以及限制

Animated 动画文档

开发文档

GitHub

如何在 duxapp 中开发一个兼容 RN 的动画库的更多相关文章

  1. 如何在JAVA中实现一个固定最大size的hashMap

    如何在JAVA中实现一个固定最大size的hashMap 利用LinkedHashMap的removeEldestEntry方法,重载此方法使得这个map可以增长到最大size,之后每插入一条新的记录 ...

  2. 如何在idea中引入一个新maven项目

    如何在idea中引入一个新的maven项目,请参见如下操作:      

  3. 如何在html中把一个图片或者表格覆盖在一张已有图片上的任意位置

    如何在html中把一个图片或者表格覆盖在一张已有图片上的任意位置   <div style="position:relative;"> <img src=&quo ...

  4. (转)如何在Linux中统计一个进程的线程数

    如何在Linux中统计一个进程的线程数 原文:http://os.51cto.com/art/201509/491728.htm 我正在运行一个程序,它在运行时会派生出多个线程.我想知道程序在运行时会 ...

  5. Kubernetes入门(四)——如何在Kubernetes中部署一个可对外服务的Tensorflow机器学习模型

    机器学习模型常用Docker部署,而如何对Docker部署的模型进行管理呢?工业界的解决方案是使用Kubernetes来管理.编排容器.Kubernetes的理论知识不是本文讨论的重点,这里不再赘述, ...

  6. 使用 js 实现一个简易版的动画库

    使用 js 实现一个简易版的动画库 具有挑战性的前端面试题 animation css refs https://www.infoq.cn/article/0NUjpxGrqRX6Ss01BLLE x ...

  7. [C++/Python] 如何在C++中使用一个Python类? (Use Python-defined class in C++)

    最近在做基于OpenCV的车牌识别, 其中需要用到深度学习的一些代码(Python), 所以一开始的时候开发语言选择了Python(祸患之源). 固然现在Python的速度不算太慢, 但你一定要用Py ...

  8. Andriod项目开发实战(1)——如何在Eclipse中的一个包下建新包

    最开始是想将各个类分门别类地存放在不同的包中,所以想在项目源码包中新建几个不同功能的包eg:utils.model.receiver等,最后的结果应该是下图左边这样的:   很明显建立项目后的架构是上 ...

  9. 如何在HoloLens中创建一个2D的Hello World程序

    注:本文提及到的代码示例下载地址 > How to build an "Hello World" 2D app in HololLens. HoloLens 是微软的一款MR ...

  10. IE 中开发,兼容与性能测试工具汇总

    前言 对于开发者来说, IE的兼容性是最让人头疼的. 因为是微软的产品, 且绑定在操作系统上, 所以IE的占用率还是相当大, 对于开发者来说, 这部分的兼容的考虑就不可避免了. 对于IE 的各版本来说 ...

随机推荐

  1. MyBatisPlus——标准数据层开发

    标准数据层开发 标准数据层CRUD功能 lombok 一个java类库,提供了一组注解,简化POJO实体类开发    常用注解@Data    为当前实体类在编译期设置对应的get/set方法,无参/ ...

  2. Google Aviator Evaluator 使用入门(一)

    Aviator Evaluator 使用入门(一) https://www.yuque.com/boyan-avfmj/aviatorscript/tvahat 一.什么是规则引擎 定义:规则引擎是一 ...

  3. 《你必须掌握的Entity Framework 6.x与Core 2.0》代码下载

      https://item.jd.com/12376014.html<你必须掌握的Entity Framework 6.x与Core 2.0>代码下载,共享代码: https://git ...

  4. 数据库运维实操优质文章分享(含Oracle、MySQL等) | 2024年7月刊

    本文为大家整理了墨天轮数据社区2024年7月发布的优质技术文章/文档,主题涵盖Oracle.MySQL.PostgreSQL等主流数据库系统以及国产数据库的深度教程和实用指南.从基础的安装配置到复杂的 ...

  5. 2022年最新数据库调查报告:超八成DBA月薪过万,你拖后腿了吗?

    数据库管理员属于IT行业高薪职业的一种,近几年关于数据库管理员的薪资统计文章也层出不穷,那么当前,DBA们的薪资究竟到达了怎样的水平呢? 墨天轮数据社区发布最新<2022年墨天轮数据库大调查报告 ...

  6. 2022年最新数据库经典面试题及答案汇总(含PostgreSQL、Oracle、MySQL)

    随着企业数字化需求的增加,数据库行业发展日益壮大,企业对DBA岗位的需求也处于逐步增加中.我们梳理了墨天轮平台上2022年最新的一批数据库经典面试题,主要包含PostgreSQL.MySQL和Orac ...

  7. 立即报名 | 云原生 + AI Meetup 成都站 11.4 正式开启

    2023 年,KubeSphere 社区已经在深圳.杭州.上海三个城市各组织了一场线下 Meetup.第四站,我们将走进天府成都. 11 月 4 日,云原生 + AI Meetup 成都站将正式开启! ...

  8. Nuxt.js 应用中的 builder:watch 事件钩子详解

    title: Nuxt.js 应用中的 builder:watch 事件钩子详解 date: 2024/10/24 updated: 2024/10/24 author: cmdragon excer ...

  9. js常用函数 _01 关于 model()、substr() 和 正则表达式的使用

    js常用函数 _01 关于 model().substr() 和 正则表达式的使用 1.model() Bootstrap 模态框(Modal)插件 模态框(Modal)是覆盖在父窗体上的子窗体.通常 ...

  10. Next.js 从零入门到实战 3:2024最新完整教程 | 包含SSR、API路由和性能优化

    CSS样式学习 上一篇文章讲到如何创建一个脚手架程序,目前我们已经有了一个nextjs的基本框架,因此我们可以在这个基础上进行改造.打开我们项目中的page.tsx页面,这里是程序的主页面,也就是我们 ...