Skip to main content

Module ease

Module ease 

Source
Expand description

Ease curves and tweens, in integers, drawn from the time alone.

An ease curve (Ease, or any Easing) bends progress: it takes how far through something is, in 1/65536 (ONE is the end), and gives how far along the value is. A Tween is a value going from one end to the other over a time, along a curve. Lerp is how a type goes between two of its values.

use shuyi_core::{Ease, Family, Tween};

// A score that pops: it rises 6 pixels in 300 milliseconds, overshoots
// a little and settles.
let pop = Tween::new(0, -6, 300).with_ease(Ease::Out(Family::Back));
assert_eq!(pop.value_at(0), 0);
assert_eq!(pop.value_at(200), -7); // past its end: the overshoot
assert_eq!(pop.value_at(300), -6);
assert_eq!(pop.value_at(5000), -6);

§Drawn from the time alone

A tween stores no progress. Its value is a function of its fields and the time since it started (Tween::value_at), so a tween never needs updating, and replays, saves and a test of any moment are exact. A tick simulation stores the tween and the step it started on; a view stores it and the millisecond.

Its time is a plain u32 whose unit the caller picks: steps in a tick simulation, turns in a turn game, whole milliseconds in a view. A screen gets each frame’s time in seconds, so a view counts its own milliseconds. A caller with a u64 clock passes the time since the start as u32::try_from(now.saturating_sub(start)).unwrap_or(u32::MAX).

“It landed” is Tween::finished_between the previous frame’s or step’s time and this one’s, true once, with no flag kept.

§The curves

Ease names Robert Penner’s curves as easings.net lists them: ten Familys, each eased in, out, in and out, or out and in, with Linear, smoothstep (Smooth) and Steps. In RON a curve is written Linear, InOut(Cubic) or Steps(4).

  • Out(f) is ONE - In(f)(ONE - p), for every family.
  • InOut(f) is In over the first half and Out over the second, except for Back and Elastic, whose InOut on easings.net has constants of its own.
  • OutIn(f) is Out over the first half and In over the second.
  • Back and Elastic overshoot: they go below 0 or above ONE on the way.

Every curve gives exactly 0 at 0 and exactly ONE at ONE, is within 1/65536 of its formula for Linear, Smooth, Steps and Quad to Quint, and within 2/65536 for the rest. The curves are computed in integers, so they are the same on every machine; a skin that works in floats eases through shuyi_ui::ease::ease_f32, which goes through these.

The same curves in other engines, checked against their docs on 2026-11-04 (Godot 4.7’s Tween, Bevy 0.20.1’s EaseFunction in bevy_curve, Defold 1.14.0’s go.animate, MDN’s <easing-function>). A family and a way of easing make the name: InOut(Cubic) is easings.net’s easeInOutCubic, Godot’s TRANS_CUBIC with EASE_IN_OUT, Bevy’s CubicInOut and Defold’s go.EASING_INOUTCUBIC.

Familyeasings.netGodotBevyDefold
SineSineTRANS_SINESineSINE
QuadQuadTRANS_QUADQuadraticQUAD
CubicCubicTRANS_CUBICCubicCUBIC
QuartQuartTRANS_QUARTQuarticQUART
QuintQuintTRANS_QUINTQuinticQUINT
ExpoExpoTRANS_EXPO (1)Exponential (2)EXPO
CircCircTRANS_CIRCCircularCIRC
BackBackTRANS_BACKBack (3)BACK
ElasticElasticTRANS_ELASTICElasticELASTIC
BounceBounceTRANS_BOUNCEBounceBOUNCE
Easeeasings.netGodotBevyDefoldCSS
In(f)easeIn…EASE_IN…InIN…none
Out(f)easeOut…EASE_OUT…OutOUT…none
InOut(f)easeInOut…EASE_IN_OUT…InOutINOUT…none
OutIn(f)noneEASE_OUT_INnoneOUTIN…none
LinearnoneTRANS_LINEARLinearLINEARlinear
SmoothnonenoneSmoothStepnonenone
Steps(n)nonenoneSteps(n, JumpAt::End)nonesteps(n, jump-end)
  1. Godot’s TRANS_EXPO is Penner’s first version, shifted by a thousandth so it has no jump: within 1/1000 of Expo.
  2. Bevy’s Exponential curves are scaled to meet 0 and 1 without a jump: within 1/1000 of Expo.
  3. Bevy’s BackInOut takes its constant as 1.70158 + 1.525, not 1.70158 * 1.525, so it overshoots further than InOut(Back).

None here matches Godot’s TRANS_SPRING, Bevy’s Elastic(omega), SmoothStepIn, SmoothStepOut and SmootherStep curves, or CSS’s named curves (ease, ease-in, ease-out, ease-in-out), which are cubic Béziers. CSS’s step-end is Steps(1).

§Curves and tweens are data

Ease, Tween and Repeat read and write with serde, inside a game’s own RON files. A tween written with only from, to and duration takes the defaults for the rest:

use shuyi_core::{Ease, Family, Tween};

let tween: Tween<i32> = ron::from_str("(from: 0, to: 10, duration: 300)")?;
assert_eq!(tween, Tween::new(0, 10, 300));
let eased: Tween<i32> =
    ron::from_str("(from: 0, to: 10, duration: 300, ease: InOut(Cubic))")?;
assert_eq!(eased.ease, Ease::InOut(Family::Cubic));

Structs§

Tween
A value going from from to to over duration, along a curve, after delay: drawn from the time since it started alone (value_at). The module docs say how a simulation and a view keep one.

Enums§

Ease
A named ease curve. The module docs name each beside other engines’ names.
Family
The ten families of Penner’s curves, as easings.net names them, from the gentlest to the ones that overshoot and bounce.
Repeat
How many times a Tween plays, in all.

Constants§

ONE
The whole way, in 1/65536: progress runs from 0 to ONE.

Traits§

Easing
A curve: how far along a value is, p of the way through.
Lerp
A type that goes between two of its values.