Issue
The JS Animation is using the built in JS Animate function https://developer.mozilla.org/en-US/docs/Web/API/Element/animate
myRef.current.animate(
[
{transform: `translatey(32.5px)`, offset: 0},
{transform: `translatey(-2260px)`, offset: 0.9},
{transform: `translatey(-2260px)`, offset: 0.95},
{transform: `translatey(-2247.5px)`, offset: 1}
],
{
duration: 4000,
easing: 'cubic-bezier(0.33, 1, 0.68, 1)',
fill: 'forwards',
iterations: 1,
}
)
vs
.test {
animation: 4s cubic-bezier(0.33, 1, 0.68, 1) forwards spin1;
}
@keyframes spin1 {
0% {
transform: translatey(32.5px);
}
90% {
transform: translatey(-2260px);
}
95% {
transform: translatey(-2260px);
}
100% {
transform: translatey(-2247.5px);
}
}
Expected same result from both but for some reason the JS one snaps onto the offsets, while the CSS one is smoother and doesn't snap.
Solution
This is because in CSS, the animation-timing-function
affects each KeyFrame
instead of the whole animation:
.anim {
height: 300px;
background: pink;
border: 2px solid purple;
animation: 8s cubic-bezier(0, -1, 1, 0) forwards anim;
}
@keyframes anim {
0% { width: 50px }
25% { width: 150px }
50% { width: 250px }
75% { width: 350px }
100% { width: 450px }
}
Here you can see the <code>animation-timing-function</code> has been applied 4 times.
<div class="anim"></div>
The easing
property you've set in your KeyframeEffect
option object is the one that applies to the entire iteration duration of the keyframe effect. This doesn't apply on each KeyFrame
, but on the whole animation:
const el = document.querySelector(".anim");
el.animate(
[
{ width: "50px" },
{ width: "150px" },
{ width: "250px" },
{ width: "350px" },
{ width: "450px" },
],
{
duration: 8000,
fill: "forwards",
easing: "cubic-bezier(0, -1, 1, 0)",
},
);
.anim {
height: 300px;
background: pink;
border: 2px solid purple;
}
Here you can see the <code>easing</code> timing function has been applied only once.
<div class="anim"></div>
To get the same effect as with CSS animation-timing-function
, you need to set it to all your KeyFrame
objects.
const el = document.querySelector(".anim");
el.animate(
[
{ width: "50px", easing: "cubic-bezier(0, -1, 1, 0)" },
{ width: "150px", easing: "cubic-bezier(0, -1, 1, 0)" },
{ width: "250px", easing: "cubic-bezier(0, -1, 1, 0)" },
{ width: "350px", easing: "cubic-bezier(0, -1, 1, 0)" },
{ width: "450px", easing: "cubic-bezier(0, -1, 1, 0)" },
],
{
duration: 8000,
fill: "forwards"
},
);
.anim {
height: 300px;
background: pink;
border: 2px solid purple;
}
Here you can see the <code>easing</code> timing function has been applied 4 times.
<div class="anim"></div>
Or to avoid repeating many times the same values:
const el = document.querySelector(".anim");
el.animate(
{
width: [ "50px", "150px", "250px", "350px" ],
easing: [ "cubic-bezier(0, -1, 1, 0)" ],
},
{
duration: 8000,
fill: "forwards"
},
);
.anim {
height: 300px;
background: pink;
border: 2px solid purple;
}
Here you can see the <code>easing</code> timing function has been applied 4 times.
<div class="anim"></div>
Answered By - Kaiido
Answer Checked By - - Senaida (ReactFix Volunteer)