diff --git a/UntamedMusicPlayer/MainWindow.xaml b/UntamedMusicPlayer/MainWindow.xaml index bfb5a6c..d073893 100644 --- a/UntamedMusicPlayer/MainWindow.xaml +++ b/UntamedMusicPlayer/MainWindow.xaml @@ -31,14 +31,14 @@ + Duration="0:0:0.6"> - + + To="0" Duration="0:0:0.6"/> private DateTimeOffset _shellFrameMarginAnimationStart; private double _shellFrameMarginFrom; private double _shellFrameMarginTo; + private double _shellFrameMarginAnimationDuration; // 热键 ID private const int HOTKEY_ID_VOLUME_UP = 1; @@ -96,9 +97,10 @@ public sealed partial class MainWindow : WindowEx, IRecipient private void OnRootPlayBarChanged(object? sender, PropertyChangedEventArgs e) { if ( - e.PropertyName - is nameof(RootPlayBarViewModel.IsDetail) - or nameof(RootPlayBarViewModel.IsFullScreen) + Settings.IsAutoHidePlaybackControlBar + && e.PropertyName + is nameof(RootPlayBarViewModel.IsDetail) + or nameof(RootPlayBarViewModel.IsFullScreen) ) { if (Data.RootPlayBarViewModel!.IsDetail && Data.RootPlayBarViewModel.IsFullScreen) @@ -110,7 +112,7 @@ public sealed partial class MainWindow : WindowEx, IRecipient RootGrid.PointerMoved -= RootGrid_PointerMoved; if (_isPlayBarHidden) { - AnimateShellFrameBottomMargin(117); + AnimateShellFrameBottomMargin(117, 300); ShowPlayBarStoryboard.Begin(); _isPlayBarHidden = false; StopPlayBarTimer(); @@ -129,7 +131,7 @@ public sealed partial class MainWindow : WindowEx, IRecipient { if (_isPlayBarHidden) { - AnimateShellFrameBottomMargin(117); + AnimateShellFrameBottomMargin(117, 300); ShowPlayBarStoryboard.Begin(); _isPlayBarHidden = false; } @@ -154,7 +156,7 @@ public sealed partial class MainWindow : WindowEx, IRecipient && !_isPlayBarHidden ) { - AnimateShellFrameBottomMargin(0); + AnimateShellFrameBottomMargin(0, 600); HidePlayBarStoryboard.Begin(); _isPlayBarHidden = true; } @@ -186,7 +188,7 @@ public sealed partial class MainWindow : WindowEx, IRecipient } } - private void AnimateShellFrameBottomMargin(double targetBottom) + private void AnimateShellFrameBottomMargin(double targetBottom, double durationMs) { var currentBottom = ShellFrame.Margin.Bottom; if (Math.Abs(currentBottom - targetBottom) < 0.1) @@ -203,6 +205,7 @@ public sealed partial class MainWindow : WindowEx, IRecipient _shellFrameMarginFrom = currentBottom; _shellFrameMarginTo = targetBottom; + _shellFrameMarginAnimationDuration = durationMs; _shellFrameMarginAnimationStart = DateTimeOffset.Now; _shellFrameMarginAnimationTimer.Start(); @@ -213,10 +216,15 @@ public sealed partial class MainWindow : WindowEx, IRecipient object args ) { - const double durationMs = 300; var elapsedMs = (DateTimeOffset.Now - _shellFrameMarginAnimationStart).TotalMilliseconds; - var progress = Math.Clamp(elapsedMs / durationMs, 0d, 1d); - var easedProgress = 1 - Math.Pow(1 - progress, 3); + var progress = Math.Clamp(elapsedMs / _shellFrameMarginAnimationDuration, 0d, 1d); + + // 显示 (To > From): EaseOut (1 - (1-x)^3) + // 隐藏 (To < From): EaseIn (x^3) + var easedProgress = + _shellFrameMarginTo > _shellFrameMarginFrom + ? 1 - Math.Pow(1 - progress, 3) + : Math.Pow(progress, 3); var currentBottom = _shellFrameMarginFrom + ((_shellFrameMarginTo - _shellFrameMarginFrom) * easedProgress); diff --git a/UntamedMusicPlayer/Models/Settings.cs b/UntamedMusicPlayer/Models/Settings.cs index a187206..aa29cff 100644 --- a/UntamedMusicPlayer/Models/Settings.cs +++ b/UntamedMusicPlayer/Models/Settings.cs @@ -204,7 +204,22 @@ public static class Settings } } } - #endregion + + /// + /// 是否在全屏模式下自动隐藏播放控制栏 + /// + public static bool IsAutoHidePlaybackControlBar + { + get; + set + { + if (field != value) + { + field = value; + _localSettingsService.SaveSettingAsync(nameof(IsAutoHidePlaybackControlBar), value); + } + } + } /// /// 是否启用均衡器 @@ -237,6 +252,7 @@ public static class Settings } } } + #endregion public static async Task InitializeAsync() { @@ -266,6 +282,9 @@ public static class Settings IsWindowBackgroundFollowsCover = await _localSettingsService.ReadSettingAsync( nameof(IsWindowBackgroundFollowsCover) ); + IsAutoHidePlaybackControlBar = await _localSettingsService.ReadSettingAsync( + nameof(IsAutoHidePlaybackControlBar) + ); if (NotFirstUsed) { @@ -292,6 +311,7 @@ public static class Settings var lightColor = Color.FromArgb(255, 252, 252, 252); TintColor = App.Current.RequestedTheme == ApplicationTheme.Dark ? darkColor : lightColor; + IsAutoHidePlaybackControlBar = true; } InitializedLaterAsync(); } diff --git a/UntamedMusicPlayer/Strings/en-us/Resources.resw b/UntamedMusicPlayer/Strings/en-us/Resources.resw index 97acdf6..0860811 100644 --- a/UntamedMusicPlayer/Strings/en-us/Resources.resw +++ b/UntamedMusicPlayer/Strings/en-us/Resources.resw @@ -213,6 +213,9 @@ Refresh libraries + + Automatically hide the playback control bar in full screen mode + Exclusive mode (Experimental) diff --git a/UntamedMusicPlayer/Strings/zh-cn/Resources.resw b/UntamedMusicPlayer/Strings/zh-cn/Resources.resw index 7c3b964..fd73602 100644 --- a/UntamedMusicPlayer/Strings/zh-cn/Resources.resw +++ b/UntamedMusicPlayer/Strings/zh-cn/Resources.resw @@ -213,6 +213,9 @@ 刷新库 + + 在全屏模式下自动隐藏播控栏 + 独占模式 (实验性) diff --git a/UntamedMusicPlayer/Styles/ColorStyle.xaml b/UntamedMusicPlayer/Styles/ColorStyle.xaml index 6df0a2b..a714edd 100644 --- a/UntamedMusicPlayer/Styles/ColorStyle.xaml +++ b/UntamedMusicPlayer/Styles/ColorStyle.xaml @@ -1,14 +1,12 @@ - - diff --git a/UntamedMusicPlayer/ViewModels/SettingsViewModel.cs b/UntamedMusicPlayer/ViewModels/SettingsViewModel.cs index 75a37ce..53d804e 100644 --- a/UntamedMusicPlayer/ViewModels/SettingsViewModel.cs +++ b/UntamedMusicPlayer/ViewModels/SettingsViewModel.cs @@ -194,6 +194,18 @@ public sealed partial class SettingsViewModel _dynamicBackgroundService.IsEnabled = value; } + /// + /// 是否在全屏模式下自动隐藏播放控制栏 + /// + [ObservableProperty] + public partial bool IsAutoHidePlaybackControlBar { get; set; } = + Settings.IsAutoHidePlaybackControlBar; + + partial void OnIsAutoHidePlaybackControlBarChanged(bool value) + { + Settings.IsAutoHidePlaybackControlBar = value; + } + /// /// 版本信息 /// diff --git a/UntamedMusicPlayer/Views/LocalAlbumDetailPage.xaml.cs b/UntamedMusicPlayer/Views/LocalAlbumDetailPage.xaml.cs index be4b4d3..1a8e16d 100644 --- a/UntamedMusicPlayer/Views/LocalAlbumDetailPage.xaml.cs +++ b/UntamedMusicPlayer/Views/LocalAlbumDetailPage.xaml.cs @@ -158,7 +158,7 @@ public sealed partial class LocalAlbumDetailPage : Page backgroundScaleFactorNode, progressNode ); - ExpressionNode backgroundOpacityAnimation = progressNode; + ExpressionNode backgroundOpacityAnimation = progressNode * 0.7f; backgroundVisual.StartAnimation("Scale.Y", backgroundScaleAnimation); backgroundVisual.StartAnimation("Opacity", backgroundOpacityAnimation); diff --git a/UntamedMusicPlayer/Views/LocalArtistDetailPage.xaml.cs b/UntamedMusicPlayer/Views/LocalArtistDetailPage.xaml.cs index 0383fb6..b190bdf 100644 --- a/UntamedMusicPlayer/Views/LocalArtistDetailPage.xaml.cs +++ b/UntamedMusicPlayer/Views/LocalArtistDetailPage.xaml.cs @@ -181,7 +181,7 @@ public sealed partial class LocalArtistDetailPage : Page backgroundScaleFactorNode, progressNode ); - ExpressionNode backgroundOpacityAnimation = progressNode; + ExpressionNode backgroundOpacityAnimation = progressNode * 0.7f; backgroundVisual.StartAnimation("Scale.Y", backgroundScaleAnimation); backgroundVisual.StartAnimation("Opacity", backgroundOpacityAnimation); diff --git a/UntamedMusicPlayer/Views/LyricPage.xaml b/UntamedMusicPlayer/Views/LyricPage.xaml index 0d02883..87b3617 100644 --- a/UntamedMusicPlayer/Views/LyricPage.xaml +++ b/UntamedMusicPlayer/Views/LyricPage.xaml @@ -69,14 +69,14 @@ + Duration="0:0:0.6"> + To="0" Duration="0:0:0.6"/> diff --git a/UntamedMusicPlayer/Views/LyricPage.xaml.cs b/UntamedMusicPlayer/Views/LyricPage.xaml.cs index bccc739..bc8ac07 100644 --- a/UntamedMusicPlayer/Views/LyricPage.xaml.cs +++ b/UntamedMusicPlayer/Views/LyricPage.xaml.cs @@ -32,6 +32,8 @@ public sealed partial class LyricPage : Page, IDisposable private DateTimeOffset _contentGridMarginAnimationStart; private double _contentGridMarginFrom; private double _contentGridMarginTo; + private double _contentGridMarginAnimationDuration; + private CancellationTokenSource? _coverLoadWaitCts; public LyricPage() @@ -142,9 +144,10 @@ public sealed partial class LyricPage : Page, IDisposable private void OnRootPlayBarChanged(object? sender, PropertyChangedEventArgs e) { if ( - e.PropertyName - is nameof(RootPlayBarViewModel.IsDetail) - or nameof(RootPlayBarViewModel.IsFullScreen) + Settings.IsAutoHidePlaybackControlBar + && e.PropertyName + is nameof(RootPlayBarViewModel.IsDetail) + or nameof(RootPlayBarViewModel.IsFullScreen) ) { if (Data.RootPlayBarViewModel!.IsDetail && Data.RootPlayBarViewModel.IsFullScreen) @@ -156,7 +159,7 @@ public sealed partial class LyricPage : Page, IDisposable RootGrid.PointerMoved -= RootGrid_PointerMoved; if (_isTitleBarHidden) { - AnimateContentGridTopMargin(0); + AnimateContentGridTopMargin(0, 300); ShowTitleBarStoryboard.Begin(); _isTitleBarHidden = false; StopTitleBarTimer(); @@ -174,7 +177,7 @@ public sealed partial class LyricPage : Page, IDisposable { if (_isTitleBarHidden) { - AnimateContentGridTopMargin(0); + AnimateContentGridTopMargin(0, 300); ShowTitleBarStoryboard.Begin(); _isTitleBarHidden = false; } @@ -199,7 +202,7 @@ public sealed partial class LyricPage : Page, IDisposable && !_isTitleBarHidden ) { - AnimateContentGridTopMargin(-33); + AnimateContentGridTopMargin(-33, 600); HideTitleBarStoryboard.Begin(); _isTitleBarHidden = true; } @@ -231,7 +234,7 @@ public sealed partial class LyricPage : Page, IDisposable } } - private void AnimateContentGridTopMargin(double targetTop) + private void AnimateContentGridTopMargin(double targetTop, double durationMs) { var currentTop = ContentGrid.Margin.Top; if (Math.Abs(currentTop - targetTop) < 0.1) @@ -248,6 +251,7 @@ public sealed partial class LyricPage : Page, IDisposable _contentGridMarginFrom = currentTop; _contentGridMarginTo = targetTop; + _contentGridMarginAnimationDuration = durationMs; _contentGridMarginAnimationStart = DateTimeOffset.Now; _contentGridMarginAnimationTimer.Start(); @@ -255,10 +259,15 @@ public sealed partial class LyricPage : Page, IDisposable private void ContentGridMarginAnimationTick(DispatcherQueueTimer sender, object args) { - const double durationMs = 300; var elapsedMs = (DateTimeOffset.Now - _contentGridMarginAnimationStart).TotalMilliseconds; - var progress = Math.Clamp(elapsedMs / durationMs, 0d, 1d); - var easedProgress = 1 - Math.Pow(1 - progress, 3); + var progress = Math.Clamp(elapsedMs / _contentGridMarginAnimationDuration, 0d, 1d); + + // 显示 (To > From): EaseOut + // 隐藏 (To < From): EaseIn + var easedProgress = + _contentGridMarginTo > _contentGridMarginFrom + ? 1 - Math.Pow(1 - progress, 3) + : Math.Pow(progress, 3); var currentTop = _contentGridMarginFrom @@ -413,6 +422,7 @@ public sealed partial class LyricPage : Page, IDisposable visual.Scale = new Vector3(initialScaleX, initialScaleY, 1f); var compositor = visual.Compositor; + var scaleAnimation = compositor.CreateVector3KeyFrameAnimation(); scaleAnimation.InsertKeyFrame(1f, Vector3.One); scaleAnimation.Duration = TimeSpan.FromMilliseconds(450); diff --git a/UntamedMusicPlayer/Views/OnlineAlbumDetailPage.xaml.cs b/UntamedMusicPlayer/Views/OnlineAlbumDetailPage.xaml.cs index 06b07ae..e9cea5e 100644 --- a/UntamedMusicPlayer/Views/OnlineAlbumDetailPage.xaml.cs +++ b/UntamedMusicPlayer/Views/OnlineAlbumDetailPage.xaml.cs @@ -157,7 +157,7 @@ public sealed partial class OnlineAlbumDetailPage : Page backgroundScaleFactorNode, progressNode ); - ExpressionNode backgroundOpacityAnimation = progressNode; + ExpressionNode backgroundOpacityAnimation = progressNode * 0.7f; backgroundVisual.StartAnimation("Scale.Y", backgroundScaleAnimation); backgroundVisual.StartAnimation("Opacity", backgroundOpacityAnimation); diff --git a/UntamedMusicPlayer/Views/OnlineArtistDetailPage.xaml.cs b/UntamedMusicPlayer/Views/OnlineArtistDetailPage.xaml.cs index 52aeccd..ae3055d 100644 --- a/UntamedMusicPlayer/Views/OnlineArtistDetailPage.xaml.cs +++ b/UntamedMusicPlayer/Views/OnlineArtistDetailPage.xaml.cs @@ -170,7 +170,7 @@ public sealed partial class OnlineArtistDetailPage : Page backgroundScaleFactorNode, progressNode ); - ExpressionNode backgroundOpacityAnimation = progressNode; + ExpressionNode backgroundOpacityAnimation = progressNode * 0.7f; backgroundVisual.StartAnimation("Scale.Y", backgroundScaleAnimation); backgroundVisual.StartAnimation("Opacity", backgroundOpacityAnimation); var contentVisual = ElementCompositionPreview.GetElementVisual(ContentContainer); diff --git a/UntamedMusicPlayer/Views/OnlinePlayListDetailPage.xaml.cs b/UntamedMusicPlayer/Views/OnlinePlayListDetailPage.xaml.cs index 41c67a4..dee9d8c 100644 --- a/UntamedMusicPlayer/Views/OnlinePlayListDetailPage.xaml.cs +++ b/UntamedMusicPlayer/Views/OnlinePlayListDetailPage.xaml.cs @@ -148,7 +148,7 @@ public sealed partial class OnlinePlayListDetailPage : Page backgroundScaleFactorNode, progressNode ); - ExpressionNode backgroundOpacityAnimation = progressNode; + ExpressionNode backgroundOpacityAnimation = progressNode * 0.7f; backgroundVisual.StartAnimation("Scale.Y", backgroundScaleAnimation); backgroundVisual.StartAnimation("Opacity", backgroundOpacityAnimation); diff --git a/UntamedMusicPlayer/Views/PlayListDetailPage.xaml.cs b/UntamedMusicPlayer/Views/PlayListDetailPage.xaml.cs index 35d8b2b..fb31c78 100644 --- a/UntamedMusicPlayer/Views/PlayListDetailPage.xaml.cs +++ b/UntamedMusicPlayer/Views/PlayListDetailPage.xaml.cs @@ -148,7 +148,7 @@ public sealed partial class PlayListDetailPage : Page backgroundScaleFactorNode, progressNode ); - ExpressionNode backgroundOpacityAnimation = progressNode; + ExpressionNode backgroundOpacityAnimation = progressNode * 0.7f; backgroundVisual.StartAnimation("Scale.Y", backgroundScaleAnimation); backgroundVisual.StartAnimation("Opacity", backgroundOpacityAnimation); diff --git a/UntamedMusicPlayer/Views/SettingsPage.xaml b/UntamedMusicPlayer/Views/SettingsPage.xaml index 6405dec..fbe1faa 100644 --- a/UntamedMusicPlayer/Views/SettingsPage.xaml +++ b/UntamedMusicPlayer/Views/SettingsPage.xaml @@ -285,6 +285,10 @@ + + + +