网站首页 > 技术文章 正文
手写一个 React 动画组件
Unity全流程开发热门游戏BallSort,助力迈入游戏领域
download:https://97yrbl.com/t-645.html
但是假如当前动画除了展现还需求其他交互,以至是一个组件需求动画效果,运用图片格式就不合理了。于是我写一个极端简单的 css 动画库 rc-css-animate。这里直接运用 animate.css 作为 css 动画的依赖库。 animate.css 不但提供了很多交互动画款式类,也提供了动画运转速度,延迟,以及反复次数等款式类。
能够看到,默许的 animate.css 构建动画都需求携带前缀 “animate__”。
An animated element
当然,该库是对 css 动画停止了一层封装,仍然支持其他动画库以及本人手写的 css 动画,但假如开发者需求对动画停止各种复杂控制,不引荐运用此库。
运用
能够应用如下方式运用:
import React, { useRef } from "react";
import ReactCssAnimate from "rc-css-animate";
// 引入 animate.css 作为动画依赖
import "animate.css";
function App() {
const animateRef = useRef(null);
return (
<ReactCssAnimate
// 定义当前展现动画的组件
// 默许运用 div
tag="div"
// 当前组件的 className
className=""
// 当前组件的 style
style={{}}
// 当前组件的 ref
ref={animateRef}
// 动画前缀
clsPrefix="animate__"
// 当前动画的 className
animateCls="animated backInDown infinite"
// 动画开端时分能否处于展现状态
initialVisible={false}
// 获取动画完毕能否处置展现状态
getVisibleWhenAnimateEnd={(cls) => {
// 假如当前 animateCls 中有 Out
// 返回 false 则会在动画完毕后不再显现
if (cls.includes("Out")) {
return false;
}
return true;
}}
// 动画完毕回调
onAnimationEnd={() => {
console.log("done");
}}
>
测试动画
);
}
ReactCssAnimate 运用了 React hooks,但是也提供了兼容的类组件。同时也提供了全局的前缀设置。
import React from "react";
import {
// 运用类组件兼容之前的版本
CompatibleRAnimate as ReactCssAnimate,
setPrefixCls,
} from "rc-css-animate";
// 引入 animate.css 作为动画依赖
import "animate.css";
// 设置全局 prefix,会被当前组件掩盖
setPrefixCls("animate__");
/** 构建动画块组件 */
function BlockWrapper(props) {
// 需求获取并传入 className, children, style
const { className, children, style } = props;
return (
<div
className={className}
style={{
background: "red",
padding: 100,
...style,
}}
>
{children}
);
}
function App() {
return (
<ReactCssAnimate
tag={BlockWrapper}
// 当前动画的 className
animateCls="animated backInDown infinite"
>
测试动画
);
}
源码解析
源代码较为简单,是基于 createElment 和 forwardRef 构建完成。其中 forwardRef 会将当前设置的 ref 转发到内部组件中去。关于 forwardRef 不熟习的同窗能够查看一下官网中关于 Refs 转发的文档。
import React, {
createElement,
forwardRef,
useCallback,
useEffect,
useState,
} from "react";
import { getPrefixCls } from "./prefix-cls";
import { AnimateProps } from "./types";
// 全局的动画前缀
let prefixCls: string = "";
const getPrefixCls = (): string => prefixCls;
// 设置全局的动画前缀
export const setPrefixCls = (cls: string) => {
if (typeof cls !== "string") {
return;
}
prefixCls = cls;
};
const Animate = (props: AnimateProps, ref: any) => {
const {
tag = "div",
clsPrefix = "",
animateCls,
style,
initialVisible,
onAnimationEnd,
getVisibleWhenAnimateEnd,
children,
} = props;
// 经过 initialVisible 获取组件的显隐,假如没有则默许为 true
const [visible, setVisible] = useState(initialVisible ?? true);
// 当前不需求展现,返回 null 即可
if (!visible) {
return null;
}
// 没有动画类,直接返回子组件
if (!animateCls || typeof animateCls !== "string") {
return <>{children}</>;
}
useEffect(() => {
// 当前没获取恳求完毕的设置显现躲藏,直接返回,不停止处置
if (!getVisibleWhenAnimateEnd) {
return;
}
const visibleWhenAnimateEnd = getVisibleWhenAnimateEnd(animateCls);
// 假如动画完毕后需求展现并且当前没有展现,直接停止展现
if (visibleWhenAnimateEnd && !visible) {
setVisible(true);
}
}, [animateCls, visible, getVisibleWhenAnimateEnd]);
const handleAnimationEnd = useCallback(() => {
if (!getVisibleWhenAnimateEnd) {
onAnimationEnd?.();
return;
}
// 当前处于展现状态,且动画完毕后需求躲藏,直接设置 visible 为 false
if (visible && !getVisibleWhenAnimateEnd(animateCls)) {
setVisible(false);
}
onAnimationEnd?.();
}, [getVisibleWhenAnimateEnd]);
let { className = "" } = props;
if (typeof className !== "string") {
className = "";
}
let animateClassName = animateCls;
// 获取最终的动画前缀
const finalClsPrefix = clsPrefix || getPrefixCls();
// 没有或者动画前缀不是字符串,不停止处置
if (!finalClsPrefix || typeof finalClsPrefix !== "string") {
animateClassName = animateCls.split(" ").map((item) =>
`${finalClsPrefix}${item}`
).join(" ");
}
// 创立并返回 React 元素
return createElement(
tag,
{
ref,
onAnimationEnd: handleAnimationEnd,
// 将传送的 className 和 animateClassName 兼并
className: className.concat(` ${animateClassName}`),
style,
},
children,
);
};
// 应用 forwardRef 转发 ref
// 第一个参数是 props,第二个参数是 ref
export default forwardRef(Animate);
以上代码全部在 rc-css-animate 中。这里也欢送各位小同伴提出 issue 和 pr。
猜你喜欢
- 2024-10-24 初探animation中steps()属性(animation steps属性)
- 2024-10-24 HTML5(九)——超强的 SVG 动画(htmlsvg动画代码)
- 2024-10-24 自定义日历(二)(自定义日历控件)
- 2024-10-24 Flutter简单动画Animation运用(flutter 视频教程)
- 2024-10-24 css3中动画animation中的steps()函数
- 2024-10-24 移动端渲染原理浅析(移动端渲染原理浅析设计)
- 2024-10-24 iOS 事件处理机制与图像渲染过程(简述ios中的事件响应机制)
- 2024-10-24 Android 开机问题分析(android无法开机)
- 2024-10-24 GoogleCTF + zer0ptsCTF + ImaginaryCTF 2023 笔记
- 2024-10-24 决战“金三银四”,中高级Web前端大厂面试秘籍:CSS篇
- 11-26Win7\8\10下一条cmd命令可查得笔记本电脑连接过的Wifi密码
- 11-26一文搞懂MySQL行锁、表锁、间隙锁详解
- 11-26电脑的wifi密码忘记了?一招教你如何找回密码,简单明了,快收藏
- 11-26代码解决忘记密码问题 教你用CMD命令查看所有连接过的WIFI密码
- 11-26CMD命令提示符能干嘛?这些功能你都知道吗?
- 11-26性能测试之慢sql分析
- 11-26论渗透信息收集的重要性
- 11-26如何查看电脑连接过的所有WiFi密码
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)