requestAnimationFrame
.Example
import animare, { ease } from 'animare';
const ball = document.getElementById('ball');
animare({
to: [100, 100],
duration: 2000, // 🕐
ease: [ease.linear, ease.out.bounce], // 📉
},
([x, y]) => {
ball.style.left = x + '%';
ball.style.top = y + '%';
});
Install using your package manager of choice to add animare to your project:
npm install animare
yarn add animare
Aaaaand, you’re done! That was fast. 🐇
Syntax: animare(animationOptions, (animatedValues, animationInfo) => void)
animare
is built to be a wrapper around requestAnimationFrame
to make it easier to use.
With it, you can animate anything that has a number value (scroll position, color, text, etc).
Each animated value has its option (duration, delay, ease, etc)
import animare, { ease } from 'animare';
const element = document.getElementById('element');
animare(
{
from: [0, 0.5], // start values as [opacity, scale]
to: [1, 1], // end values as [opacity, scale]
duration: [1000, 500], // duration in ms for each value
ease: ease.out.quart, // applies on all values
},
// values names can be anything
([opacity, scale], { isFinished }) => {
element.style.opacity = opacity;
element.style.transform = `scale(${scale})`;
if (isFinished) {
// do something when the animation is finished
}
}
);
to: number | number[]; // required
End value\s of the animation.
from?: number | number[] | ((index: number) => number);
Start value\s of the animation.
If a single number
is passed, it will be used as the default for all animated values.
If number[]
is passed, make sure it has the same length as to.
delay?: number | number[] | ((index: number) => number);
Delay before the animation starts in milliseconds.
If a single number
is passed, it will be used as the default for all animated values.
delayOnce?: boolean | boolean[];
Apply the delay only once, in case of repeating animations.
If a single boolean
is passed, it will be used as the default for all animated values.
duration?: number | number[] | ((index: number) => number);
The duration of the animation's value/s is in milliseconds.
If a single number
is passed, it will be used as the default for all animated values.
type directionT = "normal" | "reverse" | "alternate" | "alternate-reverse";
direction?: directionT | directionT[];
Animation's direction for each value/s.
If a single string
is passed, it will be used as the default for all animated values.
repeat?: number | number[] | ((i: number) => number);
Repeats count after the first play.
If a single number
is passed, it will be used as the default for all animated values.
for infinite ♾️ repeats use -1
.
ease?: ((x: number) => number) | ((x: number) => number)[];
Easing functions specify the rate of change of the number over time.\
Info check out the
ease
plugin.
autoPlay?: boolean;
Play the animation automatically on initialization.
type?: "wait" | "immediate";
Playing behavior of the next animation in the timeline. when using .next()
.
wait
- wait for all animated values in the previous animations to be completed.
immediate
- play the next animated value immediately after the previous one is finished.
A function will be fired on every frame update and contains the animation's logic.
Two parameters will be passed to the callback function:
1st param:
Array of the animated values, the same length as to property.
2nd param:
An object of information about the animation.
const onUpdate = ([v1, v2, v3 ...], { isFinished, fps, ... }) => {
// Animation logic here
}
isFirstFrame: boolean
True only at the first frame of the animation.
isFinished: boolean
True only when the animation is finished.
progress: number[]
The progress of each animated value is an array of numbers between 0
and 1
.
Resets on every repeat cycle.
timelineProgress: number
Overall animation's progress includes repeats, delays, and timeline repeats.
Returns -1
if the timeline ♾️ infinitely repeats.
timelineIndex: number[]
The current timeline index of each animated value.
repeatCount: number[]
A descending number represents the current repeat cycle of each animated value.
timelineRepeatCount: number[]
A descending number represents the current timeline's repeat cycle of each animated value.
alternateCycle: number[]
The current alternate cycle (1 or 2)
of each animated value.
For directions 'alternate'
or 'alternate-reverse'
fps: number
The current refresh rate.
time: number
The progress time in milliseconds of the overall animation.
play()
Start playing the animation in the direction
direction.
If the animation is already playing or paused, it will be restarted.
Note
play()
will match thedirection
property.
reverse()
Start playing the animation in the reverse direction
direction.
If the animation is already playing or paused, it will be restarted.
Note
reverse()
will reverse thedirection
property.
pause()
Pause the animation if it is playing at the moment.
resume()
Resume the animation if it is paused.
If the animation is not paused, it will be played from the beginning.
stop(stopAtStart = true)
Stop the animation at the beginning or the end.
Warning the animation will not be stopped immediately, it may take 1 or 2 frames to stop.
setOptions(animationOptions, animationIndex?: number) => void
Change the animation's initial options for a specific animation in the timeline.
Warning setting new options while the animation is playing sometimes may cause unexpected results.
getOptions(animationIndex?: number) => animationOptions
Get the animation's current options object for a specific animation in the timeline.
setTimelineOptions({ repeat?: number, speed?: number }) => void
Set or change the timeline's initial options.
Sometimes you need to do multi-step animation, you can do that using the .next()
method.
.next()
uses the same callback with updated options.
If from is not specified, it will pick up the value from the previous animation.
Note
duration, ease, and type
properties are inherited from the previous animation.
Tip If you have multiple values with different durations and delays some of them may finish before the others. you can customize the timeline playing behavior by using the
type
property.
Warning using
repeat: -1
♾️ property in one of the animations in the timeline will block the next one from playing.
import animare, { ease } from 'animare';
const ball = document.getElementById('ball');
const myAnimation = animare(
{
to: [100, 100],
duration: 2000, // 🕐
ease: [ease.linear, ease.out.bounce], // 〽️
},
([x, y]) => {
ball.style.left = x + '%';
ball.style.top = y + '%';
}
);
myAnimation.next({ to: [100, 0], delay: 500 }); // ⏭️ play this next
myAnimation.next({ to: [0, 100], delay: 500 }); // ⏭️
myAnimation.next({ to: [0, 0], delay: 500 }); // ⏭️
myAnimation.setTimelineOptions({ repeat: -1 }); // ♾️ infinite timeline repeat
animare
provides some events out of the box.
Syntax: (callback: Function) => Function
Listen to the animation's start event.
Warning
onStart
will not be fired forautoPlay
animations.
const scrollAnimation = animare({ to: 300, autoPlay: false }, ([scroll]) => {
window.scroll({ top: scroll });
});
// will be called every time the animation starts
const unsubscribe = scrollAnimation.onStart(() => {
console.log('start scrolling');
});
// somewhere else in your code
scrollAnimation.start(); // 🚀 start the animation
unsubscribe(); // 🛑 stop listening
Syntax: (at: number, callback: Function) => Function
Will be fired every time the animation reaches specific progress.
Warning
onProgress
will not be fired in case of ♾️ infinite repeats.
const scrollAnimation = animare({ to: 300 }, ([scroll]) => {
window.scroll({ top: scroll });
});
// will be called every time the animation reaches half of the duration
const unsubscribe = scrollAnimation.onProgress(0.5, () => {
console.log('half way');
});
unsubscribe(); // 🛑 stop listening
Syntax: (at: number) => Promise
Wait for the animation to reach specific progress.
Warning
asyncOnProgress
will not be fired in case of ♾️ infinite repeats.
// inside async function
const scrollAnimation = animare({ to: 300 }, ([scroll]) => {
window.scroll({ top: scroll });
});
// wait for the animation to reach 50%
await scrollAnimation.onProgress(0.5, () => {
console.log('half way');
});
Syntax: (callback: Function) => Function
Listen to the animation's finish event.
Syntax: () => Promise
Wait for the animation to finish.
React users can use the hook useAnimare
by importing it from animare/react
. This hook returns the animation's object to use in your React component.
import animare, { ease } from 'animare';
import { useAnimare } from 'animare/react';
function MyComponent() {
const ballAnimation = useAnimare(() => {
const ball = document.getElementById('ball');
const options = {
to: 1,
duration: 1000,
ease: ease.out.quad,
autoPlay: false,
};
const onUpdate = ([scale]) => {
ball.style.transform = `scale(${scale})`;
};
return animare(options, onUpdate);
});
const onClick = () => {
ballAnimation?.play();
};
// ...
}
animare
comes with a few predefined easing functions.
Info Check out
easings.net
to learn more about the available easing functions.
Tip Use
animare-ease-visualizer
tool to create custom easing functions.
Warning
ease.custom
accepts only strings generated byanimare-ease-visualizer
tool.
import { ease } from 'animare'
ease.linear // default
// ease in ease out ease in-out
ease.in.bounce ease.out.bounce ease.inOut.bounce
ease.in.circ ease.out.circ ease.inOut.circ
ease.in.cubic ease.out.cubic ease.inOut.cubic
ease.in.elastic ease.out.elastic ease.inOut.elastic
ease.in.expo ease.out.expo ease.inOut.expo
ease.in.sine ease.out.sine ease.inOut.sine
ease.in.quad ease.out.quad ease.inOut.quad
ease.in.quart ease.out.quart ease.inOut.quart
ease.in.quint ease.out.quint ease.inOut.quint
ease.in.back(c: number) ease.out.back(c: number) ease.inOut.back(c: number)
ease.in.poly(n: number) ease.out.poly(n: number) ease.inOut.poly(n: number)
ease.in.wobble(bounciness = 1) ease.out.wobble(bounciness = 1) ease.inOut.wobble(bounciness = 1)
// ⚠️ The spring easing function will only look smooth at certain parameters.
ease.spring({
mass?: number,
stiffness?: number,
damping?: number,
velocity?: number,
duration?: number
});
ease.steps(stepsNumber?: number, start?: true);
ease.cubicBezier(X1: number, Y1: number, X2: number, Y2: number); // same as CSS
// ⚠️ accepts only strings generated by `animare-ease-visualizer` tool.
ease.custom(d: string); // d is SVG's path attribute
// custom easing functions from an array of generated points.
ease.fromPoints(array: Float32List);
The more you have animated values, the more it gets difficult to know what is what.
// long-length animated values are hard to read and maintain.
import animare from 'animare';
animare(
{
from: [23, 25, 35, 34, 4, 45, 34, 45, 43, 45, 341, 34, 545],
to: [243, 22, 55, 226, 24, 61, 34, 45, 15, 45, 56, 13, 114],
duration: [500, 1000, 200, 2000, 1500, 500, 600, 700, 900, 1100, 360, 550, 400],
delay: [50, 100, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700],
},
([a, b, c, d, e, f, g, h, i, j, k, l, m]) => {
// ...
}
);
organize
is a utility function that helps you to organize your animated values.
organize
also handles color animations.
import animare, { organize } from 'animare';
const options = organize({
x: { from: 20, to: 100, duration: 1500 },
y: { from: 10, to: 200, duration: 1000 },
width: { from: 200, to: 500, duration: 1000 },
height: { from: 150, to: 300, duration: 1000 },
color: { from: 'red', to: 'orange', duration: 1000 },
rotate: { to: 360, duration: 1000 },
scale: { from: 1, to: 2 },
});
animare(
{
from: options.from,
to: options.to,
duration: options.duration,
},
(values, { progress }) => {
const { x, y, width, height, color, rotate, scale } = options.get(values);
// get the progress of rotate.
const rotateProgress = progress[options.indexOf('rotate')];
// ...
}
);
Colors are made of numbers rgb(255, 0, 0)
so you can animate them by animating each color's channel RGB.
But sometimes you want to animate a hex color #ff0000
or even a named color red
.
For that, you can use colorToArr
plugin to convert a color to an array of numbers.
Tip
organize
plugin can handle color animations for you.
import animare, { colorToArr } from 'animare';
const element = document.getElementById('element');
animare(
{
from: colorToArr('red'), // ➡️ [255, 0, 0]
to: colorToArr('#ff00ff'), // ➡️ [255, 0, 255]
duration: 1000,
},
([r, g, b]) => {
element.style.backgroundColor = `rgb(${r}, ${g}, ${b})`;
}
);