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. ASP.NET Core – Razor Class Library (RCL)

    前言 Razor Class Library 的用途是封装 Razor views, pages, controllers, page models, Razor components, View c ...

  2. MybatisPlus——入门案例

    MyBatisPlus MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发.提高效率 开发方式 基于MyBatis使用MyBatisPlus 基于Sprin ...

  3. Python — 循环语句

    while循环语句 语法: 嵌套使用: for循环语句:(for循环外部不允许访问临时变量) 语法: 使用: 待处理的数据集: range语句: 嵌套for循环: continue: break: 随 ...

  4. 系统编程-操作系统概论PART1

    Part1. 计算机的基本组成原理 Part2. 计算机执行原理顶层视图 Part3. 指令 指令周期 取指令和执行指令 指令格式 前面1字节是操作码,代码指令的功能,例如加法功能. 后面3字节用于寻 ...

  5. 使用duxapp开发 React Native App 事半功倍

    Taro的React Native端开发提供了两种开发方式,一种是将壳和代码分离,一种是将壳和代码合并在一起开发 壳是用来打包调试版或者发版安装包使用的 代码是运行在壳上的js代码 Taro壳子的代码 ...

  6. 【赵渝强老师】Redis案例分析:用setbit统计活跃用户

    一.需求背景 首先,我们来看一下需求:网站统计用户登录的次数,具体如下: 网站有1亿个用户,有经常登录的,也有不经常登录的 如何来记录用户的登录信息 如何查询活跃用户:比如:一周内,登录3次的 二.使 ...

  7. 《Vue.js 设计与实现》读书笔记 - 第15章、编译器核心技术概览

    第15章.编译器核心技术概览 15.1 模板 DSL 的编译器 完整的编译包括 [源代码] -->词法分析-->语法分析-->语义分析(编译前端) -->中间代码生成--> ...

  8. .NET 代码混淆工具-JIEJIE.NET

    前言 JIEJIE.NET是一款强大的开源.NET程序集混淆工具.它利用深度加密技术和多样化的混淆策略,有效地保护了.NET软件的版权和源代码安全,防止未经授权的访问和篡改. 项目介绍 JIEJIE. ...

  9. 2021年11月墨天轮国产数据库排行榜:openGauss闯入前三,Kingbase流行度与日俱增,TDengine厚积薄发

    2021年11月的国产数据库流行度排行榜已在墨天轮发布,本月共有163家数据库参与排名.就前15名的总体情况来看,除openGauss反超OceanBase闯入前三,TDengine厚积薄发来到第15 ...

  10. web端ant-design-vue Modal.info组件自定义icon和title使用小节

    web端ant-design-vue Modal.info组件自定义icon和title整理小节,最近在项目中用到了自定义icon和title的功能,经过测试发现,如果自定义icon title会自动 ...