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 accelerateseaseOut
: the transition starts fast, and then decelerateseaseInOut
: 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"
]
}
]
}