Demos/AnimationShowcase/Sections/ChoreographySection.cs
using Goo;
using Goo.Animation;
using Sandbox.UI;
using static Sandbox.DemoTokens;
namespace Sandbox.AnimationShowcase;
// one Timeline drives three properties on one element; caller dispatches on sample.SegmentIndex to route the value, each property holding its last value between phases. stateless: opacity/xOffset/scale come straight from Sample at Build time, no cached fields, no manual reset on replay.
public class ChoreographySection
{
const float TrackW = 380f;
const float TrackH = 100f;
const float Slide = 260f; // px of MarginLeft travel during phase 1
const float SizeMin = 40f;
const float SizeMax = 80f;
static readonly Timeline Choreography = Timeline.Sequence(
new Tween( Easing.EaseInOut, duration: 0.5f ), // seg 0 -> opacity
new Tween( Easing.EaseInOut, duration: 0.5f ), // seg 1 -> x offset
new Tween( Easing.EaseInOut, duration: 0.5f ) ); // seg 2 -> scale
TimelineAnimator _anim = new( Choreography ) { Elapsed = 999f }; // start past-end (settled)
public void Update( float dt ) => _anim.Update( dt );
public Container Build()
{
var s = _anim.Sample;
float opacity = PhaseValue( s, 0 );
float xOff = PhaseValue( s, 1 ) * Slide;
float size = SizeMin + (SizeMax - SizeMin) * PhaseValue( s, 2 );
return Section.Build(
"Choreography - one sequence across 3 properties: Fade --> Slide --> Grow",
new Container
{
FlexDirection = FlexDirection.Column,
Gap = Space3,
AlignItems = Align.FlexStart,
Children =
{
new Container
{
Width = TrackW,
Height = TrackH,
BackgroundColor = BgCardHi,
BorderRadius = Radius2,
FlexDirection = FlexDirection.Row,
AlignItems = Align.Center,
Padding = Space2,
Children =
{
new Container
{
MarginLeft = xOff,
Width = size,
Height = size,
Opacity = opacity,
BackgroundColor = Accent,
BorderRadius = Radius2,
},
},
},
CtrlButton.Build( "replay", _ => _anim.Restart() ),
},
} );
}
// Property N is at 0 until its segment runs, animates with sample.Value while
// active, and stays at the segment's end value (1f) once a later segment runs.
static float PhaseValue( TimelineSample s, int phase )
{
if ( s.SegmentIndex < phase ) return 0f;
if ( s.SegmentIndex == phase ) return s.Value;
return 1f;
}
}