-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
What's new in Anime.js V4
Important
This change log is outdated and many new features have been added since I wrote this. But it still properly reflects the direction of v4 and the overall feel of the new API I'm not sure if I'll find the time to update it before 4.0.0.
Anime.js V4 is currently available for GitHub Sponsors only.
- π’ New feature
- π Breaking change
A major focus of V4 development has been on improving performances and lowering the memory usage. Benchmarks on my 2019 Intel MacBook Pro show a consistent 60 fps when animating 3K DOM elements' CSS transform positions (equivalent to 6K tweens) as well as 50K three.js Instanced Mesh position values (or 100K tweens).
The tween system has undergone a revamp to better handle overlapping animations. This addresses a multitude of issues, particularly when multiple animations affect the same property on the same target.
Blend multiple animations together using the new composition: 'add'
parameter.
The library now comes with built-in types, thanks to internal use of JSDoc type annotations.
More than 300 tests have been written to minimize the introduction of bugs and to make the development of new features easier.
A GUI interface to inspect and speed-up your animation workflow.
Every anime.js feature is now available as an ES Module.
This makes tree shaking more effectiveβyou only have to include the parts of the library you actually use.
The entire library remains lightweight, clocking in at around 10K
gzipped.
The new API looks like this:
import {
animate,
createTimer,
createTimeline,
engine,
eases,
spring,
defaults,
stagger,
utils,
svg,
} from 'animejs';
Here's a snapshot of the principal API changes for creating animations:
// V3
import anime from 'animejs';
const animation = anime({
targets: 'div',
translateX: 100,
translateY: { value: 50, ease: 'easeOutExpo' },
direction: 'alternate',
easing: 'easeOutQuad',
});
// V4
import { animate } from 'animejs';
const animation = animate('div', {
translateX: 100,
translateY: { to: 50, ease: 'outExpo' },
alternate: true,
ease: 'outQuad',
});
The targets
parameter has been replaced with a designated function parameter: animate(targets, parameters)
.
// V3
const animation = anime({ targets: 'div', translateX: 100 });
// V4
const animation = animate('div', { translateX: 100 });
You can now animate CSS variables asigned to a target just like any other property:
// Animate the value of the CSS variable '--radius'
animate('#target', { '--radius': '20px' });
The value
property of the old object syntax has been renamed to to
:
- translateX: { value: 100 }
+ translateX: { to: 100 }
In V4, creating an animation with object syntax is almost like describing the action in plain English:
animate('p', { opacity: { to: 0 } });
This is equivalent to:
animate('p', { opacity: 0 });
Use the object syntax with the to
property only when you need to define specific property parameters or use it within keyframes.
Specific property parameters example:
animate('p', {
opacity: {
to: 0,
duration: 400,
ease: 'outExpo',
},
translateX: {
to: 100,
duration: 800,
ease: 'outElastic',
},
});
Keyframes example:
animate('p', {
opacity: [{
to: 0,
duration: 300,
ease: 'inQuad',
}, {
to: 1,
duration: 300,
ease: 'outQuad',
}],
});
Animate from a specific value. For example, "animate the paragraph opacity starting from 0":
animate('p', { opacity: { from: 0 } });
Although the [from, to]
shortcut syntax is still valid in V4, you can also combine from and to properties like this:
For instance, "animate the paragraph opacity from 0 to 0.5":
animate('p', { opacity: { from: 0, to: 0.5 } });
The properties keyframes system in V4 is similar to V3. Just swap in the from
and to
values.
animate(targets, {
prop1: [
{ from: x, to: y, duration: 500, endDelay: 250 },
{ to: z, duration: 250, delay: 400 },
{ to: w, duration: 500, ease: 'out' },
],
});
Added support for hex colors with an alpha channel, e.g., '#F443'
or '#FF444433'
.
Duration is now clamped between 1e-12
and 1e12
ms.
Setting the duration to 0
results in a 1e-12
ms animation, and setting it to Infinity
results in a 1e12
ms animation (approximately 32 years).
This solves many edge cases and ensures the .progress
property always returns a valid number instead of NaN
for 0
ms duration or always 0
for infinite duration.
The easing
parameter is now ease
.
The new default easing function is 'outQuad'
instead of 'easeOutElastic(1, .5)'
.
- easing: 'easeInOutExpo',
+ ease: 'inOutExpo',
The ease
parameter no longer accepts function-based values, allowing you to pass ease functions directly.
- easing: () => myCustomEasing,
+ ease: myCustomEasing,
'spring'
has been removed from the core and needs to be imported separately.
V4 fixes some duration calculation issues related to spring animation as well.
+ import { animate, spring } from 'animejs';
animate(target, {
translateX: 100,
- easing: 'spring(1, 10, .5, 0)',
+ ease: spring(1, 10, .5, 0)
})
New 'in'
, 'out'
, 'inOut'
, 'outIn'
eases with an optional power parameter ('in(1.5)'
).
When called without a parameter, they're equivalent to the standard CSS eases like 'ease-in'
, 'ease-out'
, 'ease-in-out'
.
Power parameters can match Quad, Cubic, Quart, and Quint ease functions:
outQuad = out(2)
outCubic = out(3)
outQuart = out(4)
outQuint = out(5)
The 'linear'
ease now accepts a series of values similar to MDN's linear ease function.
'linear(0, 0.25, 75%, 1)'
Note that 'linear'
without options behaves the same as in V3 (no easing).
In V3, animations could overlap, leading to unexpected results when sharing the same targets and properties.
In V4, running animations are cut where the new one starts to avoid overlaps. This behavior can be configured using the new composition
parameter.
-
composition: 'none'
Equivalent to V3, animations can overlap. -
composition: 'replace'
New V4 default, running animations are cut and the new one takes over. -
composition: 'add'
Additive animations combine the values of running animations with the new one.
The engine will switch to composition: 'none'
when an animation with +1K targets is created for better performances.
The round
parameter has been replaced with a more flexible modifier
parameter. This allows you to define custom functions that modify an animation's numerical value just before rendering.
- round: 100
+ modifier: utils.round(2)
You can also define your own modifier functions like so:
animate(target, {
translateX: 100,
modifier: v => v % 10
});
The direction
parameter has been replaced with alternate
and reversed
parameters.
In V3, you couldn't combine 'reverse'
and 'alternate'
.
Having two separate parameters, in my opinion, is better than the 'alternate-reverse'
option of CSS animations.
// V3
const animation = anime({
targets: 'div',
translateX: 100,
direction: 'reverse',
// direction: 'alternate' // It wasn't possible to combine reverse and alternate
});
// V4
const animation = animate('div', {
translateX: 100,
reversed: true,
alternate: true,
});
In V3, the loop
parameter defined the number of iterations with a default of 1
. In V4, it defines how many times the Timer
, Animation
, or Timeline
loops, with a default of 0
.
The new beginDelay
parameter lets you set the delay time in milliseconds before a Timer
, Animation
, or Timeline
starts. It differs from the delay
parameters
The frameRate
parameter sets the number of frames per second (fps) for a Timer, Animation, or Timeline.
Use the playbackRate
parameter to control the speed of a Timer, Animation, or Timeline, either speeding up or slowing down as needed.
Callbacks have been simplified and are now prefixed with on
for better readability.
animate('targets', {
translateX: 100,
- begin: () => {},
+ onBegin: () => {},
- update: () => {},
+ onUpdate: () => {},
- change: () => {},
+ onRender: () => {},
- changeBegin: () => {},
- changeComplete: () => {},
- loopBegin: () => {},
- loopComplete: () => {},
+ onLoop: () => {},
- complete: () => {},
+ onComplete: () => {},
});
The onRender()
callback replaces change()
, changeBegin()
, and changeComplete()
.
It's called every time an Animation
or Timeline
renders something on screen.
The new callback onLoop()
replaces loopBegin()
and loopComplete()
.
The onUpdate()
callback is now influenced by the frameRate
parameter.
animate(target, {
translateX: 100,
frameRate: 30,
onUpdate: () => { /* Called at 30 fps */ }
})
V4 introduces two new control methods: .cancel()
and .revert()
.
Completely cancels an animation and frees up memory.
Cancels an animation and reverts all changes made by it, even removing inline styles.
In V3, you could create animations without targets and use them as timers:
const timer = anime({
duration: 500,
complete: () => {
// do something in 500 ms
}
});
V4 introduces a dedicated createTimer()
factory function for creating Timers:
import { createTimer } from 'animejs';
const timer = createTimer({
duration: 500,
onComplete: () => {
// do something in 500 ms
}
});
Timer
is internally the base Class
extended by Animation
and Timeline
.
This means they share all the same properties, callbacks, and controls.
The only main difference is that Timer
has a default duration
of Infinity
instead of 1000
for Animation
.
import { createTimer } from 'animejs';
const mainLoop = createTimer({
frameRate: 60,
onUpdate: () => {
// do something on every frame at 60 fps
}
});
mainLoop.pause(); // Pause the timer
mainLoop.play(); // Play the timer
On top of a lot of bug fixes and performances improvement, Timelines have received a ton of new features.
Default parameters for child animations are now defined separatly into a dedicated defaults
object.
This allow you to defines defaults children playback parameters while also configurating the playback of the timeline.
// V3
import anime from 'animejs';
const tl = anime.timeline({
ease: 'easeInOutQuad',
duration: 800
});
// V4
import { createTimeline } from 'animejs';
const tl = createTimeline({
defaults: {
ease: 'inOutQuad',
duration: 800
}
});
The loop
, alternate
, and reversed
options now work for child animations and can be combined with the timeline parameters. In the example below, '#target'
will alternate its translateX
value between 0 and 100 six times (3 x 2 loops).
import { createTimeline } from 'animejs';
const tl = createTimeline({
loop: 2 // The entire timeline will loop twice
});
tl.add('#target', {
translateX: 100,
loop: 3, // This particular animation will loop three times within each timeline loop
alternate: true
});
You can add timers to your timeline by omitting the target parameter in the .add()
method.
import { createTimeline } from 'animejs';
const tl = createTimeline();
tl.add({ duration: 500, onComplete: () => {} })
.add({ duration: 100, onUpdate: () => {} }, '-=200')
Labels can now be added to your timeline to define time positions. Just pass a string label and time position to the .add()
function.
import { createTimeline } from 'animejs';
const tl = createTimeline();
tl.add('LABEL NAME 01', 100)
.add('LABEL NAME 02', 400)
.add('#target', { translateX: 100 }, 'LABEL NAME 01')
.add('LABEL NAME 03', '+=100') // Labels can also be defined later, like any other child
.add('#target', { translateX: 200 }, 'LABEL NAME 02')
.add('#target', { rotate: 50 }, 'LABEL NAME 03')
You can add function to your timeline by simply passing a function as only argument to the .add()
method.
import { createTimeline } from 'animejs';
const tl = createTimeline();
tl.add({ duration: 500 })
.add(() => { /* Do something */ }, '-=200')
V4 introduces new ways to specify the time position of timeline children:
-
'<='
: At the end of the last child animation -
'<-=100'
: 100ms before the end of the last child -
'<=+100'
: 100ms after the end of the last child -
'<<'
: At the beginning of the last child -
'<<-=100'
: 100ms before the beginning of the last child -
'<<+=100'
: 100ms after the beginning of the last child -
'LABEL_NAME-=100'
: 100ms before the label's time position -
'LABEL_NAME+=100'
: 100ms after the label's time position
import { createTimeline } from 'animejs';
const tl = createTimeline();
tl.add('#target', { translateX: 100, duration: 800 })
.add('#target', { translateX: 200, duration: 400 }, '<<')
.add('#target', { translateX: 100 }, '<=')
.add('LABEL NAME 03', '+=100')
.add('#target', { translateX: 200 }, 'LABEL NAME 02')
.add('#target', { rotate: 50 }, 'LABEL NAME 03')
V4 also exposes the .set()
utility method directly within timelines.
import { createTimeline } from 'animejs';
const tl = createTimeline();
tl.add('#target', { translateX: 100 })
.set('#target', { translateX: 0 }) // Instantly sets the translateX value to 0
Easily stagger multi-target animations within a timeline.
import { createTimeline } from 'animejs';
const onComplete = $el => console.log($el);
const tl = createTimeline();
- tl.add('.target:nth-child(1)', { opacity: 0, onComplete }, 0)
- .add('.target:nth-child(2)', { opacity: 0, onComplete }, 100)
- .add('.target:nth-child(3)', { opacity: 0, onComplete }, 200)
- .add('.target:nth-child(4)', { opacity: 0, onComplete }, 300)
+ tl.add('.target', { opacity: 0, onComplete }, stagger(100))
The stagger value now accepts units.
import { animate, stagger } from 'animejs';
animate('.elements', { translateX: stagger('1rem') });
// 0rem, 1rem, 2rem, 3rem, 4rem
The stagger direction
parameter has been renamed to reversed
to better align with the new playback API.
import { animate, stagger } from 'animejs';
animate('.elements', { translateX: stagger('1rem', { reversed: true }) });
// 4rem, 3rem, 2rem, 1rem, 0rem
In V4, all SVG utility functions are exposed by the svg
module.
import { svg } from 'animejs';
This method enables morphing between two shapes, even if they have a different number of points.
svg.morphTo(shapeTarget, precision);
The optional precision
parameters allows you to define the quality of the morphing.
The higher the better. If your two shapes have exactly the same number of points, passing 0
will not perform any points extrapolations and use the shapes points directly.
// V3
import anime from 'animejs';
anime({
targets: '#shape',
points: '70 41 118.574 59.369 111.145 132.631 60.855 84.631 20.426 60.369',
});
// V4
import { animate, svg } from 'animejs';
animate('#shape1', { points: svg.morphTo('#shape2') });
This replaces anime.setDashoffset
from V3 and adds the ability to define the direction of the line drawing animation.
- strokeDashoffset: [anime.setDashoffset, 0],
+ strokeDashoffset: svg.drawLine(),
import { animate, svg } from 'animejs';
animate('path', {
strokeDashoffset: [
{ to: svg.drawLine('in') },
{ to: svg.drawLine('out') },
],
});
This replaces anime.path
from V3.
// V3
import anime from 'animejs';
const path = anime.path('path');
anime({
targets: '#target',
translateX: path('x'),
translateY: path('y'),
rotate: path('angle'),
});
// V4
import { animate, svg } from 'animejs';
const { x, y, angle } = svg.createMotionPath('path');
animate('#target', {
translateX: x,
translateY: y,
rotate: angle,
});
The new utils
module combines all V3 utility functions, exposes several internal functions that were previously unavailable and add new ones.
import { utils } from 'animejs';
This replaces the V3 anime.get()
function.
Returns an array of targets from an Object
/ String
/ DOMNode
/ NodeList
.
const $target = utils.get('#target'); // [$el];
const $targets = utils.get('.target'); // [$el, $el, $el, $el, $el];
Passing both target
and property
returns the current property value of the specified target.
const width = utils.get('#target', 'width'); // '80px'
Specifying a unit as the third parameter returns the property value in that unit.
const widthInEm = utils.get('#target', 'width', 'em'); // '5em'
Passing false
as the third parameter returns the value as a Number
.
const widthNumber = utils.get('#target', 'width', false); // 80
This is the new version of the V3 anime.set()
.
utils.set('#set .demo .square', {
width: 80, // Will set the value in px
height: '1rem', // Specify unit
translateX: stagger('3rem', { start: 5, reversed: true, ease: 'in' }),
color: 'var(--hex-red)', // Non-numeric values allowed
});
This replaces the V3 anime.remove()
and lets you specify which Animation or Timeline to remove the target from.
utils.remove(targets, Animation | Timeline);
Removes all inline styles added by an Animation or Timeline.
utils.cleanInlineStyles(Animation | Timeline);
In V4, animations no longer automatically return promises. Use utils.promisify()
to enable this.
- anime({ targets: target, prop: x }).finished.then(doSomething);
+ utils.promisify(animate(target, { prop: x })).then(doSomething);
This function now allows you to specify the number of decimals for the returned value.
utils.random(0, 100); // 45
utils.random(0, 100, 2); // 45.39
Picks a random item from an Array
, NodeList
, or String
.
utils.randomPick(Array | NodeList | String);
Replaces the V3 round
animation parameter.
The decimalLength
parameter now accept the number of decimals and is mandatory.
utils.round(value, decimalLength);
Clamps a Number
between specified min and max values.
utils.clamp(value, min, max);
Snaps a Number
to the nearest specified increment.
utils.snap(value, increment);
Wraps a Number
between a defined min and max range.
utils.wrap(value, min, max);
Maps a Number
from one range to another.
utils.map(value, fromLow, fromHigh, toLow, toHigh);
Interpolates between two numbers based on given progress.
utils.interpolate(start, end, progress);
Rounds a value to a specified decimal length and pads with zeros if needed.
utils.roundPad(value, decimalLength);
Pads a Number
from the start with a string until it reaches a given length.
utils.padStart(value, totalLength, padString);
Pads a Number
from the end until it reaches a given length.
utils.padEnd(value, totalLength, padString);
Performs linear interpolation between two values.
utils.lerp(start, end, amount);
Chain multiple transformations from the utils
object. Each function in the chain processes the output from the previous function as its primary input.
const chainedFunction = utils.func1(params).func2(params);
chainedFunction(value);
These are all the chain-able functions exposed by utils
:
clamp
round
snap
wrap
mapRange
interpolate
lerp
roundPad
padStart
padEnd
import { engine } from 'animejs';
The engine
is the core of Anime.js. It controls the playback of all Timers, Animations, and Timelines.
When set to false
, this property tells the engine to not use its built-in main loop. This is especially useful if you want to integrate Anime.js with your custom main loop.
Example using Three.js built-in renderer animation loop:
engine.useDefaultMainLoop = false; // Stops Anime.js from using its built-in loop
const render = () => {
engine.tick(); // Manually advance the Anime.js engine
renderer.render(scene, camera); // Render the Three.js scene
};
renderer.setAnimationLoop(render); // Invoke Three.js built-in animation loop
This replaces the less useful V3 method animation.tick(time)
.
When set to false
, this prevents the engine from automatically pausing when the browser tab is hidden.
Replaces the V3 global property anime.suspendWhenDocumentHidden
.
Get and set the global frame rate for animations. Note: individual animations cannot exceed this global frame rate.
This property controls the global playback rate for all running animations. Individual animations will have their playback rates adjusted relative to this global rate.
Replaces the V3 global property anime.speed
.
This function allows you to manually advance the engine when engine.useDefaultMainLoop
is set to false
.
Returns a Number
.
Pauses all running animations by stopping the built-in main loop. This has no effect if engine.useDefaultMainLoop
is set to true
.
Returns self
.
Resumes the built-in main loop and adjusts the current times of all paused animations accordingly. Has no effect if engine.useDefaultMainLoop
is set to true
.
Returns self
.
You can now modify Anime.js default parameters by overriding the properties of the defaults
object.
import { defaults } from 'animejs';
defaults.playbackRate = 1;
defaults.frameRate = 120;
defaults.loop = 0;
defaults.reversed = false;
defaults.alternate = false;
defaults.autoplay = true;
defaults.beginDelay = 0;
defaults.duration = 1000;
defaults.delay = 0;
defaults.endDelay = 0;
defaults.ease = 'outQuad';
defaults.composition = 0;
defaults.modifier = v => v;
defaults.onBegin = () => {};
defaults.onUpdate = () => {};
defaults.onRender = () => {};
defaults.onLoop = () => {};
defaults.onComplete = () => {};
Anime.js V4 is currently available for GitHub Sponsors only.