ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

从自定义DoubleAnimation开始

2019-07-13 15:40:59  阅读:307  来源: 互联网

标签:Freezable 自定义 开始 QuadraticDoubleAnimation Animation double DoubleAnimation Depen


原文链接:http://www.cnblogs.com/yayx/archive/2007/04/21/721743.html 研究了好多天WPF了, 在cnblogs写第一篇技术文~纪念一下,大家也支持一下 呵呵
虽然是从从自定义DoubleAnimation开始,不过重点MS应该是在后面对Freezable DependencyProperty等的讨论~笔记的一部分,写的比较乱,不过思路还算清楚
下面尝试自定义一个DoubleAnimation
简单的准备,
一般的DoubleAnimation是线性的变换函数

1.jpg
横坐标表示时间的进度, 范围从0到1 ,这个值可以从Clock获得,纵坐标表示Double数值的进度,这里也定义为从0到1,乘以实际的总路径后就获得当前路径

 2.jpg

可能某些时候需要复杂一点的变化,比如用二次曲线吧(其实这个可以用其他方法很简单的实现,这里只是讨论自定义Animation的问题)
假设曲线为二次曲线 (设横坐标为x,则曲线方程为y=x*x) 非常简单的一个例子 

首先定义一个矩形

 

None.gif      <Rectangle Stroke="#FF000000" x:Name="myRectangle" Width="30" Height="30" HorizontalAlignment="Left" Margin="34,57.5,0,0" VerticalAlignment="Top">
None.gif
None.gif        <Rectangle.RenderTransform>
None.gif
None.gif            <TranslateTransform X="0" Y="0"/>
None.gif
None.gif        </Rectangle.RenderTransform>
None.gif
None.gif        <Rectangle.Fill>
None.gif
None.gif          <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
None.gif
None.gif            <GradientStop Color="#FF000000" Offset="0"/>
None.gif
None.gif            <GradientStop Color="#FFFFFFFF" Offset="1"/>
None.gif
None.gif          </LinearGradientBrush>
None.gif
None.gif        </Rectangle.Fill>
None.gif
None.gif      </Rectangle>
None.gif
None.gif

用DoubleAnimation让他动起来:

None.gif <Window.Triggers>
None.gif
None.gif    <EventTrigger RoutedEvent="Window.Loaded">
None.gif
None.gif      <BeginStoryboard>
None.gif
None.gif        <Storyboard>
None.gif
None.gif          <DoubleAnimation Storyboard.TargetName="myRectangle" Storyboard.TargetProperty="RenderTransform.X"
None.gif
None.gif                           Duration="0:0:3" From="0" To="180"/>
None.gif
None.gif        </Storyboard>
None.gif
None.gif      </BeginStoryboard>
None.gif
None.gif    </EventTrigger>
None.gif
None.gif </Window.Triggers>
None.gif

下面开始自定义一个Animation。WPF提供了DoubleAnimationBase类,用于扩展。

先新建一个类, 取名QuadraticDoubleAnimation,继承于DoubleAnimationBase,并实现抽象函数

首先来实现CreateInstanceCore() 这里暂时返回一个本类的新的实例就行了

None.gif        protected override Freezable CreateInstanceCore()
ExpandedBlockStart.gif        {
InBlock.gif            return new QuadraticDoubleAnimation();
ExpandedBlockEnd.gif        }
None.gif

然后实现GetCurrentValueCore(),前面提到过TimeClock会通过GetCurrentValue()方法获得当前的值,不过GetCurrentValue()方法除了完成计算值之外还会完成其他工作,因此不能够直接定义GetCurrentValue()方法,定义GetCurrentValueCore(),就行了.

我们首先计算整个delta值,也就是动画全程的距离,再通过y=x*x*delta计算并返回当前,很简单的两个语句

        protected override double GetCurrentValueCore(double defaultOriginValue, double defaultDestinationValue, AnimationClock animationClock)
        {
            double delta = To - From;
            double x=animationClock.CurrentProgress.Value;
            return x * x * delta;
        }

完成了这些,一切看起来都不错,应该可以运行了。在XAML添加应用,并把对DoubleAnimation的引用修改为对我们的QuadraticDoubleAnimation的引用。

在Window属性中加入

None.gifxmlns:myAnimations="clr-namespace:myCustonAnimation"

Storyboard的代码:

None.gif        <Storyboard>
None.gif
None.gif          <myAnimations:QuadraticDoubleAnimation Storyboard.TargetName="myRectangle" Storyboard.TargetProperty="RenderTransform.X"
None.gif
None.gif                           Duration="0:0:3" From="0" To="180"/>
None.gif
None.gif        </Storyboard>
None.gif

运行。编译情况不妙,没通过,原来To和From属性没有实现。看看DoubleAnimation类和DoubleAnimationBase类的实现
注意到Duration这样的属性是找得到的,实际上Duration属性是早在TimeLine里就实现了(继承关系TimeLine->AnimationTimeline->DoubleAnimationBase)但是From/To包括By属性是在DoubleAnimation类实现的。
看来我们需要自己实现From/To/By这几个属性了,这里只实现From和To

这非常简单,我们很快就实现了两个属性:

None.gif        private double _from;
None.gif
None.gif        public double From
None.gif
ExpandedBlockStart.gif        {
InBlock.gif
ExpandedSubBlockStart.gif            get { return _from; }
InBlock.gif
ExpandedSubBlockStart.gif            set { _from = value; }
InBlock.gif
ExpandedBlockEnd.gif        }
None.gif
None.gif        private double _to;
None.gif
None.gif        public double To
None.gif
ExpandedBlockStart.gif        {
InBlock.gif
ExpandedSubBlockStart.gif            get { return _to; }
InBlock.gif
ExpandedSubBlockStart.gif            set { _to = value; }
InBlock.gif
ExpandedBlockEnd.gif        }
None.gif

F5运行,通过了,但是动画没有动起来。

这是为什么?经过跟踪,很容易发现GetCurrentValueCore()中To,From始终是0.0,想想也对,光定义了两个属性,这里,XAML在前台定义了一个QuadraticDoubleAnimation对象后,XAML中明明定义了这两个属性的值,为什么它们没有变化呢?。这很奇怪,因为在前台设置的Duration属性就返回到了后台。
通过查看TimeLine的元数据发现,Duration是通过DependencyProperty存在的,模仿一下:

代码改变如下: 

None.gif        public double From
ExpandedBlockStart.gifContractedBlock.gif        {
ExpandedSubBlockStart.gif            get { return (double)GetValue(FromProperty); }
ExpandedSubBlockStart.gif            set { SetValue(FromProperty, value); }
ExpandedBlockEnd.gif        }
None.gif        public double To
ExpandedBlockStart.gif        {
ExpandedSubBlockStart.gif            get { return (double)GetValue(ToProperty); }
ExpandedSubBlockStart.gif            set { SetValue(ToProperty, value); }
ExpandedBlockEnd.gif        }

 GetValue和SetValue是通过ToProperty和FromProperty访问的,还需要注册两个属性名称:

 

        public static readonly DependencyProperty FromProperty =
    DependencyProperty.Register("From",
        typeof(double),
        typeof(QuadraticDoubleAnimation),
        new PropertyMetadata(null));
 
        public static readonly DependencyProperty ToProperty =
            DependencyProperty.Register("To",
                typeof(double),
                typeof(QuadraticDoubleAnimation),
                new PropertyMetadata(null));

再运行,图像动起来了,速度先慢后快,按照我们的数学函数描述的方式运动,至此自定义动画成功
实际上程序中还有许多bug,比如当不指定From或者To的时候程序就会出错,From To应该为Double?类型~等,这里都省略了。。(先庆祝一下 呵呵)

最大的问题来了,为什么直接使用属性,不能达到效果,而使用DependencyProperty就可以了呢?这和XAML的工作方式有关系。
我们自定义的Animation对象在WPF中是一个Freezable(可冻结对象,假设这为对象A),默认情况下,它是没有被冻结的。也就是说它是可以被修改的,为了保证对它的修改不影响动画的正常进行,WPF会在开始动画的时候复制一个Animation对象(假设为对象B),对象B对编程者来说是不可知的,也就是说在动画进行的过程中,我们对Animation对象的任何操作实际上都是对对象A进行的操作。

下面我们来关注对象B的复制过程。对了,在这里:

        protected override Freezable CreateInstanceCore()
        {
            return new QuadraticDoubleAnimation();
        }

当Freezable需要被复制的时候,WPF就会调用这个方法来进行复制。所以,如果我们仅仅使用普通的属性定义方式,动画时候使用的QuadraticDoubleAnimation类实际上是一个刚刚初始化过的新的Animation类,当然没有包含From和To的数据。

当使用了DependencyProperty的时候情况又怎么样呢?Freezable被复制时,CreateInstanceCore函数返回了一个空类,然后Freezable会将所有DependencyProperty中的值复制到新的类中。(我确定了Freezable确实会这么做,不过我还不清楚这种“自动深度复制”是Freezable的特性还是和DependencyProperty的特性有关,看起来似乎像前者,这里我没有深究,希望有人指教一下)

理解了这些,我们也可以尝试一些方法避开使用DependencyProperty(不过MSDN中极力推荐我们使用DependencyProperty,其实这样确实是一种更加良好的设计,下面还会看到有其他原因)

首先可以在CreateInstanceCore函数中手动的加入复制To和From属性的语句,像这样:

        protected override Freezable CreateInstanceCore()
        {
            QuadraticDoubleAnimation q = new QuadraticDoubleAnimation();
            q.From = this.From;
            q.To = 0; ;
           return q;
        }


运行程序,一切正常。

还有更简单的方法,当一个Freezable已经被冻结的时候,再使用它就不回去得到一个副本了(这对效率的提升是非常大的),所以只需要在Window的初始化的代码中将我们定义的QuadraticDoubleAnimation冻结就行了(或者在XAML中加入PresentationOptions:Freeze="True" 这需要事先声明xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options")

冻结之后,不使用DependecyProperty属性而只是用一般属性,运行程序,一切正常。

还没有完,在Freezable中我们了解到冻结之后属性不能再被改变,否则会抛出一个异常,我们来尝试一下。

加入一个按纽,Click事件处理函数如下:

        void btnClick(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(myAnimation.IsFrozen.ToString());
            myAnimation.To = 300;
        }

第一行确认myAnimation对象当前状态已经是被冻结了,第二行尝试改变它的值。

运行程序,点击按纽,弹出”True”,然后对象速度突然加快,To属性的更改生效了,这不是违反了Freezable的规则吗?

没错,这就是违反了Freezable的规则。这并不是WPF的Bug,而是我们事先已经违反了规则,Freezable的所有属性都需要定义成DependencyProperty,这样当改变属性是WPF才能进行判断,原来是我们先没有按规则出牌。

重新用DependencyProperty定义To和From两个属性,再运行程序,点按纽。

这下抛出了一个提示,To属性是只读属性。

所以我们还是按照规则使用DependencyProperty的好。

又有一个问题,刚才我们实现了一个挺酷的功能,在动画运行期实时改变了Animation的某个属性,这是在Animation没有冻结的情况下不大好办到的,不过我不推荐这样来改变属性,以下提供两种Animation没有被冻结且动画运行期改变属性的方法:

1 得到Animation的Clock(使用CreatClock方法),得到当前时间,使动画停止,改变值,然后从新开始动画,定位到同样的时间,这实际上建立了一个新的动画的实例,性能上会有损耗。
2 在CreatInstanceCore()方法中利用全局成员或者静态类或者其他什么方法,把创建的新的Animation的引用保存下来,需要的时候操作这个引用

涉及到的东西比较多比较杂,就懒得总结了~
问题讨论告一个段落,写了个小程序,了解了不少WPF的实现机制的东西。还是很好玩的 呵呵

转载于:https://www.cnblogs.com/yayx/archive/2007/04/21/721743.html

标签:Freezable,自定义,开始,QuadraticDoubleAnimation,Animation,double,DoubleAnimation,Depen
来源: https://blog.csdn.net/weixin_30840573/article/details/95756632

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有