Improved animations (#907)

* Smoother countdown and new menu and in-game animations

- Countdown digits pop in and fade out each second, with a ring of dots showing the remaining part of the second
- Medley countdown uses a smoothed timer and no longer stutters
- Rating popups scale in from below and drift upwards while fading out
- Player scores pulse briefly at the end of a line
- Pause menu fades and scales in and out
- Buttons and select slides pulse briefly when they get selected
- Shared easing functions and a draw transform for animations

* Retrigger CI
This commit is contained in:
McSchneidar authored and GitHub committed 2026-10-17 12:39:18 +02:00
1 parent 602417e773
commit e665e31f4a
17 files changed
+489 -46

No files matched your search

@@ -1,4 +1,10 @@
ScreenSing
+++++++++
Version 14
Add:
<StaticCountdownRing> Ring of dots around the countdown (skin <CountdownDot>), the countdown is centered in this rect and its size depends on the rect size
+++++++++
Version 13
@@ -370,6 +370,7 @@
<ToneHelper>ToneHelper.png</ToneHelper>
<Score>Score.png</Score>
<Star>Star.png</Star>
<CountdownDot>CountdownDot.png</CountdownDot>
<NoAvatar>NoAvatar.png</NoAvatar>
<SongMenu>SongMenu.png</SongMenu>
<PauseBG>PauseBG.png</PauseBG>
+1
View File
@@ -370,6 +370,7 @@
<ToneHelper>ToneHelper.png</ToneHelper>
<Score>Score.png</Score>
<Star>Star.png</Star>
<CountdownDot>CountdownDot.png</CountdownDot>
<NoAvatar>NoAvatar.png</NoAvatar>
<SongMenu>SongMenu.png</SongMenu>
<PauseBG>PauseBG.png</PauseBG>
+1
View File
@@ -370,6 +370,7 @@
<ToneHelper>ToneHelper.png</ToneHelper>
<Score>Score.png</Score>
<Star>Star.png</Star>
<CountdownDot>CountdownDot.png</CountdownDot>
<NoAvatar>NoAvatar.png</NoAvatar>
<SongMenu>SongMenu.png</SongMenu>
<PauseBG>PauseBG.png</PauseBG>
+1
View File
@@ -370,6 +370,7 @@
<ToneHelper>ToneHelper.png</ToneHelper>
<Score>Score.png</Score>
<Star>Star.png</Star>
<CountdownDot>CountdownDot.png</CountdownDot>
<NoAvatar>NoAvatar.png</NoAvatar>
<SongMenu>SongMenu.png</SongMenu>
<PauseBG>PauseBG.png</PauseBG>
+1
View File
@@ -370,6 +370,7 @@
<ToneHelper>ToneHelper.png</ToneHelper>
<Score>Score.png</Score>
<Star>Star.png</Star>
<CountdownDot>CountdownDot.png</CountdownDot>
<NoAvatar>NoAvatar.png</NoAvatar>
<SongMenu>SongMenu.png</SongMenu>
<PauseBG>PauseBG.png</PauseBG>
+1
View File
@@ -370,6 +370,7 @@
<ToneHelper>ToneHelper.png</ToneHelper>
<Score>Score.png</Score>
<Star>Star.png</Star>
<CountdownDot>CountdownDot.png</CountdownDot>
<NoAvatar>NoAvatar.png</NoAvatar>
<SongMenu>SongMenu.png</SongMenu>
<PauseBG>PauseBG.png</PauseBG>
Binary file not shown.

After

Width:  |  Height:  |  Size: 419 B

@@ -2,7 +2,7 @@
<Screen xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Informations>
<ScreenName>ScreenSing</ScreenName>
<ScreenVersion>13</ScreenVersion>
<ScreenVersion>14</ScreenVersion>
</Informations>
<Backgrounds>
<Background Name="Background1">
@@ -153,6 +153,22 @@
<Z>-1</Z>
</Rect>
</Static>
<Static Name="StaticCountdownRing">
<Skin>CountdownDot</Skin>
<Color>
<R>1</R>
<G>1</G>
<B>1</B>
<A>0.6</A>
</Color>
<Rect>
<X>635</X>
<Y>215</Y>
<W>650</W>
<H>650</H>
<Z>0</Z>
</Rect>
</Static>
<Static Name="StaticPauseBG">
<Skin>PauseBG</Skin>
<Color Name="Visible" />
+65 -6
View File
@@ -16,6 +16,7 @@
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using Vocaluxe.Lib.Draw;
using VocaluxeLib;
@@ -28,6 +29,64 @@ namespace Vocaluxe.Base
{
private static IDraw _Draw;
private struct STransform
{
public float Scale;
public float CenterX;
public float CenterY;
public float OffsetY;
public float Alpha;
}
private static readonly List<STransform> _Transforms = new List<STransform>();
public static void PushTransform(float scale, float centerX, float centerY, float offsetY, float alpha)
{
_Transforms.Add(new STransform { Scale = scale, CenterX = centerX, CenterY = centerY, OffsetY = offsetY, Alpha = alpha });
}
public static void PopTransform()
{
if (_Transforms.Count > 0)
{
_Transforms.RemoveAt(_Transforms.Count - 1);
}
}
private static SRectF _Transform(SRectF rect)
{
for (var i = _Transforms.Count - 1; i >= 0; i--)
{
var transform = _Transforms[i];
rect.X = transform.CenterX + (rect.X - transform.CenterX) * transform.Scale;
rect.Y = transform.CenterY + (rect.Y - transform.CenterY) * transform.Scale + transform.OffsetY;
rect.W *= transform.Scale;
rect.H *= transform.Scale;
}
return rect;
}
private static SColorF _Transform(SColorF color)
{
foreach (var transform in _Transforms)
{
color.A *= transform.Alpha;
}
return color;
}
private static float _TransformScale(float value)
{
foreach (var transform in _Transforms)
{
value *= transform.Scale;
}
return value;
}
/// <summary>
/// Initializes the Driver selected in the config
/// </summary>
@@ -295,7 +354,7 @@ namespace Vocaluxe.Base
/// <param name="allMonitors">Render on all monitors</param>
public static void DrawRect(SColorF color, SRectF rect, bool allMonitors = true)
{
_Draw.DrawRect(color, rect, allMonitors);
_Draw.DrawRect(_Transform(color), _Transform(rect), allMonitors);
}
/// <summary>
@@ -307,7 +366,7 @@ namespace Vocaluxe.Base
/// <param name="height">Height of the reflection</param>
public static void DrawRectReflection(SColorF color, SRectF rect, float space, float height)
{
_Draw.DrawRectReflection(color, rect, space, height);
_Draw.DrawRectReflection(_Transform(color), _Transform(rect), _TransformScale(space), _TransformScale(height));
}
/// <summary>
@@ -339,7 +398,7 @@ namespace Vocaluxe.Base
/// <param name="allMonitors">Render on all monitors</param>
public static void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, bool mirrored = false, bool allMonitors = true)
{
_Draw.DrawTexture(textureRef, rect, color, mirrored, allMonitors);
_Draw.DrawTexture(textureRef, _Transform(rect), _Transform(color), mirrored, allMonitors);
}
/// <summary>
@@ -353,7 +412,7 @@ namespace Vocaluxe.Base
/// <param name="allMonitors">Render on all monitors</param>
public static void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false, bool allMonitors = true)
{
_Draw.DrawTexture(textureRef, rect, color, bounds, mirrored, allMonitors);
_Draw.DrawTexture(textureRef, _Transform(rect), _Transform(color), _Transform(bounds), mirrored, allMonitors);
}
/// <summary>
@@ -367,7 +426,7 @@ namespace Vocaluxe.Base
/// <param name="allMonitors">Render on all monitors</param>
public static void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, float begin, float end, bool allMonitors = true)
{
_Draw.DrawTexture(textureRef, rect, color, begin, end, allMonitors);
_Draw.DrawTexture(textureRef, _Transform(rect), _Transform(color), begin, end, allMonitors);
}
/// <summary>
@@ -410,7 +469,7 @@ namespace Vocaluxe.Base
/// <param name="height">Height of the reflection</param>
public static void DrawTextureReflection(CTextureRef textureRef, SRectF rect, SColorF color, SRectF bounds, float space, float height, bool allMonitors = true)
{
_Draw.DrawTextureReflection(textureRef, rect, color, bounds, space, height, allMonitors);
_Draw.DrawTextureReflection(textureRef, _Transform(rect), _Transform(color), _Transform(bounds), _TransformScale(space), _TransformScale(height), allMonitors);
}
}
}
+10
View File
@@ -462,6 +462,16 @@ namespace Vocaluxe.Base
{
CDraw.DrawRectReflection(color, rect, space, height);
}
public void PushTransform(float scale, float centerX, float centerY, float offsetY, float alpha)
{
CDraw.PushTransform(scale, centerX, centerY, offsetY, alpha);
}
public void PopTransform()
{
CDraw.PopTransform();
}
}
class CBGraphics : IGraphics
+259 -36
View File
@@ -39,7 +39,7 @@ namespace Vocaluxe.Screens
// Version number for theme files. Increment it, if you've changed something on the theme files!
protected override int _ScreenVersion
{
get { return 13; }
get { return 14; }
}
private struct STimeRect
@@ -74,6 +74,7 @@ namespace Vocaluxe.Screens
private const string _StaticLyricHelper = "StaticLyricHelper";
private const string _StaticLyricHelperDuet = "StaticLyricHelperDuet";
private const string _StaticLyricHelperTop = "StaticLyricHelperTop";
private const string _StaticCountdownRing = "StaticCountdownRing";
private List<string> _StaticsPause;
private string[,,] _StaticScores;
@@ -141,9 +142,21 @@ namespace Vocaluxe.Screens
private Stopwatch _TimerDuetText1;
private Stopwatch _TimerDuetText2;
private Stopwatch _TimerStartCountdown;
private Stopwatch _TimerMedleyCountdown;
private float _MedleyCountdownStart;
private float _CountdownProgress = -1f;
private float[] _ScoreTextHeight;
private float[] _ScoreTextY;
private readonly Stopwatch _TimerPauseAnimation = new Stopwatch();
private readonly List<IMenuElement> _PauseAnimationBackgrounds = new List<IMenuElement>();
private readonly List<IMenuElement> _PauseAnimationElements = new List<IMenuElement>();
private bool _PauseAnimationOpening;
private bool _StartDelayed;
private const float _StartCountdownSeconds = 3f;
private const int _CountdownRingDots = 60;
private bool _Pause;
private bool _Webcam;
@@ -180,7 +193,8 @@ namespace Vocaluxe.Screens
_StaticTimePointer,
_StaticLyricHelper,
_StaticLyricHelperDuet,
_StaticLyricHelperTop
_StaticLyricHelperTop,
_StaticCountdownRing
};
_BuildStaticStrings(ref statics);
_ThemeStatics = statics.ToArray();
@@ -209,6 +223,10 @@ namespace Vocaluxe.Screens
_TimerDuetText1 = new Stopwatch();
_TimerDuetText2 = new Stopwatch();
_TimerStartCountdown = new Stopwatch();
_TimerMedleyCountdown = new Stopwatch();
_ScoreTextHeight = new float[CSettings.MaxNumPlayer];
_ScoreTextY = new float[CSettings.MaxNumPlayer];
_TimerShortInfoText = new System.Timers.Timer(5000);
_TimerShortInfoText.AutoReset = false;
@@ -241,6 +259,7 @@ namespace Vocaluxe.Screens
_SelectSlides[_SelectSlidePauseVocalsVolume].AddValues(new string[]
{ "0", "5", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55", "60", "65", "70", "75", "80", "85", "90", "95", "100" });
_SelectSlides[_SelectSlidePauseVocalsVolume].Selection = CConfig.VocalsVolume / 5;
_Statics[_StaticCountdownRing].Visible = false;
//Automatically find statics and texts with pause prefix
foreach (var s in _Statics.Where(s => s.ThemeLoaded && s.GetThemeName().StartsWith("StaticPause")))
@@ -531,7 +550,11 @@ namespace Vocaluxe.Screens
_TimeToFirstNoteDuet = 0f;
_StartDelayed = false;
_TimerStartCountdown.Reset();
_TimerMedleyCountdown.Reset();
_CountdownProgress = -1f;
_PrepareCountdownFont();
_SetPause(false);
_TimerPauseAnimation.Reset();
_TimeRects.Clear();
try
@@ -556,6 +579,8 @@ namespace Vocaluxe.Screens
{
_Statics[_PlayerStaticAvatar[p]].Aspect = EAspect.Crop;
_Texts[_PlayerTextScore[p]].Color = new SColorF(CBase.Themes.GetPlayerColor(p + 1), _Texts[_PlayerTextScore[p]].Color.A);
_ScoreTextHeight[p] = _Texts[_PlayerTextScore[p]].Font.Height;
_ScoreTextY[p] = _Texts[_PlayerTextScore[p]].Y;
}
_SetVisibility();
@@ -684,8 +709,7 @@ namespace Vocaluxe.Screens
for (var p = 0; p < CGame.NumPlayers; p++)
{
var fmtString = CGame.Players[p].Points < 10000 ? "0000" : "00000";
_Texts[_PlayerTextScore[p]].Text = CGame.Players[p].Points.ToString(fmtString);
_UpdateScoreText(p);
}
if (_CurrentVideo != null && !_FadeOut && CConfig.Config.Video.VideosInSongs == EOffOn.TR_CONFIG_ON)
@@ -815,22 +839,105 @@ namespace Vocaluxe.Screens
var timeToFirstMedleyNote = CGame.GetTimeFromBeats(song.Medley.StartBeat, song.Bpm) + song.Gap;
if (_CurrentTime < timeToFirstMedleyNote)
{
var timeDiff = timeToFirstMedleyNote - _CurrentTime + 1;
var fullSeconds = (float)Math.Truncate(timeDiff);
var partSeconds = timeDiff - fullSeconds;
_Texts[_TextMedleyCountdown].Visible = true;
_Texts[_TextMedleyCountdown].Text = fullSeconds.ToString();
_Texts[_TextMedleyCountdown].Font.Height = Math.Max(CSettings.RenderH / 8f, partSeconds * CSettings.RenderH);
var textBounds = CFonts.GetTextBounds(_Texts[_TextMedleyCountdown]);
var x = CSettings.RenderW / 2 - textBounds.Width / 2;
var y = CSettings.RenderH / 2 - textBounds.Height / 2;
_Texts[_TextMedleyCountdown].X = x;
_Texts[_TextMedleyCountdown].Y = y;
_SetCountdown(_GetSmoothedMedleyCountdown(timeToFirstMedleyNote - _CurrentTime));
}
else
{
_Texts[_TextMedleyCountdown].Visible = false;
_TimerMedleyCountdown.Reset();
_SetCountdown(0f);
}
}
private float _GetSmoothedMedleyCountdown(float remaining)
{
if (_Pause)
{
_TimerMedleyCountdown.Reset();
return remaining;
}
var predicted = _MedleyCountdownStart - _TimerMedleyCountdown.ElapsedMilliseconds / 1000f;
if (!_TimerMedleyCountdown.IsRunning || Math.Abs(predicted - remaining) > 0.1f)
{
_MedleyCountdownStart = remaining;
_TimerMedleyCountdown.Restart();
return remaining;
}
return predicted;
}
private float _GetCountdownHeight()
{
return Math.Min(_Statics[_StaticCountdownRing].Rect.W, _Statics[_StaticCountdownRing].Rect.H) * 0.62f;
}
private void _PrepareCountdownFont()
{
var text = _Texts[_TextMedleyCountdown];
text.Text = "0123456789";
text.Font.Height = _GetCountdownHeight() * 1.2f;
CFonts.GetTextBounds(text);
text.Text = string.Empty;
text.Visible = false;
}
private void _SetCountdown(float remaining)
{
var text = _Texts[_TextMedleyCountdown];
if (remaining <= 0f)
{
text.Visible = false;
_CountdownProgress = -1f;
return;
}
var fullSeconds = (float)Math.Ceiling(remaining);
var phase = fullSeconds - remaining;
var scale = 0.6f + 0.4f * CHelper.EaseOutBack(Math.Min(1f, phase / 0.35f));
var alpha = 1f;
if (phase > 0.75f)
{
var fade = (phase - 0.75f) / 0.25f;
alpha = 1f - fade;
scale += fade * 0.2f;
}
text.Visible = true;
text.Text = fullSeconds.ToString("0");
text.Font.Height = _GetCountdownHeight() * scale;
text.Alpha = alpha;
var ring = _Statics[_StaticCountdownRing].Rect;
var textBounds = CFonts.GetTextBounds(text);
text.X = ring.X + ring.W / 2f - textBounds.Width / 2f;
text.Y = ring.Y + ring.H / 2f - textBounds.Height / 2f;
_CountdownProgress = 1f - phase;
}
private void _DrawCountdownRing()
{
var ring = _Statics[_StaticCountdownRing];
if (!_Texts[_TextMedleyCountdown].Visible || _CountdownProgress <= 0f || ring.Texture == null)
{
return;
}
var rect = ring.Rect;
var radius = Math.Min(rect.W, rect.H) / 2f;
var dotSize = radius * 0.05f;
var centerX = rect.X + rect.W / 2f;
var centerY = rect.Y + rect.H / 2f;
var dots = _CountdownRingDots * _CountdownProgress;
for (var i = 0; i < _CountdownRingDots && i < dots; i++)
{
var angle = (i + 0.5f) / _CountdownRingDots * 2f * (float)Math.PI;
var x = centerX + radius * (float)Math.Sin(angle) - dotSize / 2f;
var y = centerY - radius * (float)Math.Cos(angle) - dotSize / 2f;
var color = new SColorF(ring.Color.R, ring.Color.G, ring.Color.B, ring.Color.A * Math.Min(1f, dots - i));
CDraw.DrawTexture(ring.Texture, new SRectF(x, y, dotSize, dotSize, rect.Z), color);
}
}
@@ -846,22 +953,11 @@ namespace Vocaluxe.Screens
if (remaining > 0f)
{
var fullSeconds = (float)Math.Ceiling(remaining);
var partSeconds = remaining - (float)Math.Floor(remaining);
_Texts[_TextMedleyCountdown].Visible = true;
_Texts[_TextMedleyCountdown].Text = fullSeconds.ToString("0");
_Texts[_TextMedleyCountdown].Font.Height = Math.Max(CSettings.RenderH / 8f, partSeconds * CSettings.RenderH);
var textBounds = CFonts.GetTextBounds(_Texts[_TextMedleyCountdown]);
var x = CSettings.RenderW / 2f - textBounds.Width / 2f;
var y = CSettings.RenderH / 2f - textBounds.Height / 2f;
_Texts[_TextMedleyCountdown].X = x;
_Texts[_TextMedleyCountdown].Y = y;
_SetCountdown(remaining);
}
else
{
_Texts[_TextMedleyCountdown].Visible = false;
_SetCountdown(0f);
_Texts[_TextMedleyCountdown].Text = string.Empty;
_TimerStartCountdown.Reset();
@@ -1076,24 +1172,57 @@ namespace Vocaluxe.Screens
}
}
private void _UpdateScoreText(int player)
{
var fmtString = CGame.Players[player].Points < 10000 ? "0000" : "00000";
var text = _Texts[_PlayerTextScore[player]];
text.Text = CGame.Players[player].Points.ToString(fmtString);
var scale = 1f;
var timeSinceLastLine = _CurrentTime - CGame.Players[player].TimeLastLineChange;
if (timeSinceLastLine >= 0f && timeSinceLastLine < 0.5f)
{
scale = 1f + 0.05f * (float)Math.Sin(Math.PI * timeSinceLastLine / 0.5f);
}
text.Font.Height = _ScoreTextHeight[player] * scale;
text.Y = _ScoreTextY[player] - _ScoreTextHeight[player] * (scale - 1f) / 2f;
}
private void _UpdateRatingPopups()
{
for (var i = 0; i < CGame.NumPlayers; i++)
{
var fadeOutTime = 1f;
var showTime = 0.5f;
var fadeInTime = 0.25f;
var fadeOutTime = 0.5f;
var showTime = 0.9f;
var popup = _RatingPopups[_PlayerRatingPopup[i]];
var timeSinceLastLine = Math.Abs(_CurrentTime - CGame.Players[i].TimeLastLineChange);
if (timeSinceLastLine < showTime)
if (timeSinceLastLine < fadeInTime)
{
_RatingPopups[_PlayerRatingPopup[i]].Alpha = 1;
var progress = timeSinceLastLine / fadeInTime;
popup.Alpha = Math.Min(1f, timeSinceLastLine / 0.12f);
popup.Scale = 0.6f + 0.4f * CHelper.EaseOutBack(progress);
popup.OffsetY = (1f - CHelper.EaseOutCubic(progress)) * popup.Rect.H * 0.5f;
}
else if (timeSinceLastLine < showTime)
{
popup.Alpha = 1;
popup.Scale = 1;
popup.OffsetY = 0;
}
else if (timeSinceLastLine < showTime + fadeOutTime)
{
_RatingPopups[_PlayerRatingPopup[i]].Alpha = 1 - (timeSinceLastLine - showTime) / fadeOutTime;
var progress = (timeSinceLastLine - showTime) / fadeOutTime;
popup.Alpha = 1 - progress;
popup.Scale = 1;
popup.OffsetY = -CHelper.EaseOutCubic(progress) * popup.Rect.H * 0.5f;
}
else
{
_RatingPopups[_PlayerRatingPopup[i]].Alpha = 0;
popup.Alpha = 0;
popup.Scale = 1;
popup.OffsetY = 0;
}
switch (CGame.Players[i].RatingLastLine)
@@ -1169,8 +1298,26 @@ namespace Vocaluxe.Screens
}
}
var pauseAnimation = _GetPauseAnimation(out var pauseAlpha, out var pauseScale);
if (pauseAnimation)
{
_SetPauseAnimationVisibility(false);
}
base.Draw();
if (pauseAnimation)
{
_SetPauseAnimationVisibility(true);
CDraw.PushTransform(1f, 0f, 0f, 0f, pauseAlpha);
_PauseAnimationBackgrounds.ForEach(element => element.Draw());
CDraw.PushTransform(pauseScale, CSettings.RenderW / 2f, CSettings.RenderH / 2f, 0f, 1f);
_PauseAnimationElements.ForEach(element => element.Draw());
CDraw.PopTransform();
CDraw.PopTransform();
_SetPauseAnimationVisibility(_PauseAnimationOpening);
}
switch (CConfig.Config.Theme.TimerLook)
{
case ETimerLook.TR_CONFIG_TIMERLOOK_NORMAL:
@@ -1186,6 +1333,7 @@ namespace Vocaluxe.Screens
}
_DrawLyricHelper();
_DrawCountdownRing();
}
private void _DrawLyricHelper()
@@ -1433,8 +1581,76 @@ namespace Vocaluxe.Screens
_SetPause(!_Pause);
}
private bool _GetPauseAnimation(out float alpha, out float scale)
{
alpha = 1f;
scale = 1f;
if (!_TimerPauseAnimation.IsRunning || CSettings.ProgramState == EProgramState.EditTheme)
{
return false;
}
var duration = _PauseAnimationOpening ? 250f : 200f;
var progress = _TimerPauseAnimation.ElapsedMilliseconds / duration;
if (progress >= 1f)
{
_TimerPauseAnimation.Stop();
return false;
}
if (_PauseAnimationOpening)
{
alpha = Math.Min(1f, progress / 0.6f);
scale = 0.85f + 0.15f * CHelper.EaseOutBack(progress);
}
else
{
alpha = 1f - progress;
scale = 1f - 0.08f * progress;
}
return true;
}
private void _SetPauseAnimationVisibility(bool visible)
{
foreach (var element in _PauseAnimationBackgrounds.Concat(_PauseAnimationElements))
{
element.Visible = visible;
}
}
private void _StorePauseAnimationElements()
{
_PauseAnimationBackgrounds.Clear();
_PauseAnimationElements.Clear();
_PauseAnimationBackgrounds.AddRange(_StaticsPause.Select(s => (IMenuElement)_Statics[s]).Where(s => s.Visible));
var elements = new List<IMenuElement>
{
_Buttons[_ButtonCancel],
_Buttons[_ButtonContinue],
_Buttons[_ButtonSkip],
_Buttons[_ButtonRestartGame],
_Buttons[_ButtonRestartRound],
_SelectSlides[_SelectSlidePauseVocalsVolume]
};
elements.AddRange(_SelectSlidesPause.Select(s => (IMenuElement)_SelectSlides[s]));
elements.AddRange(_TextsPause.Select(s => (IMenuElement)_Texts[s]));
_PauseAnimationElements.AddRange(elements.Distinct().Where(e => e.Visible));
}
private void _SetPause(bool paused)
{
var pauseChanged = paused != _Pause;
if (pauseChanged && !paused)
{
_StorePauseAnimationElements();
_PauseAnimationOpening = false;
_TimerPauseAnimation.Restart();
}
_Pause = paused;
foreach (var s in _StaticsPause)
@@ -1461,6 +1677,13 @@ namespace Vocaluxe.Screens
_SelectSlides[_SelectSlidePauseVocalsVolume].Visible = _Pause && CGame.AudioMode == EAudioMode.TR_AUDIOMODE_VOCALS;
_Texts[_TextPauseVocalsVolume].Visible = _Pause && CGame.AudioMode == EAudioMode.TR_AUDIOMODE_VOCALS;
if (pauseChanged && paused)
{
_StorePauseAnimationElements();
_PauseAnimationOpening = true;
_TimerPauseAnimation.Restart();
}
if (_StartDelayed)
{
if (_Pause)
+13
View File
@@ -372,5 +372,18 @@ namespace VocaluxeLib
return bmp;
}
public static float EaseOutBack(float t)
{
const float overshoot = 1.70158f;
t -= 1f;
return t * t * ((overshoot + 1f) * t + overshoot) + 1f;
}
public static float EaseOutCubic(float t)
{
t = 1f - t;
return 1f - t * t * t;
}
}
}
+3
View File
@@ -149,6 +149,9 @@ namespace VocaluxeLib
void DrawRect(SColorF color, SRectF rect, bool allMonitors = true);
void DrawRectReflection(SColorF color, SRectF rect, float space, float height);
void PushTransform(float scale, float centerX, float centerY, float offsetY, float alpha);
void PopTransform();
}
public interface IGraphics
+40
View File
@@ -15,6 +15,7 @@
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
#endregion
using System;
using System.Diagnostics;
using System.Xml.Serialization;
using VocaluxeLib.Draw;
@@ -74,10 +75,17 @@ namespace VocaluxeLib.Menu
}
}
private readonly Stopwatch _SelectTimer = new Stopwatch();
public override bool Selected
{
set
{
if (value && !base.Selected)
{
_SelectTimer.Restart();
}
base.Selected = value;
Text.Selected = value;
}
@@ -171,6 +179,38 @@ namespace VocaluxeLib.Menu
}
public void Draw()
{
var scale = _GetSelectionScale();
if (scale > 1f)
{
CBase.Drawing.PushTransform(scale, Rect.X + Rect.W / 2f, Rect.Y + Rect.H / 2f, 0f, 1f);
_DrawButton();
CBase.Drawing.PopTransform();
}
else
{
_DrawButton();
}
}
private float _GetSelectionScale()
{
if (!Selected || !_Selectable || !_SelectTimer.IsRunning || CBase.Settings.GetProgramState() == EProgramState.EditTheme)
{
return 1f;
}
var progress = _SelectTimer.ElapsedMilliseconds / 450f;
if (progress >= 1f)
{
_SelectTimer.Stop();
return 1f;
}
return 1f + 0.025f * (float)Math.Sin(Math.PI * progress);
}
private void _DrawButton()
{
if (!Visible && CBase.Settings.GetProgramState() != EProgramState.EditTheme)
{
+31 -3
View File
@@ -1,4 +1,5 @@
using System.Xml.Serialization;
using System;
using System.Xml.Serialization;
using VocaluxeLib.Draw;
namespace VocaluxeLib.Menu
@@ -29,6 +30,8 @@ namespace VocaluxeLib.Menu
public bool ThemeLoaded { get; private set; }
public CText Text;
public float Alpha = 1;
public float Scale = 1;
public float OffsetY;
public SColorF Color;
public CRatingPopup(int partyModeId)
@@ -65,12 +68,37 @@ namespace VocaluxeLib.Menu
color.A = Alpha;
Text.Color.A = Alpha;
var w = Rect.W * Scale;
var h = Rect.H * Scale;
var x = Rect.X + (Rect.W - w) / 2f;
var y = Rect.Y + (Rect.H - h) / 2f + OffsetY;
if (_TextureRatingPopup != null)
{
CBase.Drawing.DrawTexture(_TextureRatingPopup, Rect, color, false);
CBase.Drawing.DrawTexture(_TextureRatingPopup, new SRectF(x, y, w, h, Rect.Z), color, false);
}
Text.DrawRelative(Rect.X, Rect.Y);
if (Math.Abs(Scale - 1f) < 0.001f)
{
Text.DrawRelative(x, y);
return;
}
var textX = Text.X;
var textY = Text.Y;
var textW = Text.W;
var fontHeight = Text.Font.Height;
Text.X = textX * Scale;
Text.Y = textY * Scale;
Text.W = textW * Scale;
Text.Font.Height = fontHeight * Scale;
Text.DrawRelative(x, y);
Text.X = textX;
Text.Y = textY;
Text.W = textW;
Text.Font.Height = fontHeight;
}
public object GetTheme()
+39
View File
@@ -144,10 +144,17 @@ namespace VocaluxeLib.Menu
}
}
private readonly Stopwatch _SelectTimer = new Stopwatch();
public override bool Selected
{
set
{
if (value && !base.Selected)
{
_SelectTimer.Restart();
}
base.Selected = value;
if (!value)
@@ -582,6 +589,38 @@ namespace VocaluxeLib.Menu
}
public void Draw()
{
var scale = _GetSelectionScale();
if (scale > 1f)
{
CBase.Drawing.PushTransform(scale, Rect.X + Rect.W / 2f, Rect.Y + Rect.H / 2f, 0f, 1f);
_DrawSelectSlide();
CBase.Drawing.PopTransform();
}
else
{
_DrawSelectSlide();
}
}
private float _GetSelectionScale()
{
if (!Selected || !_SelectTimer.IsRunning || CBase.Settings.GetProgramState() == EProgramState.EditTheme)
{
return 1f;
}
var progress = _SelectTimer.ElapsedMilliseconds / 450f;
if (progress >= 1f)
{
_SelectTimer.Stop();
return 1f;
}
return 1f + 0.025f * (float)Math.Sin(Math.PI * progress);
}
private void _DrawSelectSlide()
{
if (!Visible && CBase.Settings.GetProgramState() != EProgramState.EditTheme)
{