From 5c50bd569aa6ab328eda78277ede8cdc267ff3fc Mon Sep 17 00:00:00 2001 From: Zhe Fang Date: Sun, 30 Nov 2025 20:14:47 -0500 Subject: [PATCH] chores: fix floating animation; improve layout --- .../Helper/LyricsLayoutHelper.cs | 76 +++++++++ .../Helper/ValueTransition.cs | 1 + .../Logic/LyricsLayoutManager.cs | 12 +- .../Models/LyricsLayoutMetrics.cs | 17 ++ .../Renderer/PlayingLineRenderer.cs | 9 +- .../BetterLyrics.WinUI3/Views/LyricsPage.xaml | 17 +- .../Views/LyricsPage.xaml.cs | 159 ++++++++++-------- 7 files changed, 210 insertions(+), 81 deletions(-) create mode 100644 BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/LyricsLayoutHelper.cs create mode 100644 BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsLayoutMetrics.cs diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/LyricsLayoutHelper.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/LyricsLayoutHelper.cs new file mode 100644 index 0000000..6fe8305 --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/LyricsLayoutHelper.cs @@ -0,0 +1,76 @@ +using BetterLyrics.WinUI3.Models; +using Microsoft.UI.Xaml; +using System; + +namespace BetterLyrics.WinUI3.Helper +{ + public static class LyricsLayoutHelper + { + // 硬性限制 + private const float BaseMinFontSize = 14f; + private const float BaseMaxFontSize = 80f; + private const float TargetMinVisibleLines = 5f; + private const float WidthPaddingRatio = 0.85f; + + // 比例配置 + private const float RatioSongTitle = 1f; + private const float RatioArtist = 0.85f; + private const float RatioAlbum = 0.75f; + private const float RatioTranslation = 0.7f; + private const float RatioTransliteration = 0.55f; + private const float AbsoluteMinReadableSize = 10f; + + public static LyricsLayoutMetrics CalculateLayout(double width, double height) + { + float baseSize = CalculateBaseFontSize(width, height); + + return new LyricsLayoutMetrics + { + MainLyricsSize = baseSize, + TranslationSize = ApplyRatio(baseSize, RatioTranslation), + TransliterationSize = ApplyRatio(baseSize, RatioTransliteration), + SongTitleSize = ApplyRatio(baseSize, RatioSongTitle), + ArtistNameSize = ApplyRatio(baseSize, RatioArtist), + AlbumNameSize = ApplyRatio(baseSize, RatioAlbum) + }; + } + + private static float CalculateBaseFontSize(double width, double height) + { + float usableWidth = (float)width * WidthPaddingRatio; + + // 宽度 300~500px 时,除以 14 (字大) + // 宽度 >1000px 时,除以 30 (字适中,展示更多内容) + float targetCharsPerLine; + if (width < 500) + { + targetCharsPerLine = 14f; + } + else if (width > 1000) + { + targetCharsPerLine = 30f; + } + else + { + // 平滑过渡 + float t = (float)(width - 500) / 500f; + targetCharsPerLine = 14f + 16f * t; + } + + float sizeByWidth = usableWidth / targetCharsPerLine; + float sizeByHeight = (float)height / TargetMinVisibleLines; + + float targetSize = Math.Min(sizeByWidth, sizeByHeight); + + // 窄屏时底线设高一点 (16px),宽屏如果高度不够可能允许更小 + float currentMinLimit = (width < 400) ? 16f : BaseMinFontSize; + + return Math.Clamp(targetSize, currentMinLimit, BaseMaxFontSize); + } + + private static float ApplyRatio(float baseSize, float ratio) + { + return Math.Max(baseSize * ratio, AbsoluteMinReadableSize); + } + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ValueTransition.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ValueTransition.cs index 660c457..a3e6f2f 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ValueTransition.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ValueTransition.cs @@ -27,6 +27,7 @@ namespace BetterLyrics.WinUI3.Helper public T StartValue => _startValue; public T TargetValue => _targetValue; public EasingType? EasingType => _easingType; + public double Progress => _progress; public ValueTransition(T initialValue, double durationSeconds, Func? interpolator = null, EasingType? easingType = null, double delaySeconds = 0) { diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsLayoutManager.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsLayoutManager.cs index b8226fb..eddcc17 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsLayoutManager.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsLayoutManager.cs @@ -1,4 +1,5 @@ -using BetterLyrics.WinUI3.Models; +using BetterLyrics.WinUI3.Helper; +using BetterLyrics.WinUI3.Models; using BetterLyrics.WinUI3.Models.Settings; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.UI.Xaml; @@ -41,13 +42,16 @@ namespace BetterLyrics.WinUI3.Logic if (style.IsDynamicLyricsFontSize) { - originalFontSize = (int)Math.Clamp(Math.Min(canvasHeight, canvasWidth) / 15, 18, 96); - translatedFontSize = phoneticFontSize = (int)(originalFontSize * 2.0 / 3.0); + var lyricsLayoutMetrics = LyricsLayoutHelper.CalculateLayout(canvasWidth, canvasHeight); + + phoneticFontSize = (int)lyricsLayoutMetrics.TransliterationSize; + originalFontSize = (int)lyricsLayoutMetrics.MainLyricsSize; + translatedFontSize = (int)lyricsLayoutMetrics.TranslationSize; } else { - originalFontSize = style.OriginalLyricsFontSize; phoneticFontSize = style.PhoneticLyricsFontSize; + originalFontSize = style.OriginalLyricsFontSize; translatedFontSize = style.TranslatedLyricsFontSize; } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsLayoutMetrics.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsLayoutMetrics.cs new file mode 100644 index 0000000..d8d46dc --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LyricsLayoutMetrics.cs @@ -0,0 +1,17 @@ +using Microsoft.UI.Xaml; + +namespace BetterLyrics.WinUI3.Models +{ + public struct LyricsLayoutMetrics + { + public float MainLyricsSize; + public float TranslationSize; + public float TransliterationSize; + + public float SongTitleSize; + public float ArtistNameSize; + public float AlbumNameSize; + + public Thickness AlbumArtPadding; + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/PlayingLineRenderer.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/PlayingLineRenderer.cs index 23ccef4..1638147 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/PlayingLineRenderer.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Renderer/PlayingLineRenderer.cs @@ -229,14 +229,19 @@ namespace BetterLyrics.WinUI3.Renderer if (settings.IsLyricsFloatAnimationEnabled) { - double targetFloatOffset = sourceCharRect.Height * 0.05; + double targetFloatOffset = sourceCharRect.Height * 0.1; if (charIndex < curCharIndexInt) floatOffset = 0; else if (charIndex == curCharIndexInt) { var p = exactProgressIndex - curCharIndexInt; floatOffset = -targetFloatOffset + p * targetFloatOffset; } - else floatOffset = -targetFloatOffset; + else + { + floatOffset = -targetFloatOffset; + } + // 制造句间上浮过度动画,这里用任何一个 Transition 都行,主要是获取当前行的进入视野的 Progress + floatOffset *= line.YOffsetTransition.Progress; } var parentSyllable = line.LyricsSyllables.FirstOrDefault(x => x.StartIndex <= charIndex && charIndex < x.StartIndex + x.Text.Length); diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml index 7d26dfd..b6bf56c 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml @@ -31,9 +31,9 @@ - + - + @@ -184,11 +184,14 @@ - + + + + + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml.cs index 52f7297..12e0eea 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml.cs @@ -12,7 +12,8 @@ using BetterLyrics.WinUI3.ViewModels; using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging.Messages; -using DevWinUI; +using CommunityToolkit.WinUI; +using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Documents; @@ -21,7 +22,6 @@ using Microsoft.UI.Xaml.Media.Imaging; using System; using System.Numerics; using System.Threading.Tasks; -using System.Windows.Media.Media3D; namespace BetterLyrics.WinUI3.Views { @@ -39,6 +39,8 @@ namespace BetterLyrics.WinUI3.Views private readonly IMediaSessionsService _mediaSessionsService = Ioc.Default.GetRequiredService(); private readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService(); + private readonly DispatcherQueueTimer _timer = App.Current.Resources.DispatcherQueue.CreateTimer(); + public CornerRadius AlbumArtCornerRadius { get { return (CornerRadius)GetValue(AlbumArtCornerRadiusProperty); } @@ -48,16 +50,6 @@ namespace BetterLyrics.WinUI3.Views public static readonly DependencyProperty AlbumArtCornerRadiusProperty = DependencyProperty.Register(nameof(AlbumArtCornerRadius), typeof(double), typeof(NowPlayingCanvas), new PropertyMetadata(new CornerRadius(0))); - - public double AlbumArtSize - { - get { return (double)GetValue(AlbumArtSizeProperty); } - set { SetValue(AlbumArtSizeProperty, value); } - } - - public static readonly DependencyProperty AlbumArtSizeProperty = - DependencyProperty.Register(nameof(AlbumArtSize), typeof(double), typeof(NowPlayingCanvas), new PropertyMetadata(0.0)); - public LyricsPageViewModel ViewModel => (LyricsPageViewModel)DataContext; public LyricsPage() @@ -85,25 +77,7 @@ namespace BetterLyrics.WinUI3.Views // ==== SongInfo - private int GetTitleFontSize() - { - var albumArtLayoutSettings = _liveStatesService.LiveStates.LyricsWindowStatus.AlbumArtLayoutSettings; - if (albumArtLayoutSettings.IsAutoSongInfoFontSize) - { - return (int)Math.Clamp(Math.Min(RootGrid.ActualHeight, RootGrid.ActualWidth) / 20, 8, 72); - } - else - { - return albumArtLayoutSettings.SongInfoFontSize; - } - } - - private int GetArtistsAlbumFontSize() - { - return (int)(GetTitleFontSize() * 0.8); - } - - private void RenderTextBlock(TextBlock? sender, string? text, int fontSize) + private void RenderTextBlock(TextBlock? sender, string? text, double fontSize) { if (sender == null || string.IsNullOrEmpty(text) || fontSize == 0) return; @@ -115,28 +89,23 @@ namespace BetterLyrics.WinUI3.Views var fontFamilyName = LanguageHelper.IsCJK(ch) ? lyricsStyleSettings.LyricsCJKFontFamily : lyricsStyleSettings.LyricsWesternFontFamily; sender.Inlines.Add(new Run { Text = $"{ch}", FontFamily = new FontFamily(fontFamilyName) }); } - sender.FontSize = fontSize; + sender.FontSize = (int)fontSize; sender.Foreground = new SolidColorBrush(_mediaSessionsService.AlbumArtThemeColors.BgFontColor); } - private void RenderTextBlock(AnimatedTextBlock? sender, string? text, int fontSize) - { - if (sender == null || string.IsNullOrEmpty(text) || fontSize == 0) return; - - var lyricsStyleSettings = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings; - var fontFamilyName = LanguageHelper.IsCJK(text) ? lyricsStyleSettings.LyricsCJKFontFamily : lyricsStyleSettings.LyricsWesternFontFamily; - - sender.FontFamily = new Microsoft.UI.Xaml.Media.FontFamily(fontFamilyName); - sender.FontSize = fontSize; - - sender.Text = text; - } - private void RenderSongInfo() { - RenderTextBlock(TitleTextBlock, _mediaSessionsService.CurrentSongInfo?.Title, GetTitleFontSize()); - RenderTextBlock(ArtistsTextBlock, _mediaSessionsService.CurrentSongInfo?.DisplayArtists, GetArtistsAlbumFontSize()); - RenderTextBlock(AlbumTextBlock, _mediaSessionsService.CurrentSongInfo?.Album, GetArtistsAlbumFontSize()); + var lyricsLayoutMetrics = LyricsLayoutHelper.CalculateLayout(RootGrid.ActualWidth, RootGrid.ActualHeight); + + var albumArtLayoutSettings = _liveStatesService.LiveStates.LyricsWindowStatus.AlbumArtLayoutSettings; + + var titleFontSize = albumArtLayoutSettings.IsAutoSongInfoFontSize ? lyricsLayoutMetrics.SongTitleSize : albumArtLayoutSettings.SongInfoFontSize; + var artistsFontSize = albumArtLayoutSettings.IsAutoSongInfoFontSize ? lyricsLayoutMetrics.ArtistNameSize : albumArtLayoutSettings.SongInfoFontSize * 0.8; + var albumFontSize = albumArtLayoutSettings.IsAutoSongInfoFontSize ? lyricsLayoutMetrics.AlbumNameSize : albumArtLayoutSettings.SongInfoFontSize * 0.8; + + RenderTextBlock(TitleTextBlock, _mediaSessionsService.CurrentSongInfo?.Title, titleFontSize); + RenderTextBlock(ArtistsTextBlock, _mediaSessionsService.CurrentSongInfo?.DisplayArtists, artistsFontSize); + RenderTextBlock(AlbumTextBlock, _mediaSessionsService.CurrentSongInfo?.Album, albumFontSize); } private void UpdateSongInfoOpacity() @@ -246,6 +215,43 @@ namespace BetterLyrics.WinUI3.Views } } + private void UpdateLyricsPlaceholderSpan() + { + var status = _liveStatesService.LiveStates.LyricsWindowStatus; + switch (status.LyricsDisplayType) + { + case LyricsDisplayType.AlbumArtOnly: + break; + case LyricsDisplayType.LyricsOnly: + Grid.SetRow(LyricsPlaceholder, 0); + Grid.SetRowSpan(LyricsPlaceholder, 3); + Grid.SetColumn(LyricsPlaceholder, 1); + Grid.SetColumnSpan(LyricsPlaceholder, 3); + break; + case LyricsDisplayType.SplitView: + switch (status.LyricsLayoutOrientation) + { + case LyricsLayoutOrientation.Horizontal: + Grid.SetRow(LyricsPlaceholder, 0); + Grid.SetRowSpan(LyricsPlaceholder, 3); + Grid.SetColumn(LyricsPlaceholder, 3); + Grid.SetColumnSpan(LyricsPlaceholder, 1); + break; + case LyricsLayoutOrientation.Vertical: + Grid.SetRow(LyricsPlaceholder, 2); + Grid.SetRowSpan(LyricsPlaceholder, 1); + Grid.SetColumn(LyricsPlaceholder, 1); + Grid.SetColumnSpan(LyricsPlaceholder, 3); + break; + default: + break; + } + break; + default: + break; + } + } + // ==== private void UpdateAlbumArtGridSpan() @@ -329,15 +335,19 @@ namespace BetterLyrics.WinUI3.Views private void UpdateGap() { + var lyricsLayoutMetrics = LyricsLayoutHelper.CalculateLayout(RootGrid.ActualWidth, RootGrid.ActualHeight); + var status = _liveStatesService.LiveStates.LyricsWindowStatus; double height = RootGrid.ActualHeight; double width = RootGrid.ActualWidth; - double xMargin = 0; - double yMargin = 0; double middleGapCol = 0; double gapBetweenAlbumArtAndSongInfo = 0; + double trackSummaryRowHeight = 0; + + double xMargin = 0; + double yMargin = 0; if (height < 400) { @@ -354,18 +364,19 @@ namespace BetterLyrics.WinUI3.Views } else { - gapBetweenAlbumArtAndSongInfo = GetTitleFontSize() / 2; + gapBetweenAlbumArtAndSongInfo = lyricsLayoutMetrics.SongTitleSize / 2; } switch (status.LyricsLayoutOrientation) { case LyricsLayoutOrientation.Horizontal: - xMargin = Math.Max(16, Math.Min(width, height) * 0.25); - yMargin = Math.Max(16, Math.Min(width, height) * 0.15); + xMargin = Math.Clamp(Math.Min(width, height) * 0.25, 16, 128); + yMargin = Math.Max(32, Math.Min(width, height) * 0.15); break; case LyricsLayoutOrientation.Vertical: - xMargin = Math.Max(16, Math.Min(width, height) * 0.15); - yMargin = Math.Max(16, Math.Min(width, height) * 0.10); + xMargin = Math.Max(16, Math.Min(width, height) * 0.05); + yMargin = Math.Max(16, Math.Min(width, height) * 0.05); + trackSummaryRowHeight = Math.Max(64, Math.Min(width, height) * 0.25); break; default: break; @@ -373,30 +384,36 @@ namespace BetterLyrics.WinUI3.Views MiddleGapColDef.Width = new(middleGapCol); - TrackSummaryGridRow0.Height = TrackSummaryGridRow4.Height = new(yMargin); + TrackSummaryGridRow0.Height = new(yMargin); + TrackSummaryGridRow4.Height = new(yMargin); LeftGapDef.Width = RightGapDef.Width = new(xMargin); + + TrackSummaryRowDef.Height = new(trackSummaryRowHeight); + TrackSummaryGridCol1.Width = TrackSummaryGridRow2.Height = new(gapBetweenAlbumArtAndSongInfo); } private void OnLayoutChanged() { - UpdateSongInfoOpacity(); + _timer.Debounce(() => + { + UpdateGap(); - UpdateAlbumArtShadow(); - UpdateAlbumArtOpacity(); + UpdateSongInfoOpacity(); + UpdateLyricsOpacity(); + UpdateAlbumArtOpacity(); - UpdateTrackSummaryGridSpan(); + UpdateAlbumArtShadow(); - UpdateAlbumArtGridSpan(); + UpdateTrackSummaryGridSpan(); + UpdateAlbumArtGridSpan(); + UpdateSongInfoStackPanelSpan(); + UpdateLyricsPlaceholderSpan(); - UpdateSongInfoStackPanelSpan(); + UpdateAlbumArtCornerRadius(); - UpdateLyricsOpacity(); - UpdateLyricsLayout(); - - UpdateAlbumArtCornerRadius(); - - UpdateGap(); + UpdateLyricsLayout(); + }, Constants.Time.DebounceTimeout); } // ==== @@ -763,5 +780,11 @@ namespace BetterLyrics.WinUI3.Views } } + private void LyricsScrollViewer_PointerWheelChanged(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) + { + var pointerPoint = e.GetCurrentPoint(LyricsScrollViewer); + int mouseWheelDelta = pointerPoint.Properties.MouseWheelDelta; + NowPlayingCanvas.LyricsStartY += mouseWheelDelta; + } } }