源码-transition-hook


源码-Transition Hook

​ 包含三个hook,useTransition、useSwitchTransition 和useListTransition,都是通过外界传入的状态触发返回更新时机(from、enter 和 leave)。这三者有什么区别,useSwitchTransitione 在状态变化的时候可,保留了上一阶段的状态,相对于useTransition实现的效果跟丰富, useListTransition可以对一组值实现动画效果

源码地址

定时操作

​ 想要实现一个定时操作,大多数时候借助 setTimeout实现,但是setTimeout会出现丢帧的现象,使用requestAmimationFrame就可以避免丢帧

requestAmimationFrame(callback)
//下次重绘前调用callback,callback会被传入一个存储毫秒级的时间值,该参数与performance.now()的返回值相同,它表示            requestAnimationFrame() 开始去执行回调函数的时刻,如果想要模拟setTimeout的效果,可以这样写

const setTimeoutDiy = (callback, time) => {
  const startTime = performance.now()
  const timer = { id : null}
  const call = () => {
    timer.id = requestAnimationFrame(now => {
      if (now - startTime > time) callback()
      else call()
    })
  }
    call()
  return timer
}

useTransition


/* 
实现思路:
    外部传入state,通过state改变stage状态,stage共分为三个状态from、enter和leave,如果 stage === true,结点可见,且状态为from,在下一次重绘前状态变为enter。如果stage === false,说明结点状态要变为leave,且需要提示可以卸载结点了。
*/    

import { useState, useRef, useEffect } from 'react'

interface Props {
  state: boolean
  timeout: number
}

interface Timer {
  id?: number
}

const setAnimationFrameTimeout = (callBack: () => void,timeout: number = 0) => {
  const startTime = performance.now()
  let timer: Timer = {}
  const call = () => {
    requestAnimationFrame(now => {
      if (now - startTime > timeout) callBack
      else call()
    })
  }
  call()
  return timer
}

const clearAnimationFrame = (timer: Timer) => {
  timer.id && cancelAnimationFrame(timer.id)
}



type Stage = 'from' | 'enter' | 'leave'



const useTransition = (props: Props) => {
  const { state, timeout } = props
  const [stage, setStage] = useState<Stage>()
  const timerRef = useRef<Timer>({})
  const [shouldMount, setShouldMount] = useState<boolean>(false)
  useEffect(() => {
    if (state) {
      setStage('from')
      setShouldMount(false)
      setAnimationFrameTimeout(() => {
        setStage('enter')
      })
    } else {
      setStage('leave')
      clearAnimationFrame(timerRef.current)
      timerRef.current = timerRef.current = setAnimationFrameTimeout(() => {
        setShouldMount(true)
      }, timeout)
    }
  }, [state, timeout])
  return [stage, shouldMount]
}

export default useTransition

useSwitchTransition

/*
实现思路:
    useSwitchTransition 最多存在两个状态(两个对象,对象包含stage字段),当前状态,以及新进入的状态,通过控制这两个状态stage的变化的先后,可以实现先入后出以及先出后入的效果。
    useSwitchTransition维护一个状态列表,里面最多包含两个状态,通过监听(useEffect)外部传入的状态 和 状态列表里面的状态实现stage转换。
    实现默认效果(新加入的状态出现的同时,上一个状态消失)
        这种实现只需要维护一个状态
         const [latestState] = stateList
         const inputState
         if inputState === latestState return
         stateList = [{inputState, stage: from}, {...latestState, stage: leave}]
         下一次重绘:
                 stateList = [{inputState, stage: enter}, {...latestState, stage: leave}]
         指定时间卸载旧的状态:
                 stateList = [{inputState, stage: enter}]

    实现先入后出 or 先出后入的效果
    const [secondState, latestState] = stateList
    const inputState
    if inputState !== latestState:
      加入状态列表
      stateList = [latestState, {inputState,stage:'from'}]

    if inputState === latestState
    & !secondState:
      需要更新上一次加入的状态 stage = enter

    if inputState === latestState
    & secondState:
      需要更新上上次加入的状态 stage === leave,并且在指定时间后移除状态列表
*/

useListTransition

/*
实现思路:
    useListTransition的实现其实很简单,自身维护一个状态数组,跟外部传入的Array对比,如果没有变化,就将stage === from 的状态改变成 stage = enter,如果有变化的话,要么是增加,要么是删除,增加的话就加入状态数组且stage = from,如果是删除的话,即刻将stage 变成leave,然后在指定时间内将其移除状态列表
*/

文章作者: 木叶勇
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 木叶勇 !
  目录