Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork28
😎 🍿 React hook for highly-performant and manipulable animations using Web Animations API.
License
wellyshen/use-web-animations
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
UsingWeb Animations API (a.k.a WAAPI) in the Reacthook way. Let's create highly-performant, flexible and manipulable web animations in the modern world. Hope you guys 👍🏻 it!
❤️ it? ⭐️ it onGitHub orTweet about it.
⚡️ Try yourself:https://use-web-animations.netlify.app
⚡️ Try yourself:https://use-web-animations.netlify.app#animations
- 🚀 Animate on the Web withhighly-performant and manipulable way, usingWeb Animations API.
- 🎣 Easy to use, based on Reacthook.
- 🎛 Super flexibleAPI design that can coverall the cases that you need.
- 🎞Built-ins animations for you, based onAnimate.css.
- 🔩 Supports custom
refsforsome reasons. - 📜 SupportsTypeScript type definition.
- 🗄️ Server-side rendering compatibility.
- 🦔 Tiny size (~ 4.4kB gzipped). No external dependencies, aside for the
react.
To useuse-web-animations, you must usereact@16.8.0 or greater which includes hooks.
This package is distributed vianpm.
$ yarn add @wellyshen/use-web-animations# or$ npm install --save @wellyshen/use-web-animationsWith the Web Animations API, we can move interactive animations from stylesheets to JavaScript, separating presentation from behavior. The API was designed based on the concept of theCSS Animations but there're still some differences between them. I strongly recommend you to read thedocumentation before we dive into this hook.
TheAPI design of the hook not only inherits the DX of theWeb Animations API but also provides useful features and sugar events to us. Here are some examples to show you how does it work.
⚠️ Most modern browsers support Web Animations API natively. You can also usepolyfill for full browser support.
Create an animation by thekeyframes andanimationOptions options (these are theparameters of theElement.animate()).
💡 This hook supports thepseudoElement property via the
animationOptionsoption.
importuseWebAnimationsfrom"@wellyshen/use-web-animations";constApp=()=>{const{ ref, playState}=useWebAnimations({keyframes:{transform:"translateX(500px)",// Move by 500pxbackground:["red","blue","green"],// Go through three colors},animationOptions:{delay:500,// Start with a 500ms delayduration:1000,// Run for 1000msiterations:2,// Repeat oncedirection:"alternate",// Run the animation forwards and then backwardseasing:"ease-in-out",// Use a fancy timing function},onReady:({ playState, animate, animation})=>{// Triggered when the animation is ready to play},onUpdate:({ playState, animate, animation})=>{// Triggered when the animation enters the running state or changes state},onFinish:({ playState, animate, animation})=>{// Triggered when the animation enters the finished state},// More useful options...});return(<divclassName="container"><p>🍿 Animation is{playState}</p><divclassName="target"ref={ref}/></div>);};
For browsers that don't yet support theonReady andonFinish events, we can use theonUpdate to monitor the animation's state instead.
letprevPending=true;constApp=()=>{const{ ref}=useWebAnimations({// ...onUpdate:({ playState,animation:{ pending}})=>{if(prevPending&&!pending){console.log("Animation is ready to play");}prevPending=pending;if(playState==="finished"){console.log("Animation has finished playing");}},});// ...};
Thekeyframes andanimationOptions are cached when the hook is mounted. However, we can set/update the animation by theanimation method.
const{ animation}=useWebAnimations();constchangeAnim=()=>animation({keyframes:{transform:["translateX(0)","translateX(100px)"]},animationOptions:1000,id:"123",playbackRate:1,autoPlay:true,});
The shortcoming with existing technologies was the lack of playback control. The Web Animations API provides several useful methods for controlling playback: play, pause, reverse, cancel, finish, seek, control speed via themethods of theAnimation interface. This hook exposes the animation instance for us to interact with animations, we can access it by thegetAnimation() return value.
importuseWebAnimationsfrom"@wellyshen/use-web-animations";constApp=()=>{const{ ref, playState, getAnimation}=useWebAnimations({playbackRate:0.5,// Change playback rate, default is 1autoPlay:false,// Automatically starts the animation, default is truekeyframes:{transform:"translateX(500px)"},animationOptions:{duration:1000,fill:"forwards"},});constplay=()=>{getAnimation().play();};constpause=()=>{getAnimation().pause();};constreverse=()=>{getAnimation().reverse();};constcancel=()=>{getAnimation().cancel();};constfinish=()=>{getAnimation().finish();};constseek=(e)=>{constanimation=getAnimation();consttime=(animation.effect.getTiming().duration/100)*e.target.value;animation.currentTime=time;};constupdatePlaybackRate=(e)=>{getAnimation().updatePlaybackRate(e.target.value);};return(<divclassName="container"><buttononClick={play}>Play</button><buttononClick={pause}>Pause</button><buttononClick={reverse}>Reverse</button><buttononClick={cancel}>Cancel</button><buttononClick={finish}>Finish</button><inputtype="range"onChange={seek}/><inputtype="number"defaultValue="1"onChange={updatePlaybackRate}/><divclassName="target"ref={ref}/></div>);};
When using the Web Animations API, we can get the information of an animation via theproperties of theAnimation interface. However, we can get the information of an animation by thegetAnimation() return value as well.
importuseWebAnimationsfrom"@wellyshen/use-web-animations";constApp=()=>{const{ ref, getAnimation}=useWebAnimations({keyframes:{transform:"translateX(500px)"},animationOptions:{duration:1000,fill:"forwards"},});constspeedUp=()=>{constanimation=getAnimation();animation.updatePlaybackRate(animation.playbackRate*0.25);};constjumpToHalf=()=>{constanimation=getAnimation();animation.currentTime=animation.effect.getTiming().duration/2;};return(<divclassName="container"><buttononClick={speedUp}>Speed Up</button><buttononClick={jumpToHalf}>Jump to Half</button><divclassName="target"ref={ref}/></div>);};
The animation instance isn't a part ofReact state, which means we need to access it by thegetAnimation() whenever we need. If you want to monitor an animation's information, here's theonUpdate event for you. The event is implemented by therequestAnimationFrame internally and the event callback is triggered when the animation entersrunning state or changes state.
import{useState}from"react";importuseWebAnimationsfrom"@wellyshen/use-web-animations";constApp=()=>{const[showEl,setShowEl]=useState(false);const{ ref}=useWebAnimations({keyframes:{transform:"translateX(500px)"},animationOptions:{duration:1000,fill:"forwards"},onUpdate:({ animation})=>{if(animation.currentTime>animation.effect.getTiming().duration/2)setShowEl(true);},});return(<divclassName="container">{showEl&&<divclassName="some-element"/>}<divclassName="target"ref={ref}/></div>);};
We can create and play an animation at theanimationOptions we want by theanimate method, which is implemented based on theElement.animate(). It's useful for interactions and thecomposite modes.
Let's create a mouse interaction effect:
import{useEffect}from"react";importuseWebAnimationsfrom"@wellyshen/use-web-animations";constApp=()=>{const{ ref, animate}=useWebAnimations();useEffect(()=>{document.addEventListener("mousemove",(e)=>{// The target will follow the mouse cursoranimate({keyframes:{transform:`translate(${e.clientX}px,${e.clientY}px)`},animationOptions:{duration:500,fill:"forwards"},});});},[animate]);return(<divclassName="container"><divclassName="target"ref={ref}/></div>);};
Create a bounce effect via lifecycle and composite mode:
importuseWebAnimationsfrom"@wellyshen/use-web-animations";constApp=()=>{const{ ref, animate}=useWebAnimations({id:"fall",// Set animation id, default is empty stringkeyframes:[{top:0,easing:"ease-in"},{top:"500px"}],animationOptions:{duration:300,fill:"forwards"},onFinish:({ animate, animation})=>{// Lifecycle is triggered by each animation, we can check the id to prevent animation from repeatingif(animation.id==="bounce")return;animate({id:"bounce",keyframes:[{top:"500px",easing:"ease-in"},{top:"10px",easing:"ease-out"},],animationOptions:{duration:300,composite:"add"},});},});return(<divclassName="container"><divclassName="target"ref={ref}/></div>);};
⚠️ Composite modes isn't fully supported by all the browsers, please check thebrowser compatibility carefully before using it.
Too lazy to think about animation? We provide a collection of ready-to-use animations for you, they are implemented based onAnimate.css.
importuseWebAnimations,{bounce}from"@wellyshen/use-web-animations";constApp=()=>{// Add a pre-defined effect to the targetconst{ ref}=useWebAnimations({ ...bounce});return(<divclassName="container"><divclassName="target"ref={ref}/></div>);};
We can customize the built-in animation by overriding its properties:
const{ keyframes, animationOptions}=bounce;const{ ref}=useWebAnimations({ keyframes,animationOptions:{ ...animationOptions,delay:1000,// Delay 1sduration:animationOptions.duration*0.75,// Speed up the animation},});
See all available animations
- bounce
- flash
- pulse
- rubberBand
- shakeX
- shakeY
- headShake
- swing
- tada
- wobble
- jello
- heartBeat
- backInDown
- backInLeft
- backInRight
- backInUp
- backOutDown
- backOutLeft
- backOutRight
- backOutUp
- bounceIn
- bounceInDown
- bounceInLeft
- bounceInRight
- bounceInUp
- bounceOut
- bounceOutDown
- bounceOutLeft
- bounceOutRight
- bounceOutUp
- fadeIn
- fadeInDown
- fadeInDownBig
- fadeInLeft
- fadeInLeftBig
- fadeInRight
- fadeInRightBig
- fadeInUp
- fadeInUpBig
- fadeInTopLeft
- fadeInTopRight
- fadeInBottomLeft
- fadeInBottomRight
- fadeOut
- fadeOutDown
- fadeOutDownBig
- fadeOutLeft
- fadeOutLeftBig
- fadeOutRight
- fadeOutRightBig
- fadeOutUp
- fadeOutUpBig
- fadeOutTopLeft
- fadeOutTopRight
- fadeOutBottomLeft
- fadeOutBottomRight
- flip
- flipInX
- flipInY
- flipOutX
- flipOutY
- lightSpeedInRight
- lightSpeedInLeft
- lightSpeedOutRight
- lightSpeedOutLeft
- rotateIn
- rotateInDownLeft
- rotateInDownRight
- rotateInUpLeft
- rotateInUpRight
- rotateOut
- rotateOutDownLeft
- rotateOutDownRight
- rotateOutUpLeft
- rotateOutUpRight
- hinge
- jackInTheBox
- rollIn
- rollOut
- zoomIn
- zoomInDown
- zoomInLeft
- zoomInRight
- zoomInUp
- zoomOut
- zoomOutDown
- zoomOutLeft
- zoomOutRight
- zoomOutUp
- slideInDown
- slideInLeft
- slideInRight
- slideInUp
- slideOutDown
- slideOutLeft
- slideOutRight
- slideOutUp
In case of you had a ref already or you want to share a ref for other purposes. You can pass in the ref instead of using the one provided by this hook.
constref=useRef();const{ playState}=useWebAnimations({ ref});
This hook supportsTypeScript, you can tell the hook what type of element you are going to animate through thegeneric type:
constApp=()=>{const{ ref}=useWebAnimations<HTMLDivElement>();return<divref={ref}/>;};
💡 For more available types, pleasecheck it out.
constreturnObj=useWebAnimations(options?:object);
It's returned with the following properties.
| Key | Type | Default | Description |
|---|---|---|---|
ref | object | Used to set the target element for animating. | |
playState | string | undefined | Describes theplayback state of an animation. | |
getAnimation | function | Access theanimation instance forplayback control,animation's information and more. | |
animate | function | Imperativelyset/update the animation. Useful forinteractive animations and composite animations. |
Theoptions provides the following configurations and event callbacks for you.
| Key | Type | Default | Description |
|---|---|---|---|
ref | object | Forsome reasons, you can pass in your own ref instead of using the built-in. | |
id | string | "" | Sets the ID of an animation, implemented based on theAnimation.id. |
playbackRate | number | 1 | Sets the playback rate of an animation, implemented based on theAnimation.playbackRate. |
autoPlay | boolean | true | Automatically starts the animation. |
keyframes | Array | object | An array of keyframe objects, or a keyframe object whose property are arrays of values to iterate over. Seebasic usage for more details. | |
animationOptions | number | object | Aninteger representing the animation's duration (in milliseconds), or anobject containing one or more timing properties. Seebasic usage for more details. | |
onReady | function | It's invoked when an animation is ready to play. You can access theplayState,animate andanimation from the event object. | |
onUpdate | function | It's invoked when an animation enters therunning state or changes state. You can access theplayState,animate andanimation from the event object. | |
onFinish | function | It's invoked when an animation enters thefinished state. You can access theplayState,animate andanimation from the event object. |
Web Animations API has good support amongst browsers, but it's not universal. You'll need to polyfill browsers that don't support it. Polyfills is something you should do consciously at the application level. Thereforeuse-web-animations doesn't include it.
Installweb-animations-js:
$ yarn add web-animations-js# or$ npm install --save web-animations-jsThen import it at your app's entry point:
import"web-animations-js/web-animations.min";
You can read thedocument for more information.
💡 If you have written any blog post or article about
use-web-animations, please open a PR to add it here.
- Featured onReact Status #196.
- Featured onJavaScript Weekly #496.
- Featured onReact Newsletter #218.
Thanks goes to these wonderful people (emoji key):
Welly 💻📖🚧 |
This project follows theall-contributors specification. Contributions of any kind welcome!
About
😎 🍿 React hook for highly-performant and manipulable animations using Web Animations API.
Topics
Resources
License
Code of conduct
Contributing
Security policy
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Contributors3
Uh oh!
There was an error while loading.Please reload this page.

