A UI Razor component that draws a fullscreen fade overlay and provides a static per-scene singleton API to fade in/out by animating opacity over time.
@using Sandbox
@using Sandbox.UI
@inherits PanelComponent
@namespace Sandbox
<root>
@if (opacity > 0f)
{
<div class="fade" style="opacity: @opacity"></div>
}
</root>
@code {
public const float DefaultDuration = 2f;
/// <summary>Per-scene singleton so the static API can find the live instance.</summary>
public static ScreenFade Current { get; private set; }
private float startOpacity = 1f;
private float targetOpacity = 0f;
private float duration = DefaultDuration;
private RealTimeSince startedAt;
private float opacity = 1f;
/// <summary>Fade from black to clear over <paramref name="duration"/> seconds.</summary>
public static void FadeIn(float duration = DefaultDuration)
{
Current?.Begin(targetOpacity: 0f, duration);
}
/// <summary>Fade from clear to black over <paramref name="duration"/> seconds.</summary>
public static void FadeOut(float duration = DefaultDuration)
{
Current?.Begin(targetOpacity: 1f, duration);
}
protected override void OnEnabled()
{
base.OnEnabled();
Current = this;
opacity = 1f;
Begin(targetOpacity: 0f, DefaultDuration);
}
protected override void OnDisabled()
{
base.OnDisabled();
if (Current == this) Current = null;
}
protected override void OnUpdate()
{
float t = System.Math.Clamp((float)startedAt / duration, 0f, 1f);
opacity = MathX.Lerp(startOpacity, targetOpacity, t);
}
private void Begin(float targetOpacity, float duration)
{
startOpacity = opacity;
this.targetOpacity = targetOpacity;
this.duration = System.MathF.Max(0.0001f, duration);
startedAt = 0f;
}
protected override int BuildHash() => System.HashCode.Combine((int)(opacity * 100));
}