Code/Demos/AnimationShowcase/Sections/EasingsSection.cs
using Goo;
using Goo.Animation;
using Sandbox.UI;
using static Sandbox.DemoTokens;

namespace Sandbox.AnimationShowcase;

public class EasingsSection
{
    const float TrackW    = 480f;
    const float DotSize   = 22f;
    const float DotTravel = TrackW - DotSize;
    const float LaneH     = 28f;
    const float TrackH    = 8f + 6f * LaneH;

    static readonly Tween TLinear    = new( Easing.Linear,    duration: 1.2f );
    static readonly Tween TEaseInOut = new( Easing.EaseInOut, duration: 1.2f );
    static readonly Tween TExpoOut   = new( Easing.ExpoOut,   duration: 1.2f );
    static readonly Tween TBounceOut = new( Easing.BounceOut, duration: 1.2f );
    static readonly Tween TSineInOut = new( Easing.SineInOut, duration: 1.2f );
    static readonly Tween TSnap      = new( Easing.CubicBezier( 0.8f, 0f, 0.2f, 1f ), duration: 1.2f );

    float _elapsed = 999f;

    public void Update( float dt ) => _elapsed += dt;

    public Container Build() => Section.Build(
        "tween easings - click track to replay",
        new Container
        {
            FlexDirection = FlexDirection.Row,
            Gap           = Space4,
            AlignItems    = Align.FlexStart,
            Children =
            {
                Track(),
                Legend.Build(
                    ( "Linear",      "constant speed",          Accent ),
                    ( "EaseInOut",   "smooth start and end",    AccentGreen ),
                    ( "ExpoOut",     "fast start, slow end",    AccentOrange ),
                    ( "BounceOut",   "bouncing landing",        AccentYellow ),
                    ( "SineInOut",   "sine curve",              Accent ),
                    ( "CubicBezier", "(.8, 0, .2, 1) snap",     AccentGreen )
                ),
            },
        } );

    Container Track()
    {
        var track = new Container
        {
            Width           = TrackW,
            Height          = TrackH,
            BackgroundColor = BgCard,
            BorderRadius    = Radius2,
            Position        = PositionMode.Relative,
            PointerEvents   = PointerEvents.All,
            OnClick         = _ => _elapsed = 0f,
        };
        AddLane( track, 0, TLinear,    Accent );
        AddLane( track, 1, TEaseInOut, AccentGreen );
        AddLane( track, 2, TExpoOut,   AccentOrange );
        AddLane( track, 3, TBounceOut, AccentYellow );
        AddLane( track, 4, TSineInOut, Accent );
        AddLane( track, 5, TSnap,      AccentGreen );
        return track;
    }

    void AddLane( Container parent, int idx, Tween tween, Color color )
    {
        float top  = 8f + idx * LaneH;
        float left = tween.Eval( _elapsed ) * DotTravel;
        parent.Children.Add( Dot.AtCorner( left, top, DotSize, color ) );
    }
}