假设我们有一个动画,当用户将矩形的宽度从 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%。 / p>
我正在测试使用动画的不同方式,例如触发器,样式中的EventTrigger和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>
此外,我想解释一下我希望看到的计算结果。 因此,让我们想象一下另一个动画,该动画的时间从 100到1100 ,持续时间为 2秒。如果我们将其停止在 300 的位置,则必须开始回到 100 ,但不能持续2秒钟,而要恢复:
((300 - 100) / (1100 - 100)) * 2s = 0.4s
如果我们停在900,持续时间将为:
((900 - 100) / (1100 - 100)) * 2s = 1.6s
如果让动画正常完成,它将是:
((1100 - 100) / (1100 - 100)) * 2s = 2s
答案 0 :(得分:0)
不幸的是,情节提要是经过编译的代码,这意味着您不能在元素中使用绑定来提高其功能。
我必须做类似的事情,而我发现达到此目的的唯一方法是访问故事板属性,该属性在我需要触摸时需要动态,并根据代码的需要设置该属性
因此触摸并从背后的代码中触发并不是理想的选择,但是嘿,用这种方式看,不是业务逻辑,它是严格的表示层,因此背后的代码通常是可以接受的做法。
因此,请在后面的代码中处理触发器,计算出数字,更新情节提要属性然后再启动它。目前,这可能是您的最佳选择。
我希望看到情节提要成为逻辑树的一部分,并具有依赖项属性来动态修改值,因此可以请求功能:)。
答案 1 :(得分:0)
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);
}
}
并像这样使用它:
<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>
为完整起见,这也是没有任何触发器和情节提要的最简单的代码隐藏解决方案:
<Rectangle Width="50" Height="100" HorizontalAlignment="Left" Fill="Black"
MouseEnter="RectangleMouseEnter" MouseLeave="RectangleMouseLeave"/>
使用这些事件处理程序:
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);
}
答案 2 :(得分:0)
自定义动画的替代方法可以是附加属性,该属性绑定到目标元素的ActualWidth
,可以动态调整动画的Duration
。
附加的属性可能如下所示:
public static class AnimationEx
{
public static readonly DependencyProperty DynamicDurationProperty =
DependencyProperty.RegisterAttached(
"DynamicDuration", typeof(double), typeof(AnimationEx),
new PropertyMetadata(DynamicDurationPropertyChanged));
public static double GetDynamicDuration(DoubleAnimation animation)
{
return (double)animation.GetValue(DynamicDurationProperty);
}
public static void SetDynamicDuration(DoubleAnimation animation, double value)
{
animation.SetValue(DynamicDurationProperty, value);
}
private static void DynamicDurationPropertyChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var animation = o as DoubleAnimation;
if (animation != null && animation.To.HasValue)
{
animation.Duration = TimeSpan.FromSeconds(
Math.Abs((double)e.NewValue - animation.To.Value) / 100);
}
}
}
其中可能还有另一个速度因子附加属性,该属性硬编码为上面的100
。
该属性的用法如下:
<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" To="50"
local:AnimationEx.DynamicDuration="{Binding Path=ActualWidth,
RelativeSource={RelativeSource AncestorType=Rectangle}}" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
一个可能的缺点是,当ActualWidth更改时,Duration会不断重置。但是,我无法通知任何视觉效果。