# Animation

Note

The following functions are in the vjmap namespace. Remember to prefix with vjmap before execution, e.g. vjmap.createAnimation

# Animation

animate executes keyframe or spring animations.

createAnimation({
  from: 0,
  to: 100,
  onUpdate: latest => console.log(latest)
})
1
2
3
4
5

It can animate numbers:

createAnimation({ from: 0, to: 100 })
1

Or strings of the same type:

createAnimation({ from: "0px", to: "100px" })
createAnimation({ from: "#fff", to: "#000" })
1
2

Strings can be quite complex, such as box shadows or SVG path definitions. The only limitation is that the numbers and colors they contain must be in the same order:

createAnimation({
  from: "0px 0px 0px rgba(0, 0, 0, 0)",
  to: "10px 10px 0px rgba(0, 0, 0, 0.2)"
})
1
2
3
4

The animation type will be auto-detected from the provided options, or you can manually select by defining type as "keyframes", "spring", or "decay".

# Options

These options can be set for all animations:

# from

Initial value to start the animation.

Defaults to 0

createAnimation({
  from: "linear-gradient(#e66465, #9198e5)",
  to: "linear-gradient(#9198e5, #e66465)"
})
1
2
3
4

# elapsed

Set initial elapsed time in milliseconds. Use negative values for delay.

createAnimation({
  to: 100,
  elapsed: -300
})
1
2
3
4

# repeat

Number of times to repeat the animation. Set to Infinity to repeat forever.

createAnimation({
  to: 100,
  repeat: 2
})
1
2
3
4

# repeatDelay

Duration to wait before repeating the animation, in milliseconds.

createAnimation({
  to: 100,
  repeat: 2,
  repeatDelay: 200
})
1
2
3
4
5

# repeatType

Either "loop", "mirror" or "reverse". Default is "loop".

  • "loop": Repeat animation from 0.
  • "mirror": Alternately swap from/to values.
  • "reverse": Alternately reverse the animation.
createAnimation({
  to: 100,
  repeat: 2,
  repeatType: "reverse"
})
1
2
3
4
5

# type

animate will auto-detect the animation type from the provided options. You can also manually select a specific type by defining type as "keyframes", "spring", or "decay".

createAnimation({
  to: 100,
  type: "spring"
})
1
2
3
4

# Lifecycle Events

The following lifecycle events are available for all animations:

# onUpdate

Called every frame as the animation triggers with the latest computed value.

createAnimation({
  to: 100,
  onUpdate: latest => console.log(latest)
})
1
2
3
4

# onPlay

Called when the animation starts. Currently, this runs automatically when animate is called.

createAnimation({
  to: 100,
  onPlay: () => {}
})
1
2
3
4

# onComplete

Called when the animation completes successfully.

createAnimation({
  to: 100,
  onComplete:() => {}
})
1
2
3
4

# onRepeat

Called when the animation repeats.

createAnimation({
  to: 100,
  repeat: 2,
  onRepeat: () => {}
})
1
2
3
4
5

# onStop

Called when the animation is stopped by the stop control.

const animation = createAnimation({
  to: 100,
  onStop: () => {}
})

animation.stop()
1
2
3
4
5
6

Keyframe Options

Keyframe animation is the default type and can be defined with from and to options:

createAnimation({ from: 0, to: 100 })
1

Or as a series of keyframes provided to the to option:

createAnimation({ to: [0, 100, 200] })
1

# to

Single value to animate, or array of values to animate.

createAnimation({
  to: ["#0ff", "#f00", "#0f0"]
})
1
2
3

If to is an array, any defined from will be ignored.

# duration

Defines the animation duration in milliseconds.

createAnimation({
  to: 100,
  duration: 300
})
1
2
3
4

# ease

An easing function or array of functions for easing between each keyframe.

nimate({
  to: 100,
  ease: linear
})

createAnimation({
  to: ["#fff", "#000", "#f00"],
  ease: [linear, easeInOut]
})
1
2
3
4
5
6
7
8
9

If set to an array, the length of this array must be one less than the number of values being animated between.

# offset

An array of values between 0 and 1 that defines at which point in the overall animation each keyframe should be reached.

The array length should match the number of defined keyframes.

createAnimation({
  to: ["#fff", "#000", "#f00"],
  offset: [0, 0.2, 1]
})
1
2
3
4

# Spring Options

Springs are great for creating natural-feeling interfaces and interruptible dynamic animations.

Spring animation will be used if any of the stiffness, damping, or mass options are detected.

Note: Spring simulation is inherently numerical, so if given a color, array, or object, it will run the animation from 0 to 100 and interpolate to the given value.

# to

Single value to animate.

createAnimation({
  to: 100,
  type: "spring"
})
1
2
3
4

If to is an array, any defined from will be ignored.

# stiffness

Defines the stiffness of the spring. Higher stiffness produces faster animation.

Default is 100

createAnimation({
  to: 100,
  stiffness: 1000
})
1
2
3
4

# damping

This is the counterforce to stiffness. When you decrease this value relative to stiffness, the spring becomes more elastic and the animation lasts longer. Similarly, higher relative values will have less elasticity and result in shorter animation.

Default is 10

createAnimation({
  to: 100,
  damping: 50
})
1
2
3
4

# mass

This is the mass of the animated object. Heavier objects take longer to accelerate and decelerate.

Default is 1.

createAnimation({
  to: 100,
  mass: 2
})
1
2
3
4

# velocity

Initial velocity of the animation, in units per second.

createAnimation({
  to: 100,
  velocity: 1000
})
1
2
3
4

# duration

Duration of the spring in milliseconds.

Will be overridden by stiffness, mass, or damping.

createAnimation({
  to: 100,
  duration: 1000
})
1
2
3
4

# bounce

Bounciness of the spring, as a value between 0 and 1, where 0 has no bounce.

Will be overridden by stiffness, mass, or damping.

createAnimation({
  to: 100,
  bounce: 0.2
})
1
2
3
4

# restDelta

Distance from the animation target at which the animation can be considered complete. Animation completes when both restDelta and restSpeed are satisfied.

createAnimation({
  to: 100,
  restDelta: 0.5
})
1
2
3
4

# restSpeed

Absolute velocity in units per second below which the animation can be considered complete. Animation completes when both restDelta and restSpeed are satisfied. Default is 10.

js createAnimation({ to: 100, restSpeed: 5 })

# Playback Control

createAnimation returns PlaybackControls, which can be used to control animation playback.

Currently this only includes a stop method, but may be extended with more.

# stop

Stop the animation.

const playback = createAnimation({ from: 0, to: 100 })
playback.stop()
1
2

# inertia

The inertia animation is used to gradually decelerate a number.

# Options

In addition to from, onUpdate, and onComplete, inertia also supports:

# velocity

Initial velocity of the animation, in units per second.

inertia({
  from: 0,
  velocity: 100
})
1
2
3
4

# power

Constant used to calculate the target value. Higher power = further target.

Default is 0.8.

inertia({
  from: 0,
  power: 0.3
})
1
2
3
4

# timeConstant

Adjusting the time constant changes the deceleration duration.

Default is 350.

inertia({
  from: 0,
  velocity: 100,
  timeConstant: 400
})
1
2
3
4
5

# modifyTarget

A function that receives the calculated target and returns a new target. Used to snap the target to a grid.

const roundToNearest = target => v => Math.ceil(v / target) * target

inertia({
  from: 0,
  velocity: 100,
  modifyTarget: roundToNearest(100)
})
1
2
3
4
5
6
7

# min

Minimum value. The animation will switch from gradual deceleration and use spring animation to snap to this point.

inertia({
  from: 50,
  velocity: -100,
  min: 0
})
1
2
3
4
5

# max

Maximum value. The animation will switch from gradual deceleration and use spring animation to snap to this point.

inertia({
  from: 50,
  velocity: 100,
  max: 100
})
1
2
3
4
5

# bounceStiffness

Defines the stiffness of the spring when the animation hits min or max. Higher stiffness produces faster animation.

Default is 500

inertia({
  from: 0,
  velocity: 100,
  max: 50,
  bounceStiffness: 1000
})
1
2
3
4
5
6

# bounceDamping

This is the counterforce to bounceStiffness. When you decrease this value relative to bounceStiffness, the spring becomes more elastic and the animation lasts longer. Similarly, higher relative values will have less elasticity and result in shorter animation.

Default is 10

inertia({
  from: 0,
  velocity: 100,
  max: 50,
  bounceDamping: 300
})
1
2
3
4
5
6

# restDelta

Distance from the animation target at which the animation can be considered complete.

inertia({
  from: 0,
  velocity: 100,
  restDelta: 0.5
})
1
2
3
4
5

# Iterators

Iterators let you run animations with a high degree of control.

Each can be initialized with the matching options above (decay uses a subset of inertia's options, excluding bounce- options):

const animation = spring({
  from: 0,
  to: 100,
  stiffness: 200
})
1
2
3
4
5

Using the returned iterator, you can resolve the animation at a specific timestamp with its next method.

// Resolve animation at 200 milliseconds
const { value, done } = animation.next(200)
1
2

# Easing

Built-in easing functions

# Functions

Each easing function can be imported like this:

Each function accepts a progress value between 0 and 1 and returns a new value:

const progress = 0.5
const easedProgress = easeInOut(progress)
1
2
  • linear
  • easeIn
  • easeInOut
  • easeOut
  • circIn
  • circInOut
  • circOut
  • backIn
  • backInOut
  • backOut
  • anticipate
  • bounceIn
  • bounceInOut
  • bounceOut

# cubicBezier

const easing = cubicBezier(0, .42, 0, 1)
1

# steps

steps returns an easing function that converts the animation into a series of discrete steps.

const easing = steps(5)
1

It can optionally accept a second parameter, "start" or "end" (default) to determine whether steps align with the start or end of the animation.

steps(5, "start")
1

# mirrorEasing

Mirror an existing easing function.

# reverseEasing

Reverse an existing easing function. For example, providing it easeIn returns an easeOut.

const reversed = reverseEasing(linear)
reversed(1) // 0
reversed(0.5) // 0.5
reversed(0) // 1
1
2
3
4

# createExpoIn

Create an easing function based on the provided exponential power. Higher power means stronger easing.

const expoIn = createExpoIn(4)
1

The returned easing function is an ease-in, meaning it starts slow and ends fast. mirrorEasing and reverseEasing can be used to create ease-in and ease-out variants:

const expoIn = createExpoIn(4)
const expoOut = mirrorEasing(easeIn)
const expoInOut = reverseEasing(easeIn)
1
2
3

# createBackIn

Create an easing function with overshoot. It accepts a power value; higher power means stronger overshoot.

const backIn = createBackIn(4)
1

The returned easing function is an ease-in, meaning overshoot occurs at the start of the animation. mirrorEasing and reverseEasing can be used to create ease-in and ease-out variants:

const backIn = createBackIn(4)
const backOut = mirrorEasing(easeIn)
const backInOut = reverseEasing(easeIn)
1
2
3

# createAnticipate

Create an easing that pulls back slightly before animating with overshoot. The stronger the power, the greater the overshoot.

const anticipate = createAnticipate(4)
1

# General

# angle

Returns the angle between two points in degrees.

angle(
  { x: 0, y: 0 },
  { x: 45, y: 100 }
)
1
2
3
4

# attract

attract(5, 10, 12)
1

# attractExpo

attractExpo(5, 10, 12)
1

# clamp

Clamp a value to a given range.

const min = 50
const max = 100
clamp(min, max, 150) // 100
1
2
3

# degreesToRadians

Convert degrees to radians.

degreesToRadians(45) // 0.785...
1

# distance

Returns the distance between two numbers, two 2D points, or two 3D points.

distance(10, 50)
distance({ x: 0, y: 0 }, { x: 45, y: 100 })
distance({ x: 0, y: 0, z: 100 }, { x: 45, y: 100, z: 0 })
1
2
3

# interpolate

Creates a function that interpolates from a linear number sequence to a non-linear number sequence, strings of the same number format, colors, or their arrays/objects.

const mapXToOpacity = interpolate(
  [-100, 0, 100],
  [0, 1, 0]
)
mapXToOpacity(-50) // 0.5

const mapProgressToValues = interpolate(
  [0, 1],
  [
    { x: 0, color: "#fff" },
    { x: 100, color: "#000" }
  ]
)
mapProgressToValues(0.5) // { x: 50, color: "#888" }

const rescale = interpolate(
  [0, 1],
  [100, 200],
  { clamp: false }
)
rescale(2) // 300
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Options

interpolate accepts an optional third parameter, an options object.

  • clamp: Clamp values to the given range. Default is true.
  • ease: An Easing function or array of easing functions to ease the interpolation of each segment.
  • mixer: A function that, when given from and to values, returns a new function that accepts a progress value between 0 and 1 and blends between these two values.

# isPoint

Returns true if the provided argument is a 2D point.

isPoint({ x: 0 }) // false
isPoint({ x: 0, y: 0 }) // true
1
2

# isPoint3D

Returns true if the provided argument is a 3D point.

isPoint3D({ x: 0 }) // false
isPoint3D({ x: 0, y: 0 }) // false
isPoint3D({ x: 0, y: 0, z: 0 }) // true
1
2
3

# mix

Will blend between two values, with progress given as the third parameter.

mix(0, 100, 0.5) // 50
mix(0, 100, 2) // 200
1
2

# mixColor

Returns a function that, when given a progress value, blends between two colors. Accepts hex, rgba, and hsla colors.

mixColor("#000", "#fff")(0.5) // "rgba(125, 125, 125, 1)"
1

# mixComplex

Returns a function that, when given a progress value, blends between two strings with the same number and color order.

mixComplex("100px #fff", "0px #000")(0.5) // "50px rgba(125, 125, 125, 1)"
1

# pointFromVector

Given a point, angle in degrees, and distance, returns a new point.

const point = { x: 0, y: 0 }
const angle = 45
const distance = 100

pointFromVector(point, angle, distance)
1
2
3
4
5

# progress

Given a min and max range and a value, returns the value normalized to a 0-1 progress range.

const min = 100
const max = 200
progress(min, max, 150) // 0.5
1
2
3

# radiansToDegrees

Convert radians to degrees.

radiansToDegrees(0.785) // 45
1

# snap

Creates a function that snaps numbers to the nearest position in the provided array or fixed interval.

// Snap to regular intervals
const snapTo = snap(45);

snapTo(1); // 0
snapTo(40); // 45
snapTo(50); // 45
snapTo(80); // 90

// Snap to values in an array
const snapTo = snap([-100, -50, 100, 200]);

snapTo(-200); // -100
snapTo(-76); // -100
snapTo(-74); // -50
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# toDecimal

Round a number to a specific decimal place.

toDecimal(3.3333); // 3.33
toDecimal(6.6666, 1); // 6.67
1
2

# velocityPerFrame

velocityPerFrame(50, 16.7); // 0.835
1

# velocityPerSecond

velocityPerSecond(1, 16.7); // 59.880...
1

# wrap

wrap(0, 1, 0.5); // 0.5
wrap(0, 1, 1.5); // 0.5
1
2