During the development of a project, designers will inevitably create animated effects to enhance the user experience. If the current effect does not require interaction and is only for display, we can use GIF or APNG to achieve the effect.
but if the current animation requires other interactions in addition to presentation, or even a component requires animation effects, it would be unreasonable to use a picture format. So I wrote an extremely simple css animation library rc-css-animate . Here we directly use animate.css as the dependent library for css animation. Animate.css not only provides many interactive animation style classes, but also provides animation running speed, latency, and the number of repetitions and other style classes.
as you can see, the default animate.css build animation needs to carry the prefix "animate__".
<h1 class="animate__animated animate__bounce">An animated element</h1>
of course, the library encapsulates css animation and still supports other animation libraries as well as their own handwritten css animation, but this library is not recommended if developers need to control all kinds of complex animation.
use
can be used in the following ways:
import React, { useRef } from "react";
import ReactCssAnimate from "rc-css-animate";
// introduce animate.css as an animation dependency
import "animate.css";
function App() {
const animateRef = useRef(null);
return (
<div className="App">
<ReactCssAnimate
// Define the components that currently display the animation
// Use by default div
tag="div"
// Of the current component className
className=""
// Of the current component style
style={{}}
// Of the current component ref
ref={animateRef}
// Animation prefix
clsPrefix="animate__"
// Of the current animation className
animateCls="animated backInDown infinite"
// Is the animation displayed at the beginning of the animation?
initialVisible={false}
// Gets whether the display status is processed at the end of the animation
getVisibleWhenAnimateEnd={(cls) =>{
// if there is an Out in the current animateCls
// if false is returned, it will not be displayed at the end of the animation.
If (cls.includes ("Out")) {
Return false
}
Return true
}}
// callback at the end of the animation
OnAnimationEnd= {() => {
console.log("done");
}}
>
<div>
Test animation
</div>
</ReactCssAnimate>
</div>
);
}
ReactCssAnimate uses React hooks, but also provides compatible class components. Global prefix settings are also provided.
import React from "react";
import {
// use previous versions of the compatibility of class components
CompatibleRAnimate as ReactCssAnimate,
setPrefixCls,
} from "rc-css-animate";
// introduce animate.css as an animation dependency
import "animate.css";
// set the global prefix, which will be overwritten by the current component
setPrefixCls("animate__");
/ * * build animation block components * /
function BlockWrapper(props) {
// need to obtain and input className, children, style
const { className, children, style } = props;
return (
<div
className={className}
style={{
background: "red",
padding: 100,
...style,
}}
>
{children}
</div>
);
}
function App() {
return (
<div className="App">
<ReactCssAnimate
tag={BlockWrapper}
// Of the current animation className
animateCls="animated backInDown infinite"
>
<div>
Test animation
</div>
</ReactCssAnimate>
</div>
);
}
Source code parsing
The source code is relatively simple and is built on createElment and forwardRef. Where forwardRef forwards the currently set ref to the internal component. Students who are not familiar with forwardRef can check out the documents on the official website about Refs retweet .
import React, {
createElement,
forwardRef,
useCallback,
useEffect,
useState,
} from "react";
import { getPrefixCls } from "./prefix-cls";
import { AnimateProps } from "./types";
// Global animation prefix
let prefixCls: string = "";
const getPrefixCls = (): string => prefixCls;
// set the global animation prefix
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;
// obtain the explicit concealment of the component through initialVisible. If not, the default is true.
const [visible, setVisible] = useState<boolean>(initialVisible ?? true);
// currently you don't need to show it. Just return null.
if (!visible) {
return null;
}
// No animation class, return subcomponents directly
if (!animateCls || typeof animateCls !== "string") {
return <>{children}</>;
}
useEffect(() => {
// the settings that do not get the end of the request are hidden and returned directly without processing.
if (!getVisibleWhenAnimateEnd) {
return;
}
const visibleWhenAnimateEnd = getVisibleWhenAnimateEnd(animateCls);
// if the animation needs to be displayed at the end of the animation and is not currently displayed, show it directly.
if (visibleWhenAnimateEnd && !visible) {
setVisible(true);
}
}, [animateCls, visible, getVisibleWhenAnimateEnd]);
const handleAnimationEnd = useCallback(() => {
if (!getVisibleWhenAnimateEnd) {
onAnimationEnd?.();
return;
}
// it is currently displayed and needs to be hidden after the animation ends. Set visible to false directly.
if (visible && !getVisibleWhenAnimateEnd(animateCls)) {
setVisible(false);
}
onAnimationEnd?.();
}, [getVisibleWhenAnimateEnd]);
let { className = "" } = props;
if (typeof className !== "string") {
className = "";
}
let animateClassName = animateCls;
// get the final animation prefix
const finalClsPrefix = clsPrefix || getPrefixCls();
// none or the animation prefix is not a string and will not be processed
if (!finalClsPrefix || typeof finalClsPrefix !== "string") {
animateClassName = animateCls.split(" ").map((item) =>
`${finalClsPrefix}${item}`
).join(" ");
}
// create and return React element
return createElement(
tag,
{
ref,
onAnimationEnd: handleAnimationEnd,
// merge the passed className and animateClassName
className: className.concat(` ${animateClassName}`),
style,
},
children,
);
};
// use forwardRef to forward ref
// the first parameter is props, and the second parameter is ref
export default forwardRef(Animate);
the above code is all in rc-css-animate . You are also welcome to put forward issue and pr.