use-web-animations

React hook for highly-performant and manipulable animations using Web Anima...

README

# USE-WEB-ANIMATIONS

Using Web Animations API (a.k.a WAAPI) in the React hook way. Let's create highly-performant, flexible and manipulable web animations in the modern world. Hope you guys 👍🏻 it!

❤️ it? ⭐️ it on GitHub or Tweet about it.
build status coverage status npm version npm downloads npm downloads gzip size All Contributors PRs welcome Twitter URL

demo_basic

⚡️ Try yourself: https://use-web-animations.netlify.app

demo_animate

⚡️ Try yourself: https://use-web-animations.netlify.app#animations

Features


- 🚀 Animate on the Web with highly-performant and manipulable way, using Web Animations API.
- 🎣 Easy to use, based on React hook.
- 🎛 Super flexible API design that can cover all the cases that you need.
- 🎞 Built-ins animations for you, based on Animate.css.
- 🔩 Supports custom refs for some reasons.
- 📜 Supports TypeScript type definition.
- 🗄️ Server-side rendering compatibility.
- 🦔 Tiny size (~ 4.4kB gzipped). No external dependencies, aside for thereact.

Requirement


To use use-web-animations, you must use react@16.8.0 or greater which includes hooks.

Installation


This package is distributed via npm.

  1. ```sh
  2. $ yarn add @wellyshen/use-web-animations
  3. # or
  4. $ npm install --save @wellyshen/use-web-animations
  5. ```

Before We Start


With 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 the CSS Animations but there're still some differences between them. I strongly recommend you to read the documentation before we dive into this hook.

Usage


The API design of the hook not only inherits the DX of the Web 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 use polyfill for full browser support.


Basic Usage


Create an animation by the keyframes and animationOptions options (these are the parameters of theElement.animate()).

💡 This hook supports the pseudoElement property via theanimationOptions option.

Edit useWebAnimations - basic

  1. ``` js
  2. import useWebAnimations from "@wellyshen/use-web-animations";

  3. const App = () => {
  4.   const { ref, playState } = useWebAnimations({
  5.     keyframes: {
  6.       transform: "translateX(500px)", // Move by 500px
  7.       background: ["red", "blue", "green"], // Go through three colors
  8.     },
  9.     animationOptions: {
  10.       delay: 500, // Start with a 500ms delay
  11.       duration: 1000, // Run for 1000ms
  12.       iterations: 2, // Repeat once
  13.       direction: "alternate", // Run the animation forwards and then backwards
  14.       easing: "ease-in-out", // Use a fancy timing function
  15.     },
  16.     onReady: ({ playState, animate, animation }) => {
  17.       // Triggered when the animation is ready to play
  18.     },
  19.     onUpdate: ({ playState, animate, animation }) => {
  20.       // Triggered when the animation enters the running state or changes state
  21.     },
  22.     onFinish: ({ playState, animate, animation }) => {
  23.       // Triggered when the animation enters the finished state
  24.     },
  25.     // More useful options...
  26.   });

  27.   return (
  28.     <div className="container">
  29.       <p> Animation is {playState}</p>
  30.       <div className="target" ref={ref} />
  31.     </div>
  32.   );
  33. };
  34. ```

For browsers that don't yet support the onReady and onFinish events, we can use the onUpdate to monitor the animation's state instead.

  1. ``` js
  2. let prevPending = true;

  3. const App = () => {
  4.   const { ref } = useWebAnimations({
  5.     // ...
  6.     onUpdate: ({ playState, animation: { pending } }) => {
  7.       if (prevPending && !pending) {
  8.         console.log("Animation is ready to play");
  9.       }
  10.       prevPending = pending;

  11.       if (playState === "finished") {
  12.         console.log("Animation has finished playing");
  13.       }
  14.     },
  15.   });

  16.   // ...
  17. };
  18. ```

Setting/Updating Animation


The keyframes and animationOptions are cached when the hook is mounted. However, we can set/update the animation by the animation method.

  1. ``` js
  2. const { animation } = useWebAnimations();

  3. const changeAnim = () =>
  4.   animation({
  5.     keyframes: { transform: ["translateX(0)", "translateX(100px)"] },
  6.     animationOptions: 1000,
  7.     id: "123",
  8.     playbackRate: 1,
  9.     autoPlay: true,
  10.   });
  11. ```

Playback Control


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 the methods of theAnimation interface. This hook exposes the animation instance for us to interact with animations, we can access it by the getAnimation() return value.
Edit useWebAnimations - controls

  1. ``` js
  2. import useWebAnimations from "@wellyshen/use-web-animations";

  3. const App = () => {
  4.   const { ref, playState, getAnimation } = useWebAnimations({
  5.     playbackRate: 0.5, // Change playback rate, default is 1
  6.     autoPlay: false, // Automatically starts the animation, default is true
  7.     keyframes: { transform: "translateX(500px)" },
  8.     animationOptions: { duration: 1000, fill: "forwards" },
  9.   });

  10.   const play = () => {
  11.     getAnimation().play();
  12.   };

  13.   const pause = () => {
  14.     getAnimation().pause();
  15.   };

  16.   const reverse = () => {
  17.     getAnimation().reverse();
  18.   };

  19.   const cancel = () => {
  20.     getAnimation().cancel();
  21.   };

  22.   const finish = () => {
  23.     getAnimation().finish();
  24.   };

  25.   const seek = (e) => {
  26.     const animation = getAnimation();
  27.     const time = (animation.effect.getTiming().duration / 100) * e.target.value;
  28.     animation.currentTime = time;
  29.   };

  30.   const updatePlaybackRate = (e) => {
  31.     getAnimation().updatePlaybackRate(e.target.value);
  32.   };

  33.   return (
  34.     <div className="container">
  35.       <button onClick={play}>Play</button>
  36.       <button onClick={pause}>Pause</button>
  37.       <button onClick={reverse}>Reverse</button>
  38.       <button onClick={cancel}>Cancel</button>
  39.       <button onClick={finish}>Finish</button>
  40.       <input type="range" onChange={seek} />
  41.       <input type="number" defaultValue="1" onChange={updatePlaybackRate} />
  42.       <div className="target" ref={ref} />
  43.     </div>
  44.   );
  45. };
  46. ```

Getting Animation's Information


When using the Web Animations API, we can get the information of an animation via the properties of theAnimation interface. However, we can get the information of an animation by the getAnimation() return value as well.

  1. ``` js
  2. import useWebAnimations from "@wellyshen/use-web-animations";

  3. const App = () => {
  4.   const { ref, getAnimation } = useWebAnimations({
  5.     keyframes: { transform: "translateX(500px)" },
  6.     animationOptions: { duration: 1000, fill: "forwards" },
  7.   });

  8.   const speedUp = () => {
  9.     const animation = getAnimation();
  10.     animation.updatePlaybackRate(animation.playbackRate * 0.25);
  11.   };

  12.   const jumpToHalf = () => {
  13.     const animation = getAnimation();
  14.     animation.currentTime = animation.effect.getTiming().duration / 2;
  15.   };

  16.   return (
  17.     <div className="container">
  18.       <button onClick={speedUp}>Speed Up</button>
  19.       <button onClick={jumpToHalf}>Jump to Half</button>
  20.       <div className="target" ref={ref} />
  21.     </div>
  22.   );
  23. };
  24. ```

The animation instance isn't a part of React state, which means we need to access it by thegetAnimation() whenever we need. If you want to monitor an animation's information, here's the onUpdate event for you. The event is implemented by the requestAnimationFrame internally and the event callback is triggered when the animation entersrunning state or changes state.

  1. ``` js
  2. import { useState } from "react";
  3. import useWebAnimations from "@wellyshen/use-web-animations";

  4. const App = () => {
  5.   const [showEl, setShowEl] = useState(false);
  6.   const { ref } = useWebAnimations({
  7.     keyframes: { transform: "translateX(500px)" },
  8.     animationOptions: { duration: 1000, fill: "forwards" },
  9.     onUpdate: ({ animation }) => {
  10.       if (animation.currentTime > animation.effect.getTiming().duration / 2)
  11.         setShowEl(true);
  12.     },
  13.   });

  14.   return (
  15.     <div className="container">
  16.       {showEl && <div className="some-element" />}
  17.       <div className="target" ref={ref} />
  18.     </div>
  19.   );
  20. };
  21. ```

Dynamic Interactions with Animation


We can create and play an animation at the animationOptions we want by the animate method, which is implemented based on the Element.animate(). It's useful for interactions and the composite modes.

Let's create a mouse interaction effect:
Edit useWebAnimations - interaction

  1. ``` js
  2. import { useEffect } from "react";
  3. import useWebAnimations from "@wellyshen/use-web-animations";

  4. const App = () => {
  5.   const { ref, animate } = useWebAnimations();

  6.   useEffect(() => {
  7.     document.addEventListener("mousemove", (e) => {
  8.       // The target will follow the mouse cursor
  9.       animate({
  10.         keyframes: { transform: `translate(${e.clientX}px, ${e.clientY}px)` },
  11.         animationOptions: { duration: 500, fill: "forwards" },
  12.       });
  13.     });
  14.   }, [animate]);

  15.   return (
  16.     <div className="container">
  17.       <div className="target" ref={ref} />
  18.     </div>
  19.   );
  20. };
  21. ```

Create a bounce effect via lifecycle and composite mode:

  1. ``` js
  2. import useWebAnimations from "@wellyshen/use-web-animations";

  3. const App = () => {
  4.   const { ref, animate } = useWebAnimations({
  5.     id: "fall", // Set animation id, default is empty string
  6.     keyframes: [{ top: 0, easing: "ease-in" }, { top: "500px" }],
  7.     animationOptions: { duration: 300, fill: "forwards" },
  8.     onFinish: ({ animate, animation }) => {
  9.       // Lifecycle is triggered by each animation, we can check the id to prevent animation from repeating
  10.       if (animation.id === "bounce") return;

  11.       animate({
  12.         id: "bounce",
  13.         keyframes: [
  14.           { top: "500px", easing: "ease-in" },
  15.           { top: "10px", easing: "ease-out" },
  16.         ],
  17.         animationOptions: { duration: 300, composite: "add" },
  18.       });
  19.     },
  20.   });

  21.   return (
  22.     <div className="container">
  23.       <div className="target" ref={ref} />
  24.     </div>
  25.   );
  26. };
  27. ```

⚠️ Composite modes isn't fully supported by all the browsers, please check the browser compatibility carefully before using it.


Use Built-in Animations


Too lazy to think about animation? We provide a collection of ready-to-use animations for you, they are implemented based on Animate.css.


  1. ``` js
  2. import useWebAnimations, { bounce } from "@wellyshen/use-web-animations";

  3. const App = () => {
  4.   // Add a pre-defined effect to the target
  5.   const { ref } = useWebAnimations({ ...bounce });

  6.   return (
  7.     <div className="container">
  8.       <div className="target" ref={ref} />
  9.     </div>
  10.   );
  11. };
  12. ```

We can customize the built-in animation by overriding its properties:

  1. ``` js
  2. const { keyframes, animationOptions } = bounce;
  3. const { ref } = useWebAnimations({
  4.   keyframes,
  5.   animationOptions: {
  6.     ...animationOptions,
  7.     delay: 1000, // Delay 1s
  8.     duration: animationOptions.duration * 0.75, // Speed up the animation
  9.   },
  10. });
  11. ```

See all available animations

Attention seekers


- bounce
- flash
- pulse
- rubberBand
- shakeX
- shakeY
- headShake
- swing
- tada
- wobble
- jello
- heartBeat

Back entrances


- backInDown
- backInLeft
- backInRight
- backInUp

Back exits


- backOutDown
- backOutLeft
- backOutRight
- backOutUp

Bouncing entrances


- bounceIn
- bounceInDown
- bounceInLeft
- bounceInRight
- bounceInUp

Bouncing exits


- bounceOut
- bounceOutDown
- bounceOutLeft
- bounceOutRight
- bounceOutUp

Fading entrances


- fadeIn
- fadeInDown
- fadeInDownBig
- fadeInLeft
- fadeInLeftBig
- fadeInRight
- fadeInRightBig
- fadeInUp
- fadeInUpBig
- fadeInTopLeft
- fadeInTopRight
- fadeInBottomLeft
- fadeInBottomRight

Fading exits


- fadeOut
- fadeOutDown
- fadeOutDownBig
- fadeOutLeft
- fadeOutLeftBig
- fadeOutRight
- fadeOutRightBig
- fadeOutUp
- fadeOutUpBig
- fadeOutTopLeft
- fadeOutTopRight
- fadeOutBottomLeft
- fadeOutBottomRight

Flippers


- flip
- flipInX
- flipInY
- flipOutX
- flipOutY

Lightspeed


- lightSpeedInRight
- lightSpeedInLeft
- lightSpeedOutRight
- lightSpeedOutLeft

Rotating entrances


- rotateIn
- rotateInDownLeft
- rotateInDownRight
- rotateInUpLeft
- rotateInUpRight

Rotating exits


- rotateOut
- rotateOutDownLeft
- rotateOutDownRight
- rotateOutUpLeft
- rotateOutUpRight

Specials


- hinge
- jackInTheBox
- rollIn
- rollOut

Zooming entrances


- zoomIn
- zoomInDown
- zoomInLeft
- zoomInRight
- zoomInUp

Zooming exits


- zoomOut
- zoomOutDown
- zoomOutLeft
- zoomOutRight
- zoomOutUp

Sliding entrances


- slideInDown
- slideInLeft
- slideInRight
- slideInUp

Sliding exits


- slideOutDown
- slideOutLeft
- slideOutRight
- slideOutUp

Use Your Own ref


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.

  1. ``` js
  2. const ref = useRef();
  3. const { playState } = useWebAnimations({ ref });
  4. ```

Working in TypeScript


This hook supports TypeScript, you can tell the hook what type of element you are going to animate through the generic type:

  1. ```ts
  2. const App = () => {
  3.   const { ref } = useWebAnimations<HTMLDivElement>();

  4.   return <div ref={ref} />;
  5. };
  6. ```

💡 For more available types, please check it out.


API


  1. ``` js
  2. const returnObj = useWebAnimations(options?: object);
  3. ```

Return Object


It's returned with the following properties.

KeyTypeDefaultDescription
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
`ref`object|
`playState`stringundefined|
`getAnimation`function|
`animate`function|

Parameter


The options provides the following configurations and event callbacks for you.

KeyTypeDefaultDescription
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
`ref`object|
`id`string`""`Sets
`playbackRate`number`1`Sets
`autoPlay`boolean`true`Automatically
`keyframes`Arrayobject|
`animationOptions`numberobject|

| onReady | function | | It's invoked when an animation is ready to play. You can access the playState, animate and animation from the event object. |
| onUpdate | function | | It's invoked when an animation enters the running state or changes state. You can access the playState, animate and animation from the event object. |
| onFinish | function | | It's invoked when an animation enters the finished state. You can access the playState, animate and animation from the event object. |

Use Polyfill


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.


  1. ```sh
  2. $ yarn add web-animations-js
  3. # or
  4. $ npm install --save web-animations-js
  5. ```

Then import it at your app's entry point:

  1. ``` js
  2. import "web-animations-js/web-animations.min";
  3. ```

You can read the document for more information.

Articles / Blog Posts


💡 If you have written any blog post or article about use-web-animations, please open a PR to add it here.


- Featured on React Status #196.
- Featured on JavaScript Weekly #496.
- Featured on React Newsletter #218.

Contributors ✨


Thanks goes to these wonderful people (emoji key):





Welly

💻 📖 🚧






This project follows the all-contributors specification. Contributions of any kind welcome!