有些组件需要一些简单的淡入淡出css动画效果 通过visible为触发点 主要就是在visible切换true/false的时候做出简单动画 enter类型和appear类型不让一起出现
CSS animation 与 CSS transition 有何区别?
https://www.zhihu.com/question/19749045
Transition 强调过渡,Transition + Transform = 两个关键帧的Animation
Animation 强调流程与控制,Duration + TransformLib + Control = 多个关键帧的Animation
如果只有两个关键帧我会选择Transition + Transform
[
](https://ant.design/components/alert-cn/)
rc-motion做的事情
实际功能例子
https://ant.design/components/alert-cn/
在css中已经预先写好各个动画的类名和动画名
// relative path
components/alert/style/index.less
// transition
.transition {
transition: background 0.3s, height 1.3s, opacity 1.3s;
// transition: all 5s!important;
&.transition-appear,
&.transition-enter {
opacity: 0;
}
...
}
// animation
.animation {
animation-duration: 1.3s;
animation-fill-mode: both;
&.animation-appear,
&.animation-enter {
animation-name: enter;
animation-fill-mode: both;
animation-play-state: paused;
}
...
}
// keyframes
@keyframes enter {
from {
transform: scale(0);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
通过控制status来获取对应钩子,可产生对应的样式或者执行用户自定义操作,控制类名添加和删除
用户各个阶段传入的钩子
// 动画状态
export const STATUS_NONE = 'none' as const;
export const STATUS_APPEAR = 'appear' as const;
export const STATUS_ENTER = 'enter' as const;
export const STATUS_LEAVE = 'leave' as const;
// visible变化 判断属于哪个status
useIsomorphicLayoutEffect(() => {
let nextStatus: MotionStatus;
if (!isMounted && visible && motionAppear) {
nextStatus = STATUS_APPEAR;
}
if (isMounted && visible && motionEnter) {
nextStatus = STATUS_ENTER;
}
if (
(isMounted && !visible && motionLeave) ||
(!isMounted && motionLeaveImmediately && !visible && motionLeave)
) {
nextStatus = STATUS_LEAVE;
}
if (nextStatus) {
setStatus(nextStatus);
startStep();
}
}, [visible]);
// 通过status匹配钩子
const eventHandlers = React.useMemo<{
[STEP_PREPARE]?: MotionPrepareEventHandler;
[STEP_START]?: MotionEventHandler;
[STEP_ACTIVE]?: MotionEventHandler;
}>(() => {
switch (status) {
case STATUS_APPEAR:
return {
[STEP_PREPARE]: onAppearPrepare,
[STEP_START]: onAppearStart,
[STEP_ACTIVE]: onAppearActive,
};
case STATUS_ENTER:
return {
[STEP_PREPARE]: onEnterPrepare,
[STEP_START]: onEnterStart,
[STEP_ACTIVE]: onEnterActive,
};
case STATUS_LEAVE:
return {
[STEP_PREPARE]: onLeavePrepare,
[STEP_START]: onLeaveStart,
[STEP_ACTIVE]: onLeaveActive,
};
default:
return {};
}
}, [status]);
// 传入钩子返回值可作为最新样式
onLeaveActive={() => { background: 'green' }}
step控制请求浏览器下一帧空闲执行动画且更新需要的动态样式
if (eventHandlers[STEP_PREPARE] && step === STEP_START) {
mergedStyle = {
transition: 'none',
...mergedStyle,
};
}
useDomMotionEvents
绑定和解绑动画结束事件
一般都是用户只关心动画什么时候结束
为什么不用监听开始?
// 动画名称 需要考虑兼容性
animationend | transitionend
// 绑定和解绑
addEventListener
removeEventListener
motion
// 构建浏览器动画样式前缀 兼容性 作为事件监听名称的兼容
prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();
prefixes[`Webkit${styleProp}`] = `webkit${eventName}`;
prefixes[`Moz${styleProp}`] = `moz${eventName}`;
prefixes[`ms${styleProp}`] = `MS${eventName}`;
prefixes[`O${styleProp}`] = `o${eventName.toLowerCase()}`;
// 组合两个关键字
animationend | transitionend