Add support for widget tweening

Signed-off-by: Ritchie Frodomar <alkalinethunder@gmail.com>
This commit is contained in:
Ritchie Frodomar 2024-08-01 16:08:19 -04:00
parent e370da94bc
commit c2bdff2096
8 changed files with 209 additions and 2 deletions

View file

@ -0,0 +1,49 @@
using Microsoft.Xna.Framework;
namespace AcidicGUI.Animation;
internal abstract class Animation
{
private readonly CurveFunction curve;
private readonly float duration;
private float progress;
private bool completed;
private bool cancelled;
public bool IsCompleted => completed;
public bool IsCancelled => cancelled;
protected Animation(CurveFunction curve, float duration)
{
this.curve = curve;
this.duration = duration;
}
private void Complete()
{
if (IsCancelled)
return;
if (IsCompleted)
return;
completed = true;
}
public void Update(float time)
{
if (IsCancelled || IsCompleted)
return;
float percentage = MathHelper.Clamp(curve(progress / duration), 0, 1);
OnUpdate(percentage);
progress += time;
if (progress >= duration)
{
Complete();
}
}
protected abstract void OnUpdate(float transitionPercentage);
}

View file

@ -0,0 +1,11 @@
namespace AcidicGUI.Animation;
public sealed class AnimationHandle
{
private readonly Animation animation;
internal AnimationHandle(Animation animation)
{
this.animation = animation;
}
}

View file

@ -0,0 +1,106 @@
using AcidicGUI.Widgets;
using Microsoft.Xna.Framework;
namespace AcidicGUI.Animation;
public static class Animator
{
private static readonly List<Animation> animations = new();
public static AnimationHandle Start<TValue>(
TValue start,
TValue end,
float duration,
InterpolationFunction<TValue> interpolator,
Action<TValue> updateCallback,
CurveFunction? curve = null
)
where TValue : struct
{
curve ??= CurveFunctions.Linear;
var animation = new Animation<TValue>(curve, duration, interpolator, start, end);
animation.UpdateCallback = updateCallback;
animations.Add(animation);
return new AnimationHandle(animation);
}
public static AnimationHandle Start(
float start,
float end,
float duration,
Action<float> updateCallback,
CurveFunction? curve = null
)
{
return Start<float>(start, end, duration, MathHelper.Lerp, updateCallback, curve);
}
public static AnimationHandle Start(
Vector2 start,
Vector2 end,
float duration,
Action<Vector2> updateCallback,
CurveFunction? curve = null
)
{
return Start<Vector2>(start, end, duration, Vector2.Lerp, updateCallback, curve);
}
public static AnimationHandle Start(
Vector3 start,
Vector3 end,
float duration,
Action<Vector3> updateCallback,
CurveFunction? curve = null
)
{
return Start<Vector3>(start, end, duration, Vector3.Lerp, updateCallback, curve);
}
public static AnimationHandle Start(
Vector4 start,
Vector4 end,
float duration,
Action<Vector4> updateCallback,
CurveFunction? curve = null
)
{
return Start<Vector4>(start, end, duration, Vector4.Lerp, updateCallback, curve);
}
public static AnimationHandle Start(
Color start,
Color end,
float duration,
Action<Color> updateCallback,
CurveFunction? curve = null
)
{
return Start<Color>(start, end, duration, Color.Lerp, updateCallback, curve);
}
public static AnimationHandle RenderOpacityFade(this Widget widget, float targetOpacity, float duration, CurveFunction? curve = null)
{
return Start(widget.RenderOpacity, targetOpacity, duration, opacity => widget.RenderOpacity = opacity, curve);
}
internal static void Update(float time)
{
for (var i = 0; i < animations.Count; i++)
{
Animation animation = animations[i];
animation.Update(time);
if (animation.IsCancelled || animation.IsCompleted)
{
animations.RemoveAt(i);
i--;
}
}
}
}

View file

@ -0,0 +1,3 @@
namespace AcidicGUI.Animation;
public delegate float CurveFunction(float value);

View file

@ -0,0 +1,28 @@
namespace AcidicGUI.Animation;
public static class CurveFunctions
{
public static readonly CurveFunction Linear = x => x;
}
internal sealed class Animation<TValue> : Animation where TValue : struct
{
private readonly InterpolationFunction<TValue> interpolator;
private readonly TValue minValue;
private readonly TValue maxValue;
public Action<TValue>? UpdateCallback { get; set; }
public Animation(CurveFunction curve, float duration, InterpolationFunction<TValue> interpolator, TValue startValue, TValue endValue) : base(curve, duration)
{
this.interpolator = interpolator;
this.minValue = startValue;
this.maxValue = endValue;
}
protected override void OnUpdate(float transitionPercentage)
{
var next = interpolator(minValue, maxValue, transitionPercentage);
UpdateCallback?.Invoke(next);
}
}

View file

@ -0,0 +1,3 @@
namespace AcidicGUI.Animation;
public delegate T InterpolationFunction<T>(T min, T max, float value) where T : struct;

View file

@ -1,4 +1,5 @@
using AcidicGUI.Events;
using AcidicGUI.Events;
using AcidicGUI.Animation;
using AcidicGUI.Layout;
using AcidicGUI.Rendering;
using AcidicGUI.TextRendering;
@ -210,7 +211,9 @@ public sealed class GuiManager : IFontFamilyProvider
public void Update(float deltaTime)
{
foreach (Widget widget in CollapseChildren())
Animator.Update(deltaTime);
foreach (Widget widget in this.CollapseChildren())
{
if (widget is IUpdateHandler handler)
handler.Update(deltaTime);

View file

@ -1,3 +1,4 @@
using AcidicGUI.Animation;
using AcidicGUI.Events;
using AcidicGUI.Widgets;
using SociallyDistant.Core.Modules;
@ -16,6 +17,9 @@ public class OverlayWorkspace :
public OverlayWorkspace()
{
this.RenderOpacity = 0;
this.RenderOpacityFade(1, 0.15f);
Children.Add(workspace);
SetCustomProperty(WidgetBackgrounds.Overlay);
RenderEffect = BackgroundBlurWidgetEffect.GetEffect(Application.Instance.Context);