Taro hasn't had an RN-compatible animation solution for a long time. duxapp extends the createAnimation method to make it RN-compatible, so let's take a look at the implementation.
createAnimation method
This method is used to create an animation instance, use the method like the following, each step to create a group of animation, each group of animation at the same time, after the execution of a group to continue to execute the next group, until all the animation execution is complete
const an = createAnimation()
.translate(50, 50).rotate(180).step()
.translate(0, 0).rotate(0).step()
.export()
Sets the result of the creation to the View'sanimation
property, this animation can be executed
In Taro, this method is currently compatible with small programs and H5, what we want to achieve is to make him compatible with the RN, so that the back to add animation to our components is much simpler, do not have to add animation to the components of the H5 write a set of RN and then write a set of
RN's animation program
The animation inside the RN is the same as thecreateAnimation
This way can be said to be a world of difference, the following look at this realization of the fade animation code, this is an official example of the
import React, { useRef, useEffect } from 'react';
import { Animated, Text, View } from 'react-native';
const FadeInView = (props) => {
const fadeAnim = useRef(new (0)).current // Transparency is initially set to 0.
(() => {
( // Execute the animation over time
fadeAnim, // the value of the variable in the animation
{
toValue: 1, // the transparency will eventually change to 1, i.e. it will be completely opaque
duration: 10000, // make the animation last for a while
}
).start(); // Start the animation.
}, [fadeAnim])
return (
< // Use the specialized animatable View component
style={{
... ,
opacity: fadeAnim, // Bind transparency to the value of the animation variable.
}}
>.
{}
</>
);
}
The good thing is that the RN's animation library itself is very powerful, and all we have to do is simulate the implementation of acreateAnimation
There's still a way out of this.
realization
To implement RN, we need two steps
- 1. Write a class to simulate
createAnimation
method to create some animation data through this class - 2. Pass the data created by this class to a custom component, which parses the data into animations and executes them.
Creating the Animation Class
This class is simple enough to emulate each of the createAnimation methods and add a new method in theexport()
After that a data is generated and returned, because the code is too long, the following is part of the code show
export const create = option => new Animation(option)
class Animation {
constructor(option = {}) {
if (!) {
= 400
}
if (!) {
= 'linear'
}
if (!) {
= 0
}
if (!) {
= '50% 50% 0'
}
= option
}
result = []
current = {}
export() {
const res =
= []
return res
}
step(option) {
if (().length) {
({
option: {
...,
...option
},
action:
})
= {}
}
return this
}
set(name, value) {
[name] = value
return this
}
translate(x, y) {
(x)
return (y)
}
translate3D(x, y, z) {
(x)
(y)
return (z)
}
translateX(val) {
return ('translateX', val)
}
translateY(val) {
return ('translateY', val)
}
translateZ(val) {
return ('translateZ', val)
}
}
Creating components for animation
This place can be relatively complex, with a couple of difficult points
- Animations on applets perform changes based on the default value of the css, but the default value on RN needs to be set in the animation, so it is necessary to get this default value
- Split the animation into style attributes suitable for RN-side animation components.
The code here is too long, go togithub View it, or see it after creating a project with duxapp
utilization
Animation library after writing a good we can give us some of the original components for transformation, such as PullView, before this, this component is for the RN side and the other end of the two sets of code, now only need a set of code to achieve, the following sample code shows the way this component animation implementation
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 './'
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()
const [maskAn, setMaskAn] = useState()
const ans = useRef()
const refs = useRef({})
= onClose
= overlayOpacity
const translate = siteTranslates[side]
const close = useCallback(async () => {
let an =
if (side === 'center' && .TARO_ENV !== 'rn') {
an = ('-50%', '-50%')
}
setMainAn(an[](pxNum()).opacity(0).step(
.TARO_ENV !== 'rn' ? {
transformOrigin: '25% 25% 0'
} : {}
).export())
setMaskAn((0).step().export())
await asyncTimeOut(duration)
?.()
}, [duration, side, , ])
useBackHandler(close, !mask)
useImperativeHandle(ref, () => {
return {
close
}
})
useEffect(() => {
nextTick(() => {
if (!) {
= {
main: ({
duration,
timingFunction: 'ease-in-out'
}),
mask: ({
duration,
timingFunction: 'ease-in-out'
})
}
}
if (side === 'center') {
let an = (1).opacity(1)
if (.TARO_ENV !== 'rn') {
an = ('-50%').translateY('-50%')
}
setMainAn(().export())
} else {
setMainAn((0).translateY(0).opacity(1).step().export())
}
setMaskAn(().step().export())
})
}, [duration, side])
return <Absolute group={group}>
{masking && <
animation={maskAn}
className='PullView'
>
<View className='PullView__other'
onClick={() => {
if (mask) {
return
}
close()
}}
></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
} : {
[]: px()
})
}}
>
{
isValidElement(children) ?
cloneElement(children, {
style: {
...,
...(side === 'center'
? {}
: side === 'bottom' || side === 'top'
? { width: '100%' }
: { height: '100%' })
}
}) :
children
}
</>
</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 }
}
ultimate
Of course, this animation is not perfect, just a basic animation, and even the use of a number of limitations, you can click on the following animation document to see the detailed use of the method and limitations
Animated Animated documents
development documentation
GitHub