Yur*_*rii 5 .net c# wpf animation
假设我们有一个动画,当用户将光标移到矩形上方时,该动画将矩形的宽度从50更改为 150,并在用户将光标移开时将其宽度从150更改为 50。让每个动画的持续时间为1 秒。
如果用户将光标移动到矩形上,然后等待 1 秒动画完成,然后将光标移离矩形,则一切正常。两个动画总共需要 2 秒,它们的速度正是我们所期望的。
但是如果用户在第一个动画 (50 -> 150) 完成之前将光标移开,则第二个动画的持续时间将为 1 秒,但速度会非常慢。我的意思是,第二个动画将动画矩形的宽度不是从150 到 50,而是从120 到 50或从70 到 50,如果您非常快速地将光标移开。但是同样的1秒!
所以我想了解的是如何使“向后”动画的持续时间成为动态的。基于第一个动画停止的点的动态。或者,如果我指定From和To值150和50,Duration为1秒,矩形宽度为100 - WPF将由自身计算出动画50%完成。
我正在测试使用动画的不同方法,如触发器、EventTrigger in style 和 VisualStateGroups,但没有成功。
这是一个显示我的问题的 XAML 示例。如果您想自己看看我在说什么,请将光标移到矩形上方,等待 1-2 秒(第一个动画完成)然后将光标移开。之后,将光标移到矩形上方 0.25 秒,然后将其移开。您将看到矩形的宽度变化非常缓慢。
<Rectangle Width="50"
Height="100"
HorizontalAlignment="Left"
Fill="Black">
<Rectangle.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width"
To="150"
Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
Run Code Online (Sandbox Code Playgroud)
另外,我想解释一下我期望看到的计算。所以让我们想象另一个动画,从100 到 1100,持续时间为2 秒。如果我们在300点停止它,它必须开始回到100但不是 2 秒,而是:
((300 - 100) / (1100 - 100)) * 2s = 0.4s
Run Code Online (Sandbox Code Playgroud)
如果我们停在 900,持续时间将是:
((900 - 100) / (1100 - 100)) * 2s = 1.6s
Run Code Online (Sandbox Code Playgroud)
如果我们让动画正常完成,它将是:
((1100 - 100) / (1100 - 100)) * 2s = 2s
Run Code Online (Sandbox Code Playgroud)
WPF 提供了编写自定义动画的可能性。
您可以创建一个具有MinSpeed以下属性的属性:
public class MinSpeedDoubleAnimation : DoubleAnimation
{
public static readonly DependencyProperty MinSpeedProperty =
DependencyProperty.Register(
nameof(MinSpeed), typeof(double?), typeof(MinSpeedDoubleAnimation));
public double? MinSpeed
{
get { return (double?)GetValue(MinSpeedProperty); }
set { SetValue(MinSpeedProperty, value); }
}
protected override Freezable CreateInstanceCore()
{
return new MinSpeedDoubleAnimation();
}
protected override double GetCurrentValueCore(
double defaultOriginValue, double defaultDestinationValue,
AnimationClock animationClock)
{
var destinationValue = To ?? defaultDestinationValue;
var originValue = From ?? defaultOriginValue;
var duration = Duration != Duration.Automatic ? Duration :
animationClock.NaturalDuration;
var speed = (destinationValue - originValue) / duration.TimeSpan.TotalSeconds;
if (MinSpeed.HasValue && Math.Abs(speed) < MinSpeed)
{
speed = Math.Sign(speed) * MinSpeed.Value;
}
var value = originValue + speed * animationClock.CurrentTime.Value.TotalSeconds;
return speed > 0 ?
Math.Min(destinationValue, value) :
Math.Max(destinationValue, value);
}
}
Run Code Online (Sandbox Code Playgroud)
并像这样使用它:
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Width"
To="150" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<local:MinSpeedDoubleAnimation
Storyboard.TargetProperty="Width"
To="50" MinSpeed="100"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
Run Code Online (Sandbox Code Playgroud)
为了完整起见,这里也是最简单的代码隐藏解决方案,没有任何触发器和故事板:
<Rectangle Width="50" Height="100" HorizontalAlignment="Left" Fill="Black"
MouseEnter="RectangleMouseEnter" MouseLeave="RectangleMouseLeave"/>
Run Code Online (Sandbox Code Playgroud)
使用这些事件处理程序:
private void RectangleMouseEnter(object sender, MouseEventArgs e)
{
var element = (FrameworkElement)sender;
var animation = new DoubleAnimation(150, TimeSpan.FromSeconds(1));
element.BeginAnimation(FrameworkElement.WidthProperty, animation);
}
private void RectangleMouseLeave(object sender, MouseEventArgs e)
{
var element = (FrameworkElement)sender;
var duration = (element.ActualWidth - 50) / 100;
var animation = new DoubleAnimation(50, TimeSpan.FromSeconds(duration));
element.BeginAnimation(FrameworkElement.WidthProperty, animation);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
634 次 |
| 最近记录: |