Location>code7788 >text

How to develop an RN-compatible animation library in duxapp

Popularity:373 ℃/2024-12-10 17:02:53

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 acreateAnimationThere's still a way out of this.

realization

To implement RN, we need two steps

  • 1. Write a class to simulatecreateAnimation 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