Demos/AnimationShowcase/Sections/MouseFollowSection.cs
using System;
using Goo;
using Goo.Animation;
using Sandbox.UI;
using static Sandbox.DemoTokens;

namespace Sandbox.AnimationShowcase;

public class MouseFollowSection
{
    const float PadW    = 480f;
    const float PadH    = 320f;
    const float DotSize = 18f;

    SmoothVector2 _smooth = new( initial: new Vector2( PadW / 2f, PadH / 2f ), smoothTime: 0.20f );
    DecayVector2  _decay  = new( initial: new Vector2( PadW / 2f, PadH / 2f ), halflife:   0.12f );
    SpringVector2 _spring = new( initial: new Vector2( PadW / 2f, PadH / 2f ), frequency:  6f, damping: 0.35f );

    public void Update( float dt )
    {
        _smooth.Update( dt );
        _decay.Update( dt );
        _spring.Update( dt );
    }

    public Container Build() => Section.Build(
        "vector2 dampers - move the cursor inside the pad",
        new Container
        {
            FlexDirection = FlexDirection.Row,
            Gap           = Space4,
            AlignItems    = Align.FlexStart,
            Children =
            {
                Pad(),
                Legend.Build(
                    ( "SmoothVector2", "velocity-tracked",             Accent ),
                    ( "DecayVector2",  "exponential, no overshoot",    AccentGreen ),
                    ( "SpringVector2", "oscillates",                   AccentOrange )
                ),
            },
        } );

    Container Pad()
    {
        var pad = new Container
        {
            Width           = PadW,
            Height          = PadH,
            BackgroundColor = BgCard,
            BorderRadius    = Radius2,
            Position        = PositionMode.Relative,
            PointerEvents   = PointerEvents.All,
            // LocalPosition is in rendered pixels; normalize via Box.Rect into authored coords.
            OnMouseMove = e =>
            {
                float rectW = e.Target?.Box.Rect.Width  ?? 0f;
                float rectH = e.Target?.Box.Rect.Height ?? 0f;
                if ( rectW <= 0f || rectH <= 0f ) return;
                float ax = Math.Clamp( e.LocalPosition.x / rectW, 0f, 1f ) * PadW;
                float ay = Math.Clamp( e.LocalPosition.y / rectH, 0f, 1f ) * PadH;
                var target = new Vector2( ax, ay );
                _smooth.Target = target;
                _decay.Target  = target;
                _spring.Target = target;
            },
        };
        pad.Children.Add( Dot.AtCenter( _smooth.Current, DotSize, Accent ) );
        pad.Children.Add( Dot.AtCenter( _decay.Current,  DotSize, AccentGreen ) );
        pad.Children.Add( Dot.AtCenter( _spring.Current, DotSize, AccentOrange ) );
        return pad;
    }
}