源码-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,然后在指定时间内将其移除状态列表
*/