Skip to main content

Easing functions

easing

Commentary

added in 1.5.0

stateful function

Applies an easing function to change a number over time. This is useful if you want to simulate a value ramping up or down at particular velocities, especially mathematically complex ones.

Like many things, easings—which are common in animation APIs—seem complicated at first, but become simple when explained with an example.

Imagine that you want to generate some data that slowly moves a value from 5 to 50. How would you do that?

One idea you might have is to use sequentialInteger to advance the number one data point at a time. That works great for the simplest case, but what if you want to advance towards 50 at a nonlinear rate? Perhaps intervals could do the job? But that doesn't work either because the numbers it produces will change at abrupt pitches. At one interval, your value might be 5, and at the next, 15, and so on.

What you want is some way to glide your value between 5 and 50 and see intermediate values between the two, along with the option to control the rate it advances.

Easings solve that problem.


To use them, you specify a handful of parameters. The simplest ones to understand are from (the value you want to start at), to (the value you want to end at), and duration (the number of milliseconds to transition between the two. ShadowTraffic will use the wallclock to decide when to stop transitioning the value and emit as many events during that time as you allow it. 1 When the easing function reaches to value, it will only ever produce that value thereafter. 2

In addition to defining those three parameters, you also define a direction (the shape of the line between your two values) and degree (the steepness of the line). These parameters follow well-known mathematical formulas. For example, direction 3 can be:

  • easeIn: the transition starts slowly, and then accelerates
  • easeOut: the transition starts fast, and then decelerates
  • easeInOut: the transition is slow at both ends, but fast in the middle

Likewise, degree 4 has like familiar values:

  • linear: a constant rate of transition, t
  • quad: a quadratic rate of transition, t^2
  • cubic: a cubic rate of transition, t^3
  • quart: a quartic rate of transition, t^4
  • quint: a quintic rate of transition, t^5

When you specify an easing, you combine a direction and a degree into a pair to create transitions like easeIn / linear, or easeInOut / cubic. These pairings specify the precise slope of the line between your from and to values . If you have trouble visualizing what any of these look like, many of these pairs are helpfully graphed on Easings.net

Lastly, in addition to the predefined easing direction / degree pairs, you may also supply your own numeric function. 5 This is especially useful if you want to stack easings with easingChain and peg a value at a particular number.


Examples

Linear easing

Linear easings are the most simple example. They consistently advance from to to at a constant rate. When you use a linear degree, the choice of direction doesn't matter because its rate never deviates.

In this example, ShadowTraffic will emit events for 8000 milliseconds that transition 5 to 125.

{
"_gen": "easing",
"direction": "easeIn",
"degree": "linear",
"from": 5,
"to": 125,
"duration": 8000
}
[
5,
5.015,
5.03,
5.045,
5.06,
5.075,
5.09,
5.105,
5.12,
5.135
]

These values resemble the plot:

Capping the end value

When the duration is reached, easing will peg the result to the value of from. In this example, duration is set to 5 milliseconds, a very small amount of time. easing generates a few intermediate values, and all values after will be 12.

{
"_gen": "easing",
"direction": "easeIn",
"degree": "linear",
"from": 10,
"to": 12,
"duration": 5
}
[
10,
10.4,
10.8,
11.2,
11.6,
12,
12,
12,
12,
12
]

Which resembles the plot:

Changing direction

Use a different direction to change the slope of the tails of the curve. In this example, easeOut will decelerate the rate of change as the value gets closer to 70.

{
"_gen": "easing",
"direction": "easeOut",
"degree": "cubic",
"from": 50,
"to": 70,
"duration": 10000
}
[
50,
50.00599940002,
50.01199760016,
50.01799460054,
50.023990401279995,
50.0299850025,
50.035978404320005,
50.041970606860005,
50.04796161024,
50.05395141458
]

Plotting the values, you can see how easeOut has an accelerated start, but "easy / flat" ending:

Changing degree

Likewise, use a different degree to change the steepness of the curve. Higher polynomials (like quart) have steeper curves than lower polynomials (like quad).

{
"_gen": "easing",
"direction": "easeInOut",
"degree": "quart",
"from": 100,
"to": 30,
"duration": 3000
}
[
100,
99.9999999999931,
99.99999999988938,
99.99999999944,
99.99999999823012,
99.99999999567902,
99.99999999104,
99.9999999834005,
99.99999997168197,
99.99999995464
]

Plotting the values, you can see the curve with flat tails at both ends:

Custom easings

If you want to define your own easing, set ease to any numeric function and duration.

{
"_gen": "easing",
"direction": "easeInOut",
"ease": {
"_gen": "normalDistribution",
"mean": 100,
"sd": 5
},
"duration": 5000
}
[
105.46946550894056,
100.23162027249977,
94.57096414497,
95.13027563999286,
97.17970044868987,
99.0733263034791,
104.18051750224168,
94.17191163932868,
103.30950041849384,
101.89600287575489
]

Imperfect lines

If the mathematically perfect curves that easing creates aren't realistic, you can use math to add a small amount of "jitter" to each value. Notice how the values aren't strictly ascending anymore.

{
"_gen": "math",
"expr": "value + jitter",
"names": {
"value": {
"_gen": "easing",
"direction": "easeIn",
"degree": "quad",
"from": 100,
"to": 150,
"duration": 5000
},
"jitter": {
"_gen": "uniformDistribution",
"bounds": [
-1,
1
]
}
}
}
[
99.14409961507134,
99.2804851852396,
99.12853933930458,
100.51843732264463,
100.36829297419827,
100.86177127196852,
99.55407228518311,
100.53739401712491,
100.00236937112885,
99.3303338883696
]

Specification

JSON schema

{
"oneOf": [
{
"type": "object",
"properties": {
"direction": {
"type": "string",
"enum": [
"easeIn",
"easeOut",
"easeInOut"
]
},
"degree": {
"type": "string",
"enum": [
"linear",
"quad",
"cubic",
"quart",
"quint"
]
},
"from": {
"type": "number"
},
"to": {
"type": "number"
},
"duration": {
"type": "integer",
"minimum": 1
}
},
"required": [
"direction",
"degree",
"from",
"to",
"duration"
]
},
{
"type": "object",
"properties": {
"ease": {
"oneOf": [
{
"type": "number"
},
{
"type": "object",
"properties": {
"_gen": {
"type": "string"
}
},
"required": [
"_gen"
]
}
]
},
"duration": {
"type": "integer",
"minimum": 1
}
},
"required": [
"ease",
"duration"
]
}
]
}