wellyshen / use-web-animations Goto Github PK
View Code? Open in Web Editor NEW๐ ๐ฟ React hook for highly-performant and manipulable animations using Web Animations API.
Home Page: https://use-web-animations.netlify.app
License: MIT License
๐ ๐ฟ React hook for highly-performant and manipulable animations using Web Animations API.
Home Page: https://use-web-animations.netlify.app
License: MIT License
A clear and concise description of what the bug is.
The Typescript types in index.d.ts
don't define the huge list of (incredibly useful) preset animations. For example, when importing the fadeOut
preset:
import useWebAnimations, { fadeOut } from '@wellyshen/use-web-animations';
I get the following error (even though fadeOut
is exported by index.js
... it's just not defined in index.d.ts
so Typescript doesn't pick it up):
Module '"@wellyshen/use-web-animations"' has no exported member 'fadeOut'. Did you mean to use 'import fadeOut from "@wellyshen/use-web-animations"' instead?
Steps to reproduce the behavior, please provide code snippets or a repository:
To fix this, merely add all of those animation presets as exported constants to your index.d.ts
(this will be quite tedious... you should probably look into a better way to do this perhaps).
I'm trying to find a way to make an animation with your hook reusable in a High Order Component. It may be my confusion around ref
, but I would be interested to see if you have an example of use-web-animations
in an HOC
Following up on my question on Twitter ๐
Hi! I'm trying to animate something like confetti falling down seen here. In order to get this to work I want to animate hundreds of small divs, with small variations between the animations.
If I have something like this in my component:
const {pushToRefs, getAnimations} = useWebAnimationGroup((index) => {...confettiFalling(index)})
// confettiFalling returns slightly different parameters for each index
return <>
{[...Array(1000).keys()].map(key => (
// pushToRefs registers each div in an array and runs the function provided to the hook for each one.
<div className="confetti-particle" key={item} ref={pushToRefs}/>
))
</>
Then I'd like to be able to pause/reverse all of the animations as if they were one
const handlePause = () => getAnimations().pause()
I could maybe get it t work with a HOC which calls the use web animation hook internally, and provides a prop for play state, and have the HOC use useEffect
to react to the changes. It would be a bit cumbersome maybe, but it can probably work fine for my usecase.
Recently I was working on a project. I had installed version 0.6 and there was an error that "bounce is not exported from @wellyshen/use-web-animations". Then I installed version 0.5.11 and it was working then.
Hope my issue will be helpful in solving the problem.
The latest vite3.0 & React will trigger unmount forcibly at the first render.
This behavior causes hasUnmountedRef.current === true. The related code is as below.
if (!hasUnmountedRef.current) {
// @ts-expect-error
onFinishRef.current({
playState: animation.playState,
animate: animate,
animation: animation
});
}
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import "./App.css";
import useWebAnimations from "@wellyshen/use-web-animations";
function App() {
const [count, setCount] = useState(0);
const { animate, ref, playState, getAnimation } = useWebAnimations({
onFinish: ({ animation }) => {
console.warn("onFinish");
},
});
return (
<div className="App">
<div className="card">
<button
onClick={() => {
animate({
keyframes: [
{ transform: `scale(1.08)`, offset: 0 },
{ transform: `scale(1)`, offset: 150 / 500.0 },
{ transform: `scale(1.08)`, offset: 1 },
],
animationOptions: {
duration: 500,
easing: "cubic-bezier(0.3,0,0.15,1)",
},
});
setCount((count) => count + 1);
}}
ref={ref}
>
count is {count}, {playState}
</button>
</div>
</div>
);
}
export default App;
"onFinish" should be triggered
This package will crash in SSR due to the built-in polyfill. I will fix the bug ASAP.
I'm trying to use the onFinish callback, together with controlling play status. After the animation finishes for the first time, the onFinish callback will be called, but not on subsequent calls.
Add an onFinish handler, start the animation with getAnimation().play() twice.
onFinish should be called whenever playState switches from 'running' to 'finished'
I have tried creating a HOC with an animated box, which has slightly different animations depending on Math.random(), the animation should run once and finish, but for some reason it keeps looping and each box finishes animating after a random amount of iteration.
import "./styles.css";
import useWebAnimations from "@wellyshen/use-web-animations";
export default function App() {
return (
<div className="container">
{[...Array(5).keys()].map((key) => (
<AnimatedBox key={key} />
))}
</div>
);
}
const AnimatedBox = () => {
const { ref } = useWebAnimations({
keyframes: [{ top: "0%" }, { top: Math.random() > 0.5 ? "50%" : "100%" }],
animationOptions: {
duration: 1000,
fill: "forwards"
}
});
return <div className="box" ref={ref} />;
};
styles are only for the flex container and to get the boxes to have size, background, and position relative.
Here's a codesandbox with the issue
I expect the animation to be calculated once the first time the hook runs, then be executed, and finish, instead of looping.
Placing the arguments inside a ref and using that fixes the looping:
const AnimatedBox = () => {
const options = useRef({
keyframes: [{ top: "0%" }, { top: Math.random() > 0.5 ? "50%" : "100%" }],
animationOptions: {
duration: 1000,
fill: "forwards"
}
});
const { ref } = useWebAnimations(options.current);
return <div className="box" ref={ref} />;
};
Should the hook do this internally? Or should this workaround be added to the doc?
There appears to be no canceling of the animation when the component unmounts. onFinished
will still fire, even if the component has been removed from the DOM by React. The internals of use-web-animation
also perform state updates during the animation cycle, which causes a warning from react.
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
JustWaitAnim@https://zzit3.csb.app/src/App.js:27:58
I've traced this into the code, and I believe the warning originates from checking then setting the animation state:
use-web-animations/src/useWebAnimations.ts
Lines 133 to 134 in f49256d
Codesandbox link: https://codesandbox.io/s/use-web-animations-no-state-unmounted-component-bug-zzit3?file=/src/App.js
Personally, I didn't expect the onFinish
callback to fire if unmounted! But I understand why it does. Mostly, I don't think the hook should trigger React warnings.
I see a few possibilities:
autocancel
?) that will call animation.cancel()
, as well as cancel any checking and callback execution.I ran into this bug because I was controlling the autoplay
option with a component property. This actually resulted in the animation running twice! It took me several hours before I found the issue: use-web-animations
uses these values to know whether to apply the animation. Thus, it assumed that it was a new animation configuration and should be applied. I was using the onFinish
callbacks to trigger logic updates, which caused many bugs due to firing twice!
Thank you for making use-web-animations
! I think it's a really neat library!
Adding a pseudo element attribute to the options
object - similar to the actual native api
Ex: https://css-tricks.com/pseudo-elements-in-the-web-animations-api/
When instantiating a new web animations object, there should be an option to define a pseudo element you'd like to target
const { ref, playState } = useWebAnimations({
keyframes: {
transform: "translateX(500px)",
background: ["red", "blue", "green"],
},
timing: {
delay: 500,
duration: 1000,
iterations: 2,
direction: "alternate",
easing: "ease-in-out",
},
pseudoElement: '::after', // NOTE: this flag here would be added
// ... More useful options ...
});
I don't think there is another way to handle this? The only alternative is to animate another element and change the markup to work well with the added element
An example where this comes up is when trying to create toast messages after clicking on an element
For example - this toast message after clicking on a copy to clipboard
button is often just a pseudo element:
So being able to keep the styling for this toast message inside the pseudo element CSS, while being able to express animation logic with useWebAnimations
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.