使用 Storyboard 沿最短 'route' 旋转网格
Rotate a Grid using a Storyboard along shortest 'route'
我正在开发一个'compass-like'控件,它可以沿着0-360度的值旋转;即全圆。通过在 ViewModel 中设置 属性,Grid 会动画到新的角度。
然而,当动画从 10 度旋转到 350 度时,它并没有使用到新角度的最短路线。它几乎 顺时针 旋转了整个圆。
但我想要实现的是它沿着最短路线进行动画处理:从 10 到 350 应该只为 逆时针 设置 20 度的动画。
下面我添加了一个我的代码的虚构示例,但它没有绑定到 ViewModel 中的 属性,而是使用绑定到 ListBox(复制粘贴应该得到它 运行 ).选择 ListBox 中的项目会旋转 Grid(其中包含 'needle')。
如果可能的话,我更喜欢 "all XAML" 解决方案。这完全可行吗,我正在努力实现什么?
<Window x:Class="CompassRotation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="375" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Grid>
<Ellipse Width="300" Height="300" Fill="DarkCyan" />
<Label Content="{Binding SelectedItem.Content, ElementName=Angle, UpdateSourceTrigger=Explicit}"
FontSize="36" />
<Grid Width="300" Height="300"
RenderTransformOrigin="0.5,0.5"
Tag="{Binding SelectedItem.Content, ElementName=Angle, UpdateSourceTrigger=PropertyChanged,NotifyOnTargetUpdated=True}">
<Grid.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Grid.RenderTransform).(RotateTransform.Angle)"
Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Grid.RenderTransform>
<RotateTransform Angle="{Binding SelectedItem.Content, ElementName=Angle, FallbackValue=45}" />
</Grid.RenderTransform>
<Rectangle Width="15" Height="300">
<Rectangle.Fill>
<LinearGradientBrush>
<GradientStopCollection>
<GradientStop Color="Red" Offset="0" />
<GradientStop Color="Green" Offset="1" />
</GradientStopCollection>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</Grid>
<ListBox Grid.Column="1" Name="Angle">
<ListBoxItem>0</ListBoxItem>
<ListBoxItem>45</ListBoxItem>
<ListBoxItem>90</ListBoxItem>
<ListBoxItem>135</ListBoxItem>
<ListBoxItem>180</ListBoxItem>
<ListBoxItem>225</ListBoxItem>
<ListBoxItem>270</ListBoxItem>
<ListBoxItem>305</ListBoxItem>
<ListBoxItem>360</ListBoxItem>
</ListBox>
</Grid>
</Window>
DoubleAnimation
有一个 From
property,您可以绑定到它来设置动画的起始值。
因此,在将 ViewModel 的 Angle
值从 10 逆时针更新到 350 之前,将 ViewModel 的 From
值设置为 370,动画将从 370 (=10) 度标记逆时针滚动回到350.
剩下的问题是,如果要将 Angle
从值 from
更新为 to
,如何确定要设置的 From
值。
我们知道的一件事是From
属性要设置在区间[to - 180, to + 180]
内,保证轮换走最短路线到to
].
如果我们称这个区间的下界为lowerbound = to - 180
,那么
From = lowerbound + (from - lowerbound) % 360
或者实际上,因为 %
behaves oddly for negative values,适用于 [-360,360]
范围内的 from
和 to
角度的更安全的公式是
From = lowerbound + (from - lowerbound + 720) % 360
我正在开发一个'compass-like'控件,它可以沿着0-360度的值旋转;即全圆。通过在 ViewModel 中设置 属性,Grid 会动画到新的角度。 然而,当动画从 10 度旋转到 350 度时,它并没有使用到新角度的最短路线。它几乎 顺时针 旋转了整个圆。
但我想要实现的是它沿着最短路线进行动画处理:从 10 到 350 应该只为 逆时针 设置 20 度的动画。
下面我添加了一个我的代码的虚构示例,但它没有绑定到 ViewModel 中的 属性,而是使用绑定到 ListBox(复制粘贴应该得到它 运行 ).选择 ListBox 中的项目会旋转 Grid(其中包含 'needle')。
如果可能的话,我更喜欢 "all XAML" 解决方案。这完全可行吗,我正在努力实现什么?
<Window x:Class="CompassRotation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="375" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Grid>
<Ellipse Width="300" Height="300" Fill="DarkCyan" />
<Label Content="{Binding SelectedItem.Content, ElementName=Angle, UpdateSourceTrigger=Explicit}"
FontSize="36" />
<Grid Width="300" Height="300"
RenderTransformOrigin="0.5,0.5"
Tag="{Binding SelectedItem.Content, ElementName=Angle, UpdateSourceTrigger=PropertyChanged,NotifyOnTargetUpdated=True}">
<Grid.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Grid.RenderTransform).(RotateTransform.Angle)"
Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Grid.RenderTransform>
<RotateTransform Angle="{Binding SelectedItem.Content, ElementName=Angle, FallbackValue=45}" />
</Grid.RenderTransform>
<Rectangle Width="15" Height="300">
<Rectangle.Fill>
<LinearGradientBrush>
<GradientStopCollection>
<GradientStop Color="Red" Offset="0" />
<GradientStop Color="Green" Offset="1" />
</GradientStopCollection>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</Grid>
<ListBox Grid.Column="1" Name="Angle">
<ListBoxItem>0</ListBoxItem>
<ListBoxItem>45</ListBoxItem>
<ListBoxItem>90</ListBoxItem>
<ListBoxItem>135</ListBoxItem>
<ListBoxItem>180</ListBoxItem>
<ListBoxItem>225</ListBoxItem>
<ListBoxItem>270</ListBoxItem>
<ListBoxItem>305</ListBoxItem>
<ListBoxItem>360</ListBoxItem>
</ListBox>
</Grid>
</Window>
DoubleAnimation
有一个 From
property,您可以绑定到它来设置动画的起始值。
因此,在将 ViewModel 的 Angle
值从 10 逆时针更新到 350 之前,将 ViewModel 的 From
值设置为 370,动画将从 370 (=10) 度标记逆时针滚动回到350.
剩下的问题是,如果要将 Angle
从值 from
更新为 to
,如何确定要设置的 From
值。
我们知道的一件事是From
属性要设置在区间[to - 180, to + 180]
内,保证轮换走最短路线到to
].
如果我们称这个区间的下界为lowerbound = to - 180
,那么
From = lowerbound + (from - lowerbound) % 360
或者实际上,因为 %
behaves oddly for negative values,适用于 [-360,360]
范围内的 from
和 to
角度的更安全的公式是
From = lowerbound + (from - lowerbound + 720) % 360