博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WPF 和 UWP 中,不用设置 From 或 To,Storyboard 即拥有更灵活的动画控制
阅读量:4661 次
发布时间:2019-06-09

本文共 5854 字,大约阅读时间需要 19 分钟。

原文:

版权声明:本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:http://blog.csdn.net/wpwalter/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系(walter.lv@qq.com)。 https://blog.csdn.net/WPwalter/article/details/78404727

无论是 WPF 还是 UWP 开发,如果用 StoryboardAnimation 做动画,我们多数时候都会设置 FromTo 属性,用于从起始值动画到目标值。然而动画并不总是可以静态地指定这些值,因为更多的时候动画的起始值和目标值取决于当前 UI 的状态。

本文中,我将将尽量避免设置 FromTo 值,让动画可以随时中断并重新开始,而中途不会出现突兀的变化。


本文涉及到的代码均在 GitHub 上以 MIT License 开源:。

预览效果

下面是本文期望实现的基本效果:

  • 在 WPF 中的动画效果

    WPF 动画随机移动

  • 在 UWP 中的动画效果

    UWP 动画随机移动

预备代码

为了让读者能够最快速地搭建一个可供试验的 DEMO,我这里贴出界面部分核心代码。

XAML 是这样的(这里的 XAML,WPF 和 UWP 完全一样,可以互相使用而不用修改任何代码):

  • 布局部分
  • 资源部分

.xaml.cs 文件中预备一些属性和字段方便使用:

#if !WINDOWS_UWP// 因为 WPF 不能在资源中指定 x:Name,所以需要在后台代码中手动查找动画资源。private Storyboard TranslateStoryboard => (Storyboard)FindResource("Storyboard.Translate");#endifprivate DoubleAnimation TranslateXAnimation => (DoubleAnimation) TranslateStoryboard.Children[0];private DoubleAnimation TranslateYAnimation => (DoubleAnimation) TranslateStoryboard.Children[1];private readonly Random _random = new Random(DateTime.Now.Ticks.GetHashCode());private Point NextRandomPosition(){    var areaX = (int) Math.Round(DisplayCanvas.ActualWidth - DisplayShape.ActualWidth);    var areaY = (int) Math.Round(DisplayCanvas.ActualHeight - DisplayShape.ActualHeight);    return new Point(_random.Next(areaX) + 1, _random.Next(areaY) + 1);}

探索动画

由于我们期望元素从当前所在的位置开始动画,到我们指定的另一个随机位置,所以直接在 XAML 中指定 FromTo 是一个艰难的行为。我们只好在 .xaml.cs 文件中指定。

WPF

在 WPF 中,如果我们没有指定动画的 From,那么动画将从当前值开始;如果我们没有指定动画的 To,那么动画将到当前值结束。从这个角度上说,似乎不设置 FromTo 将导致动画保持在当前值不变,不会有动画效果。

但是,WPF 允许在动画进行中修改动画参数,于是我们可以直接开始动画,然后再动画进行中修改元素属性到目标值。

也就是说,可以这么写:

private void BeginStoryboard_Click(object sender, RoutedEventArgs e){    TranslateStoryboard.Begin();    var nextPosition = NextRandomPosition();    TranslateTransform.X = nextPosition.X;    TranslateTransform.Y = nextPosition.Y;}

快速点击这个按钮看看,你会发现每次点击都可以立即从当前位置开始向新的目标位置动画。

快速点击动画

不过你应该注意到了一个坑——第一次并没有播放动画,而是直接跳到了目标位置;这是因为动画还没有保持住元素的位置。我们需要在初始化的时候播放一次动画;

private void OnLoaded(object sender, RoutedEventArgs e){    TranslateStoryboard.Begin();    TranslateStoryboard.Stop();}

这样就解决了第一次动画不播放的问题。

现在,我们加上暂停按钮:

private void PauseStoryboard_Click(object sender, RoutedEventArgs e){    TranslateStoryboard.Pause();}

即便是中途有暂停,依然能够继续让动画朝新的目标位置动画。

快速点击动画

如果我们希望动画从一个新的起点开始,而不是从当前状态开始,则只需要在动画开始之前设置元素的位置即可:

private void BeginStoryboard2_Click(object sender, RoutedEventArgs e){    MoveToRandomPosition();    TranslateStoryboard.Begin();    MoveToRandomPosition();    void MoveToRandomPosition()    {        var nextPosition = NextRandomPosition();        TranslateTransform.X = nextPosition.X;        TranslateTransform.Y = nextPosition.Y;    }}

UWP

UWP 的情况就不如 WPF 那么灵活了。在 UWP 中,如果不给动画指定 To 值,那么动画根本就会直接朝 0 位置执行。

于是在动画执行之前,设置动画的 To 值不可避免:

private void BeginStoryboard_Click(object sender, RoutedEventArgs e){    AnimateToRandomPosition();    TranslateStoryboard.Begin();    void Uwp_AnimateToRandomPosition()    {        var nextPosition = NextRandomPosition();        TranslateXAnimation.To = nextPosition.X;        TranslateYAnimation.To = nextPosition.Y;    }}

在这样的写法下,灵活性与 WPF 相当,但 WPF 中支持在动画没有播放的时候随时设置元素位置,而这种方式则不行(其值会被动画保持)。

完整的后台代码

public partial class StoryboardPage : Page{    public StoryboardPage()    {        InitializeComponent();        Loaded += OnLoaded;    }#if !WINDOWS_UWP    private Storyboard TranslateStoryboard => (Storyboard)FindResource("Storyboard.Translate");#endif    private DoubleAnimation TranslateXAnimation => (DoubleAnimation) TranslateStoryboard.Children[0];    private DoubleAnimation TranslateYAnimation => (DoubleAnimation) TranslateStoryboard.Children[1];    private readonly Random _random = new Random(DateTime.Now.Ticks.GetHashCode());    private void OnLoaded(object sender, RoutedEventArgs e)    {        Loaded -= OnLoaded;        TranslateStoryboard.Begin();        TranslateStoryboard.Stop();    }    private void BeginStoryboard_Click(object sender, RoutedEventArgs e)    {        Uwp_AnimateToRandomPosition();        TranslateStoryboard.Begin();        MoveToRandomPosition();    }    private void BeginStoryboard2_Click(object sender, RoutedEventArgs e)    {        MoveToRandomPosition();        Uwp_AnimateToRandomPosition();        TranslateStoryboard.Begin();        MoveToRandomPosition();    }    private void PauseStoryboard_Click(object sender, RoutedEventArgs e)    {        TranslateStoryboard.Pause();    }    [Conditional("WINDOWS_UWP")]    private void Uwp_AnimateToRandomPosition()    {        var nextPosition = NextRandomPosition();        TranslateXAnimation.To = nextPosition.X;        TranslateYAnimation.To = nextPosition.Y;    }    [Conditional("WPF")]    private void MoveToRandomPosition()    {        var nextPosition = NextRandomPosition();        TranslateTransform.X = nextPosition.X;        TranslateTransform.Y = nextPosition.Y;    }    private Point NextRandomPosition()    {        var areaX = (int) Math.Round(DisplayCanvas.ActualWidth - DisplayShape.ActualWidth);        var areaY = (int) Math.Round(DisplayCanvas.ActualHeight - DisplayShape.ActualHeight);        return new Point(_random.Next(areaX) + 1, _random.Next(areaY) + 1);    }}

总结

  1. 在 WPF 中,可以不通过 FromTo 来指定动画的起始值和终止值;但如果真的不指定 FromTo,需要提前播放一次动画以确保动画能保持住元素状态;
  2. 在 WPF 中,如果没有指定 FromTo,那么动画结束后依然能直接为元素属性复制,且会立刻生效(正常情况下需要先清除动画);
  3. 在 UWP 中,必须指定动画的 To 才能按照期望播放到目标值。
posted on
2018-09-21 22:08 阅读(
...) 评论(
...)

转载于:https://www.cnblogs.com/lonelyxmas/p/9688722.html

你可能感兴趣的文章
堆内存破坏检测实战--附完整调试过程
查看>>
【knockoutjs】 Computed VS Pure Computed 区别
查看>>
JS向数组中添加/删除元素
查看>>
House Robber
查看>>
Best Time to Buy and Sell Stock II
查看>>
函数参数按值传递
查看>>
第一类和第二类Stirling数
查看>>
wpf log4net使用
查看>>
python之路,正则表达式
查看>>
eclipse中java项目的创建
查看>>
Linux命令
查看>>
浅谈几种主要编程语言
查看>>
Linux tcpdump命令详解
查看>>
两个datagrid的数据移动(支持多选)
查看>>
HDU4826 Labyrinth
查看>>
jquery-layer
查看>>
JavaScript 基础
查看>>
iOS学习之六种传值方式
查看>>
EF 外键不显示、如何让外键显示!增、删、改 操作时,外键不显示,只显示导航属性!...
查看>>
美文共享
查看>>