feat: 自动战斗滑块动画效果

This commit is contained in:
uye
2026-03-28 13:49:59 +08:00
parent 295ec41c0b
commit 815728b9c4
2 changed files with 209 additions and 31 deletions

View File

@@ -42,37 +42,70 @@
Padding="2"
Background="{DynamicResource MouseOverRegionBrushOpacity75}"
CornerRadius="4">
<ListBox
HorizontalAlignment="Center"
Background="Transparent"
Padding="0"
BorderThickness="0"
IsEnabled="{Binding Idle}"
SelectedIndex="{Binding CopilotTabIndex}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style BasedOn="{StaticResource TransparentListBoxItemStyle}" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Margin" Value="0" />
<Setter Property="MinHeight" Value="28" />
<Setter Property="Padding" Value="12,2" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{DynamicResource RegionBrush}" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBoxItem Content="{DynamicResource MainStageStoryCollectionSideStory}" />
<ListBoxItem Content="{DynamicResource SSS}" />
<ListBoxItem Content="{DynamicResource ParadoxSimulation}" />
<ListBoxItem Content="{DynamicResource OtherActivityStage}" />
</ListBox>
<Grid x:Name="CopilotTabHost">
<Border
x:Name="CopilotTabIndicator"
Width="0"
Height="0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Background="{DynamicResource RegionBrush}"
CornerRadius="4"
IsHitTestVisible="False"
Opacity="0">
<Border.RenderTransform>
<TranslateTransform />
</Border.RenderTransform>
</Border>
<ListBox
x:Name="CopilotTabList"
Padding="0"
HorizontalAlignment="Center"
Background="Transparent"
BorderThickness="0"
IsEnabled="{Binding Idle}"
Loaded="CopilotTabList_Loaded"
SelectedIndex="{Binding CopilotTabIndex}"
SelectionChanged="CopilotTabList_SelectionChanged"
SizeChanged="CopilotTabList_SizeChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style BasedOn="{StaticResource NoSelectedHighlightListBoxItemStyle}" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Margin" Value="0" />
<Setter Property="MinHeight" Value="28" />
<Setter Property="Padding" Value="12,2" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<EventSetter Event="Loaded" Handler="CopilotTabItem_Loaded" />
<EventSetter Event="SizeChanged" Handler="CopilotTabItem_SizeChanged" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Transparent" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Transparent" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True" />
<Condition Property="IsMouseOver" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Background" Value="Transparent" />
</MultiTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBoxItem Content="{DynamicResource MainStageStoryCollectionSideStory}" />
<ListBoxItem Content="{DynamicResource SSS}" />
<ListBoxItem Content="{DynamicResource ParadoxSimulation}" />
<ListBoxItem Content="{DynamicResource OtherActivityStage}" />
</ListBox>
</Grid>
</Border>
<Grid Margin="0,5">
<Grid.ColumnDefinitions>

View File

@@ -11,10 +11,17 @@
// but WITHOUT ANY WARRANTY
// </copyright>
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using MaaWpfGui.Models;
using MaaWpfGui.ViewModels.UI;
using Point = System.Windows.Point;
using Rect = System.Windows.Rect;
namespace MaaWpfGui.Views.UI;
@@ -23,11 +30,149 @@ namespace MaaWpfGui.Views.UI;
/// </summary>
public partial class CopilotView
{
private static readonly Duration CopilotTabAnimationDuration = new(TimeSpan.FromMilliseconds(180));
public CopilotView()
{
InitializeComponent();
}
private void CopilotTabList_Loaded(object sender, RoutedEventArgs e) => QueueCopilotTabIndicatorUpdate(false);
private void CopilotTabList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.Source == sender)
{
QueueCopilotTabIndicatorUpdate(true);
}
}
private void CopilotTabList_SizeChanged(object sender, SizeChangedEventArgs e) => QueueCopilotTabIndicatorUpdate(false);
private void CopilotTabItem_Loaded(object sender, RoutedEventArgs e) => QueueCopilotTabIndicatorUpdate(false);
private void CopilotTabItem_SizeChanged(object sender, SizeChangedEventArgs e) => QueueCopilotTabIndicatorUpdate(false);
private void QueueCopilotTabIndicatorUpdate(bool animate) => Dispatcher.BeginInvoke(() => UpdateCopilotTabIndicator(animate), DispatcherPriority.Loaded);
private void UpdateCopilotTabIndicator(bool animate)
{
if (!TryGetSelectedCopilotTabBounds(out var bounds))
{
if (!IsLoaded || CopilotTabList.SelectedItem is null)
{
HideCopilotTabIndicator();
}
return;
}
CopilotTabIndicator.Opacity = 1;
TranslateTransform translateTransform = EnsureCopilotTabIndicatorTransform();
bool shouldAnimate = animate && CopilotTabIndicator.Width > 0 && CopilotTabIndicator.Height > 0;
if (!shouldAnimate)
{
StopCopilotTabIndicatorAnimations();
SetCopilotTabIndicatorBounds(translateTransform, bounds);
return;
}
AnimateCopilotTabIndicator(translateTransform, TranslateTransform.XProperty, bounds.X);
AnimateCopilotTabIndicator(translateTransform, TranslateTransform.YProperty, bounds.Y);
AnimateCopilotTabIndicator(CopilotTabIndicator, WidthProperty, bounds.Width);
AnimateCopilotTabIndicator(CopilotTabIndicator, HeightProperty, bounds.Height);
}
private void HideCopilotTabIndicator()
{
StopCopilotTabIndicatorAnimations();
CopilotTabIndicator.Opacity = 0;
}
private bool TryGetSelectedCopilotTabBounds(out Rect bounds)
{
bounds = default;
if (!IsLoaded || CopilotTabList.SelectedItem is null)
{
return false;
}
if (CopilotTabList.ItemContainerGenerator.ContainerFromItem(CopilotTabList.SelectedItem) is not ListBoxItem item ||
!item.IsLoaded ||
item.ActualWidth <= 0 ||
item.ActualHeight <= 0)
{
return false;
}
try
{
Point topLeft = item.TransformToAncestor(CopilotTabHost).Transform(new Point(0, 0));
bounds = new Rect(topLeft.X, topLeft.Y, item.ActualWidth, item.ActualHeight);
return true;
}
catch
{
return false;
}
}
private TranslateTransform EnsureCopilotTabIndicatorTransform()
{
if (CopilotTabIndicator.RenderTransform is TranslateTransform translateTransform)
{
return translateTransform;
}
translateTransform = new TranslateTransform();
CopilotTabIndicator.RenderTransform = translateTransform;
return translateTransform;
}
private void SetCopilotTabIndicatorBounds(TranslateTransform translateTransform, Rect bounds)
{
translateTransform.X = bounds.X;
translateTransform.Y = bounds.Y;
CopilotTabIndicator.Width = bounds.Width;
CopilotTabIndicator.Height = bounds.Height;
}
private void StopCopilotTabIndicatorAnimations()
{
if (CopilotTabIndicator.RenderTransform is TranslateTransform translateTransform)
{
StopCopilotTabIndicatorAnimations(translateTransform);
}
CopilotTabIndicator.BeginAnimation(WidthProperty, null);
CopilotTabIndicator.BeginAnimation(HeightProperty, null);
}
private static void StopCopilotTabIndicatorAnimations(TranslateTransform translateTransform)
{
translateTransform.BeginAnimation(TranslateTransform.XProperty, null);
translateTransform.BeginAnimation(TranslateTransform.YProperty, null);
}
private static void AnimateCopilotTabIndicator(TranslateTransform target, DependencyProperty property, double toValue)
{
target.BeginAnimation(property, CreateCopilotTabIndicatorAnimation(toValue));
}
private static void AnimateCopilotTabIndicator(Border target, DependencyProperty property, double toValue)
{
target.BeginAnimation(property, CreateCopilotTabIndicatorAnimation(toValue));
}
private static DoubleAnimation CreateCopilotTabIndicatorAnimation(double toValue)
{
return new DoubleAnimation {
To = toValue,
Duration = CopilotTabAnimationDuration,
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut },
};
}
private void FileTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (DataContext is CopilotViewModel viewModel && e.NewValue is CopilotFileItem fileItem && !fileItem.IsFolder)