diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest b/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest
index a6096ae..76c045b 100644
--- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest
@@ -12,7 +12,7 @@
+ Version="1.2.251.0" />
diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs
index dcb4068..14fdd0b 100644
--- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs
@@ -36,38 +36,73 @@ namespace BetterLyrics.WinUI3.Logic
double currentPositionMs
)
{
- if (lines == null) return;
+ if (lines == null || lines.Count == 0) return;
- var currentPlayingLine = lines.ElementAtOrDefault(primaryPlayingLineIndex);
- if (currentPlayingLine == null) return;
+ if (primaryPlayingLineIndex < 0 || primaryPlayingLineIndex >= lines.Count) return;
+ var primaryPlayingLine = lines[primaryPlayingLineIndex];
var phoneticOpacity = lyricsStyle.PhoneticLyricsOpacity / 100.0;
var originalOpacity = lyricsStyle.OriginalLyricsOpacity / 100.0;
var translatedOpacity = lyricsStyle.TranslatedLyricsOpacity / 100.0;
- for (int i = startIndex; i <= endIndex + 1; i++)
+ double topHeightFactor = canvasHeight * playingLineTopOffsetFactor;
+ double bottomHeightFactor = canvasHeight * (1 - playingLineTopOffsetFactor);
+
+ double scrollTopDurationSec = lyricsEffect.LyricsScrollTopDuration / 1000.0;
+ double scrollTopDelaySec = lyricsEffect.LyricsScrollTopDelay / 1000.0;
+ double scrollBottomDurationSec = lyricsEffect.LyricsScrollBottomDuration / 1000.0;
+ double scrollBottomDelaySec = lyricsEffect.LyricsScrollBottomDelay / 1000.0;
+ double canvasTransDuration = canvasYScrollTransition.DurationSeconds;
+
+ bool isBlurEnabled = lyricsEffect.IsLyricsBlurEffectEnabled;
+ bool isOutOfSightEnabled = lyricsEffect.IsLyricsOutOfSightEffectEnabled;
+ bool isFanEnabled = lyricsEffect.IsFanLyricsEnabled;
+ double fanAngleRad = Math.PI * (lyricsEffect.FanLyricsAngle / 180.0);
+ bool isGlowEnabled = lyricsEffect.IsLyricsGlowEffectEnabled;
+ bool isFloatEnabled = lyricsEffect.IsLyricsFloatAnimationEnabled;
+ bool isScaleEnabled = lyricsEffect.IsLyricsScaleEffectEnabled;
+
+ int safeStart = Math.Max(0, startIndex);
+ int safeEnd = Math.Min(lines.Count - 1, endIndex + 1);
+
+ for (int i = safeStart; i <= safeEnd; i++)
{
- var line = lines.ElementAtOrDefault(i);
- if (line == null) continue;
+ var line = lines[i];
+
+ var lineHeight = line.PrimaryLineHeight;
+
+ if (lineHeight == null || lineHeight <= 0) continue;
+
+ double targetCharFloat = lyricsEffect.IsLyricsFloatAnimationAmountAutoAdjust
+ ? lineHeight.Value * 0.1
+ : lyricsEffect.LyricsFloatAnimationAmount;
+ double targetCharGlow = lyricsEffect.IsLyricsGlowEffectAmountAutoAdjust
+ ? lineHeight.Value * 0.2
+ : lyricsEffect.LyricsGlowEffectAmount;
+ double targetCharScale = lyricsEffect.IsLyricsScaleEffectAmountAutoAdjust
+ ? 1.15
+ : lyricsEffect.LyricsScaleEffectAmount / 100.0;
+
+ var maxAnimationDurationMs = Math.Max(line.EndMs - currentPositionMs, 0);
bool isSecondaryLinePlaying = line.GetIsPlaying(currentPositionMs);
bool isSecondaryLinePlayingChanged = line.IsPlayingLastFrame != isSecondaryLinePlaying;
line.IsPlayingLastFrame = isSecondaryLinePlaying;
// 行动画
- if (isLayoutChanged || isPrimaryPlayingLineChanged || isMouseScrollingChanged || isSecondaryLinePlayingChanged)
+ if (isLayoutChanged || isPrimaryPlayingLineChanged || isMouseScrollingChanged)
{
int lineCountDelta = i - primaryPlayingLineIndex;
- double distanceFromPlayingLine = Math.Abs(line.PrimaryPosition.Y - currentPlayingLine.PrimaryPosition.Y);
+ double distanceFromPlayingLine = Math.Abs(line.PrimaryPosition.Y - primaryPlayingLine.PrimaryPosition.Y);
double distanceFactor;
if (lineCountDelta < 0)
{
- distanceFactor = Math.Clamp(distanceFromPlayingLine / (canvasHeight * playingLineTopOffsetFactor), 0, 1);
+ distanceFactor = Math.Clamp(distanceFromPlayingLine / topHeightFactor, 0, 1);
}
else
{
- distanceFactor = Math.Clamp(distanceFromPlayingLine / (canvasHeight * (1 - playingLineTopOffsetFactor)), 0, 1);
+ distanceFactor = Math.Clamp(distanceFromPlayingLine / bottomHeightFactor, 0, 1);
}
double yScrollDuration;
@@ -76,34 +111,34 @@ namespace BetterLyrics.WinUI3.Logic
if (lineCountDelta < 0)
{
yScrollDuration =
- canvasYScrollTransition.DurationSeconds +
- distanceFactor * (lyricsEffect.LyricsScrollTopDuration / 1000.0 - canvasYScrollTransition.DurationSeconds);
- yScrollDelay = distanceFactor * lyricsEffect.LyricsScrollTopDelay / 1000.0;
+ canvasTransDuration +
+ distanceFactor * (scrollTopDurationSec - canvasTransDuration);
+ yScrollDelay = distanceFactor * scrollTopDelaySec;
}
else if (lineCountDelta == 0)
{
- yScrollDuration = canvasYScrollTransition.DurationSeconds;
+ yScrollDuration = canvasTransDuration;
yScrollDelay = 0;
}
else
{
yScrollDuration =
- canvasYScrollTransition.DurationSeconds +
- distanceFactor * (lyricsEffect.LyricsScrollBottomDuration / 1000.0 - canvasYScrollTransition.DurationSeconds);
- yScrollDelay = distanceFactor * lyricsEffect.LyricsScrollBottomDelay / 1000.0;
+ canvasTransDuration +
+ distanceFactor * (scrollBottomDurationSec - canvasTransDuration);
+ yScrollDelay = distanceFactor * scrollBottomDelaySec;
}
line.BlurAmountTransition.SetDuration(yScrollDuration);
line.BlurAmountTransition.SetDelay(yScrollDelay);
line.BlurAmountTransition.Start(
(isMouseScrolling || isSecondaryLinePlaying) ? 0 :
- (lyricsEffect.IsLyricsBlurEffectEnabled ? (5 * distanceFactor) : 0));
+ (isBlurEnabled ? (5 * distanceFactor) : 0));
line.ScaleTransition.SetDuration(yScrollDuration);
line.ScaleTransition.SetDelay(yScrollDelay);
line.ScaleTransition.Start(
isSecondaryLinePlaying ? _highlightedScale :
- (lyricsEffect.IsLyricsOutOfSightEffectEnabled ?
+ (isOutOfSightEnabled ?
(_highlightedScale - distanceFactor * (_highlightedScale - _defaultScale)) :
_highlightedScale));
@@ -140,8 +175,8 @@ namespace BetterLyrics.WinUI3.Logic
line.AngleTransition.SetDuration(yScrollDuration);
line.AngleTransition.SetDelay(yScrollDelay);
line.AngleTransition.Start(
- (lyricsEffect.IsFanLyricsEnabled && !isMouseScrolling) ?
- Math.PI * (lyricsEffect.FanLyricsAngle / 180.0) * distanceFactor * (i > primaryPlayingLineIndex ? 1 : -1) :
+ (isFanEnabled && !isMouseScrolling) ?
+ Math.PI * (fanAngleRad / 180.0) * distanceFactor * (i > primaryPlayingLineIndex ? 1 : -1) :
0);
line.YOffsetTransition.SetEasingType(canvasYScrollTransition.EasingType);
@@ -152,7 +187,33 @@ namespace BetterLyrics.WinUI3.Logic
line.YOffsetTransition.Start(targetYScrollOffset);
}
- var maxAnimationDurationMs = Math.Max(line.EndMs - currentPositionMs, 0);
+ if (isLayoutChanged || isSecondaryLinePlayingChanged)
+ {
+ // 辉光动画
+ if (isGlowEnabled && lyricsEffect.LyricsGlowEffectScope == Enums.LyricsEffectScope.LineStartToCurrentChar
+ && isSecondaryLinePlaying)
+ {
+ foreach (var renderChar in line.PrimaryRenderChars)
+ {
+ var stepInOutDuration = Math.Min(Time.AnimationDuration.TotalMilliseconds, maxAnimationDurationMs) / 2.0 / 1000.0;
+ var stepLastingDuration = Math.Max(maxAnimationDurationMs / 1000.0 - stepInOutDuration * 2, 0);
+ renderChar.GlowTransition.Start(
+ new Models.Keyframe(targetCharGlow, stepInOutDuration),
+ new Models.Keyframe(targetCharGlow, stepLastingDuration),
+ new Models.Keyframe(0, stepInOutDuration)
+ );
+ }
+ }
+
+ // 浮动动画
+ if (isFloatEnabled)
+ {
+ foreach (var renderChar in line.PrimaryRenderChars)
+ {
+ renderChar.FloatTransition.Start(isSecondaryLinePlaying ? targetCharFloat : 0);
+ }
+ }
+ }
// 字符动画
foreach (var renderChar in line.PrimaryRenderChars)
@@ -162,50 +223,15 @@ namespace BetterLyrics.WinUI3.Logic
bool isCharPlaying = renderChar.GetIsPlaying(currentPositionMs);
bool isCharPlayingChanged = renderChar.IsPlayingLastFrame != isCharPlaying;
- if (isSecondaryLinePlayingChanged || isCharPlayingChanged)
+ if (isCharPlayingChanged)
{
- if (lyricsEffect.IsLyricsGlowEffectEnabled)
+ if (isFloatEnabled)
{
- double targetGlow = lyricsEffect.IsLyricsGlowEffectAmountAutoAdjust ? renderChar.LayoutRect.Height * 0.2 : lyricsEffect.LyricsGlowEffectAmount;
- switch (lyricsEffect.LyricsGlowEffectScope)
- {
- case Enums.LyricsEffectScope.LineStartToCurrentChar:
- if (isSecondaryLinePlayingChanged && isSecondaryLinePlaying)
- {
- var stepInOutDuration = Math.Min(Time.AnimationDuration.TotalMilliseconds, maxAnimationDurationMs) / 2.0 / 1000.0;
- var stepLastingDuration = Math.Max(maxAnimationDurationMs / 1000.0 - stepInOutDuration * 2, 0);
- renderChar.GlowTransition.Start(
- new Models.Keyframe(targetGlow, stepInOutDuration),
- new Models.Keyframe(targetGlow, stepLastingDuration),
- new Models.Keyframe(0, stepInOutDuration)
- );
- }
- break;
- default:
- break;
- }
+ renderChar.FloatTransition.SetDurationMs(Math.Min(lyricsEffect.LyricsFloatAnimationDuration, maxAnimationDurationMs));
+ renderChar.FloatTransition.Start(0);
}
- if (lyricsEffect.IsLyricsFloatAnimationEnabled)
- {
- double targetFloat =
- lyricsEffect.IsLyricsFloatAnimationAmountAutoAdjust ? renderChar.LayoutRect.Height * 0.1 : lyricsEffect.LyricsFloatAnimationAmount;
-
- if (isSecondaryLinePlayingChanged)
- {
- renderChar.FloatTransition.Start(isSecondaryLinePlaying ? targetFloat : 0);
- }
- if (isCharPlayingChanged)
- {
- renderChar.FloatTransition.SetDurationMs(Math.Min(lyricsEffect.LyricsFloatAnimationDuration, maxAnimationDurationMs));
- renderChar.FloatTransition.Start(0);
- }
- }
-
- if (isCharPlayingChanged)
- {
- renderChar.IsPlayingLastFrame = isCharPlaying;
- }
+ renderChar.IsPlayingLastFrame = isCharPlaying;
}
}
@@ -217,52 +243,31 @@ namespace BetterLyrics.WinUI3.Logic
if (isSyllablePlayingChanged)
{
- var syllableHeight = syllable.ChildrenRenderLyricsChars.FirstOrDefault()?.LayoutRect.Height ?? 0;
-
- if (lyricsEffect.IsLyricsScaleEffectEnabled)
+ if (isScaleEnabled && isSyllablePlaying)
{
- double targetScale =
- lyricsEffect.IsLyricsScaleEffectAmountAutoAdjust ? 1.15 : lyricsEffect.LyricsScaleEffectAmount / 100.0;
-
foreach (var renderChar in syllable.ChildrenRenderLyricsChars)
{
if (syllable.DurationMs >= lyricsEffect.LyricsScaleEffectLongSyllableDuration)
{
- if (isSyllablePlaying)
- {
- var stepDuration = Math.Min(syllable.DurationMs, maxAnimationDurationMs) / 2.0 / 1000.0;
- renderChar.ScaleTransition.Start(
- new Models.Keyframe(targetScale, stepDuration),
- new Models.Keyframe(1.0, stepDuration)
- );
- }
+ var stepDuration = Math.Min(syllable.DurationMs, maxAnimationDurationMs) / 2.0 / 1000.0;
+ renderChar.ScaleTransition.Start(
+ new Models.Keyframe(targetCharScale, stepDuration),
+ new Models.Keyframe(1.0, stepDuration)
+ );
}
}
}
- if (lyricsEffect.IsLyricsGlowEffectEnabled)
+ if (isGlowEnabled && isSyllablePlaying && lyricsEffect.LyricsGlowEffectScope == Enums.LyricsEffectScope.LongDurationSyllable
+ && syllable.DurationMs >= lyricsEffect.LyricsGlowEffectLongSyllableDuration)
{
- double targetGlow = lyricsEffect.IsLyricsGlowEffectAmountAutoAdjust ? syllableHeight * 0.2 : lyricsEffect.LyricsGlowEffectAmount;
- switch (lyricsEffect.LyricsGlowEffectScope)
+ foreach (var renderChar in syllable.ChildrenRenderLyricsChars)
{
- case Enums.LyricsEffectScope.LongDurationSyllable:
- if (syllable.DurationMs >= lyricsEffect.LyricsGlowEffectLongSyllableDuration)
- {
- foreach (var renderChar in syllable.ChildrenRenderLyricsChars)
- {
- if (isSyllablePlaying)
- {
- var stepDuration = Math.Min(syllable.DurationMs, maxAnimationDurationMs) / 2.0 / 1000.0;
- renderChar.GlowTransition.Start(
- new Models.Keyframe(targetGlow, stepDuration),
- new Models.Keyframe(0, stepDuration)
- );
- }
- }
- }
- break;
- default:
- break;
+ var stepDuration = Math.Min(syllable.DurationMs, maxAnimationDurationMs) / 2.0 / 1000.0;
+ renderChar.GlowTransition.Start(
+ new Models.Keyframe(targetCharGlow, stepDuration),
+ new Models.Keyframe(0, stepDuration)
+ );
}
}
@@ -270,7 +275,7 @@ namespace BetterLyrics.WinUI3.Logic
}
}
- // 更新动画
+ // 使动画步进一帧
foreach (var renderChar in line.PrimaryRenderChars)
{
renderChar.Update(elapsedTime);
diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsLine.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsLine.cs
index 7322cbe..7d0d137 100644
--- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsLine.cs
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsLine.cs
@@ -74,6 +74,8 @@ namespace BetterLyrics.WinUI3.Models.Lyrics
///
public int LaneIndex { get; set; } = 0;
+ public double? PrimaryLineHeight => PrimaryRenderChars.FirstOrDefault()?.LayoutRect.Height;
+
public RenderLyricsLine(LyricsLine lyricsLine) : base(lyricsLine)
{
AngleTransition = new(
diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/PlayingLineRenderer.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/PlayingLineRenderer.cs
index 41125e6..5121548 100644
--- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/PlayingLineRenderer.cs
+++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/PlayingLineRenderer.cs
@@ -26,12 +26,12 @@ namespace BetterLyrics.WinUI3.Renderer
Color fgColor,
LyricsEffectSettings settings)
{
- DrawPhonetic(ds, textOnlyLayer, line);
- DrawOriginalText(control, ds, textOnlyLayer, line, playbackState, bgColor, fgColor, settings);
- DrawTranslated(ds, textOnlyLayer, line);
+ DrawTertiaryText(ds, textOnlyLayer, line);
+ DrawPrimaryText(control, ds, textOnlyLayer, line, playbackState, bgColor, fgColor, settings);
+ DrawSecondaryText(ds, textOnlyLayer, line);
}
- private void DrawPhonetic(CanvasDrawingSession ds, ICanvasImage source, RenderLyricsLine line)
+ private void DrawTertiaryText(CanvasDrawingSession ds, ICanvasImage source, RenderLyricsLine line)
{
if (line.TertiaryTextLayout == null) return;
@@ -65,7 +65,7 @@ namespace BetterLyrics.WinUI3.Renderer
});
}
- private void DrawTranslated(CanvasDrawingSession ds, ICanvasImage source, RenderLyricsLine line)
+ private void DrawSecondaryText(CanvasDrawingSession ds, ICanvasImage source, RenderLyricsLine line)
{
if (line.SecondaryTextLayout == null) return;
@@ -99,7 +99,7 @@ namespace BetterLyrics.WinUI3.Renderer
});
}
- private void DrawOriginalText(
+ private void DrawPrimaryText(
ICanvasResourceCreator resourceCreator,
CanvasDrawingSession ds,
ICanvasImage source,