博客园客户端UAP开发随笔 -- App的心动杀手锏:动画

前言

在前面一篇“新年快乐”的随笔中,我们介绍了WinRT中的简单动画实现。其实在使用Windows/Windows Phone时,我们都会看到一些动画,最简单的比如按下一个button时,该button的状态变化就是动画的一种。再比如弹出式窗口或菜单,也是一种动画。WinRT中的动画种类很多,但是分类有点儿让初学者摸不着头脑:主题过渡,主题动画,视觉转换,情节提要动画。这些我们就不说了,这里主要说说自定义动画,或者说是情节提要动画(Storyboard Animation),因为这种动画是我们要常用的。

但是在一个非游戏类的App中添加动画是有原则的:在UI状态之间进行快速流畅的过渡,但不会使用户分心;超出用户的预期,但是又不会让用户厌烦。当然最大的前提是你的App的基本功能比较完美。如果有两个App实现了相同的功能,一个有动画,一个没有,你会喜欢哪个呢?答案显而易见。况且在WinRT中,动画实现比较简单,效果又很好,所以just do it!

今天我们按实现方式介绍三类动画:单一动画,复合动画,关键帧动画。其中还分别介绍了用XAML/Code如何实现动画。

收藏页面中的动画 - 单一动画

在这个页中,点击三个蓝色的收藏类别条(分类/博主/博文),都会触发两个动画:

1)类别条本身做360度的X轴旋转

2)对应的类别条下方的ListView做FadeIn/FadeOut的显示/隐藏过渡

用XAML定义动画

先说360度旋转的做法。我们定义一个Template Control,然后在该Control的Style中定义动画:

<Style TargetType="local:FavoriteGroupControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:FavoriteGroupControl">
                    <Grid x:Name="grid_Header" Height="60" Background="{ThemeResource CNBlogsThemeColor}">
                        <Grid.Projection>
                            <PlaneProjection/>
                        </Grid.Projection>
                        <Grid.Resources>
                            <Storyboard x:Name="sb_Roll">
                                <DoubleAnimation Storyboard.TargetName="grid_Header"                               Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)"
From="0" To="360" Duration="0:0:00.50"/>
                            </Storyboard>
                        </Grid.Resources>
……
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

我去掉了不重要的部分,只留下了要说明的部分,完整代码请看Windows Phone project中的Theme/Generic.xaml。

首先要定义<Grid.Projection>属性,<PlaneProjection/>表示该Grid需要做X/Y/Z轴的旋转,这个定义是必须的(如果不定义的话后面会出错)。其次,要在<Grid.Resources>中定义Storyboard,它包含有一个<DoubleAnimation>(在后面的动画中会在一个Storyboard中包含多个DoubleAnimation)。

再看DoubleAnimation的细节:

1) Storyboard.TargetName指明我们要对名字叫做gird_Header的控件下毒手

2)Storyboard.TargetPrpoerty指明了我们要玩弄那个控件的PlaneProjection.RotationX属性

3)From/To指明了要把该控件旋转一周即360度

4)Duraion指明在0.5秒内完成

好了,动画定义好了,如何触发呢?在MainPage.xaml中,你可以找到以下代码段:

<local:FavoriteGroupControl x:Name="fgc_Category" Tapped="sp_category_Tapped" Margin="0,10"/>

这里定义了一个sp_category_Tapped事件,顺藤摸瓜,我们在MainPage.xaml.cs中找到以下代码:

private void sp_category_Tapped(object sender, TappedRoutedEventArgs e)
{
            this.fg_Category.Tapped();
}

请注意!一个控件的内置动画只应该在其内部触发,而不是由外部控制。所以,这次摸的瓜是个傻瓜:) 真正的触发动画的Code应该在FavoriteGroupControl.cs中找:

protected override void OnTapped(TappedRoutedEventArgs e)
{
    Storyboard sb = this.GetTemplateChild("sb_Roll") as Storyboard;
    if (sb != null)
    {
        sb.Begin();
    }
}

它先根据名称“sb_Roll”获得Storyboard的实例sb,然后调用其Begin()方法使其开始旋转。在XAML中定义的Storyboard,都要通过事件处理代码调用Begin()来激活动画。

这里有两点要说明:

1)为什么用动画?因为凡是在用户点击屏幕时,我们都应该给予视觉上的响应,免得心急的用户狂点屏幕造成手指受伤,作为程序员的我们要有爱心

2)为什么用旋转动画?因为我喜欢,就让我任性一次吧,不容易啊。当然也可用别的动画,比如斜一下,或者陷下一点儿。

3)为什么在控件内部调用Begin()?因为你给人家提供一个控件,按下后旋转是该控件的预定行为,不要再让使用该控件的人再去管什么动画操作。当然,你也可以提供一个TemplateBinding属性来让使用该控件的人指定是否需要动画,然后在控件内部根据设置调用或不调用动画。

用Code定义动画

该部分第二个动画是显示或隐藏ListView,这次我们用另外一种方法实现的动画,用Code实现,而不是用XAML实现。看code:

class FavoriteGroup
    {
        bool ShowListView = true;
        ListView lvDetail;
        Storyboard sbShow, sbHide;

        public FavoriteGroup(ListView lv)
        {
            this.lvDetail = lv;
            CreateStoryboard();
            this.sbHide.Completed += sbHide_Completed;
        }

        private void sbHide_Completed(object sender, object e)
        {
            this.lvDetail.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
        }

        public void Tapped()
        {
            this.ShowListView = !this.ShowListView;
            if (this.ShowListView)
            {
                this.lvDetail.Opacity = 0;
                this.lvDetail.Visibility = Windows.UI.Xaml.Visibility.Visible;
                this.sbShow.Begin();
            }
            else
            {
                this.sbHide.Begin();
            }
        }

        private void CreateStoryboard()
        {
            // show listview in 1 second
            DoubleAnimation daShow = new DoubleAnimation();
            daShow.From = 0;
            daShow.To = 1;
            daShow.Duration = new Windows.UI.Xaml.Duration(TimeSpan.FromSeconds(1));

            this.sbShow = new Storyboard();
            sbShow.Children.Add(daShow);
            Storyboard.SetTarget(daShow, this.lvDetail);
            Storyboard.SetTargetProperty(daShow, "Opacity");

            // hide listview in 1 second
            DoubleAnimation daHide = new DoubleAnimation();
            daHide.From = 1;
            daHide.To = 0;
            daHide.Duration = new Windows.UI.Xaml.Duration(TimeSpan.FromSeconds(1));

            this.sbHide = new Storyboard();
            sbHide.Children.Add(daHide);
            Storyboard.SetTarget(daHide, this.lvDetail);
            Storyboard.SetTargetProperty(daHide, "Opacity");
        }

    }

在构造函数中,调用了CreateStoryboard()方法,首先定义了两个Storyboard,在每个Storyboard中定义了一个DoubleAnimation,一个是用1秒时间把ListView的Opacity值从0变到1(显示),另一个是用1秒时间把Opacity从1变到0(隐藏)。上面的写法等价于这个XAML:

<Storyboard x:Name="sbShow">
    <DoubleAnimation Storyboard.TargetName="lvDetail"
                                  Storyboard.TargetProperty="Opacity"
                                  From="0" To="1" Duraion="0:0:1"/>
</Storyboard>
<Storyboard x:Name="sbHide">
    <DoubleAnimation Storyboard.TargetName="lvDetail"
                                  Storyboard.TargetProperty="Opacity"
                                  From="1" To="0" Duraion="0:0:1"/>
</Storyboard>

为什么在这里不用XAML写法而用Code直接定义呢?是为了显示技巧吗?你猜对啦!因为在MainPage.xaml中,有三个ListView,分别为lv_category, lv_author, lv_blog,如果要用XAML定义动画,要对这个三个ListView各写一遍,重复了三次,只是ListView的名字不同,太难看啦!注意素质!于是搞了一个FavoriteGroup类(可能名字不太好,叫刺杀金xx怎么样?),里面用code包了一下,把ListView作为参数传入,就可以复用code啦。哎,纯属刁民小技,让各位看官见笑了。

Setting页面中About的动画 - 复合动画

我们再看看稍微复杂些的动画:在一个Storyboard中包含多个DoubleAnimatoin。

<Storyboard x:Name="sb_LogoMoveUp">
            <DoubleAnimation Duration="0:0:0.8"
                             From="200"
                             Storyboard.TargetName="image_Logo"
                             Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.GlobalOffsetY)"
                             To="0" />
            <DoubleAnimation Duration="0:0:0.8"
                             From="360"
                             Storyboard.TargetName="image_Logo"
                             Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationZ)"
                             To="0" />
            <DoubleAnimation Duration="0:0:0.8"
                             From="0"
                             Storyboard.TargetName="image_Logo"
                             Storyboard.TargetProperty="Opacity"
                             To="1" />
        </Storyboard>

在SettingsPage.xaml中,我们在sb_LogoMoveUp的Storyboard中定义了三个动画:

1)把image_Logo上移200个像素

2)让image_Logo旋转360度

3)让image_Logo透明度从0变成1

以上三个动画同时进行,都是在0.8秒内完成,于是我们看到了那个图片从下方“滚动”(不是滑动)到上方,并逐渐清晰,整个过程很是优雅大方,毕竟滚动摩擦比滑动摩擦小很多(扯远了),不拖泥带水,很有节操的。

要说明几点:

1)用复合动画,可以对一个控件的不同属性进行同时操作,以形成单一动画无法完成的复杂效果。比如我们是对image_Logo的三个属性同时进行操作。当然也可以不同时,用BeginTime属性来设置一下启动时间即可。

2)在这里为什么要用动画?因为我喜欢超出用户的预期,给他们以动态视觉享受,而不是干巴巴的看着一个图片发呆。用户一高兴,没准儿就给个好评了。

Windows 8.1版本中的PostControl动画 - 关键帧动画

大家可以查看Windows 8.1 project的Theme/Generic.xaml看完整代码。

在这个Control中,左边那个图,点击右侧箭头,将会向左滑动,成为右边那个样子。

这个滑动过程不是线性的,因此要用到关键帧,意思是说:在某个时间点,做这件事;到下一个时间点,再做那件事。看下面的XAML代码:

<Storyboard x:Name="sb_Button_out">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SecondViewTrans"    Storyboard.TargetProperty="X" BeginTime="0:0:0">    <SplineDoubleKeyFrame  KeyTime="00:00:00.00" Value="480"/>    <SplineDoubleKeyFrame  KeyTime="00:00:00.10" Value="460"/>    <SplineDoubleKeyFrame  KeyTime="00:00:00.20" Value="400"/>    <SplineDoubleKeyFrame  KeyTime="00:00:00.30" Value="300"/>    <SplineDoubleKeyFrame  KeyTime="00:00:00.40" Value="170"/>    <SplineDoubleKeyFrame  KeyTime="00:00:00.50" Value="0"/>    <SplineDoubleKeyFrame  KeyTime="00:00:00.54" Value="32"/>    <SplineDoubleKeyFrame  KeyTime="00:00:00.58" Value="60"/>    <SplineDoubleKeyFrame  KeyTime="00:00:00.62" Value="80"/>    <SplineDoubleKeyFrame  KeyTime="00:00:00.66" Value="92"/>    <SplineDoubleKeyFrame  KeyTime="00:00:00.70" Value="96"/>    <SplineDoubleKeyFrame  KeyTime="00:00:00.74" Value="92"/>    <SplineDoubleKeyFrame  KeyTime="00:00:00.78" Value="80"/>    <SplineDoubleKeyFrame  KeyTime="00:00:00.82" Value="60"/>    <SplineDoubleKeyFrame  KeyTime="00:00:00.86" Value="32"/>    <SplineDoubleKeyFrame  KeyTime="00:00:00.90" Value="0"/></DoubleAnimationUsingKeyFrames>
</Storyboard>

其中的那个<SplineDoubleKeyFram>就是关键帧的定义,在每个时间点,都定义了目标控件的X位置。可以看到第6个关键帧,X值已经是0了,为什么又从0变大了呢?这样就产生了触底反弹的效果,让目标控件弹回到最大96的位置,最后再回到0。

需要注意的是,关键帧只能对某个控件的唯一的一个属性操作,不能同时操作多个属性。而在上一节的复合动画中,是对某个控件的多个属性同时操作,但是不能对某个属性定义两次DoubleAnimation。这个要牢记。

小结

哦,办公室已经自动关灯了,看样子该给公家省电了,拍屁股回家吧。但是大家要记住哟,动画不能乱用,不能让用户讨厌,不能人为影响系统流畅度,不能影响系统性能。

比如在博客园UAP的WP版本中,我们在很多小地方使用了动画,比如热门页中下拉ListView时右上角的数字变化,博主页中下拉ListView时页面标题的变化,等等。这些动画都是和当前的操作密切相关的,但它们又不会强烈吸引用户注意。

在“新年快乐”页中,是故意要展示一下一些东西,所以做了很多动画。另外,在“新年快乐”页中,还用到了不使用Storyboard/DoubleAnimation/KeyFrame等技术,而是用纯code操作XAML元素的位置来制作的动画(游戏开发的基本功),我们后面再聊!

分享代码,改变世界!

Windows Phone Store App link:

http://www.windowsphone.com/zh-cn/store/app/博客园-uap/500f08f0-5be8-4723-aff9-a397beee52fc

Windows Store App link:

http://apps.microsoft.com/windows/zh-cn/app/c76b99a0-9abd-4a4e-86f0-b29bfcc51059

GitHub open source link:

https://github.com/MS-UAP/cnblogs-UAP

MSDN Sample Code:

https://code.msdn.microsoft.com/CNBlogs-Client-Universal-477943ab

MS-UAP

2015/1/9

时间: 2024-10-05 13:21:51

博客园客户端UAP开发随笔 -- App的心动杀手锏:动画的相关文章

博客园客户端UAP开发随笔 -- App连接云端内容的桥梁:WebView

当你辛苦的从网上爬下来一篇文章之后,怎么在你的应用内展示这些包含HTML标记的文章?如果你使用的是Javascript开发应用,恭喜你,直接塞进页面就可以了,同时说明你很熟悉页面开发,而现在windows也支持这种方式.但是对于使用XAML开发的应用怎么办呢?我们还有WebView控件可以用. 越来越多的服务器端API返回的数据使用HTML了,所以我们也不得不对WebView多了解一些. WebView有个Bug:放在Grid里时,最右侧有一个pixel缝隙时隐时现.要小心,别让PM抓住你的小辫

博客园客户端UAP开发随笔 -- App UI设计的三大纪律八项注意

前言 每一个页面都是这个App的门面,尤其是主页面,看上去干净整洁清爽宜人容易操作,那么你的App就成功了一半.这也反映出了你这个开发团队的基本审美素质和设计理念.如果你不是一个团队,而是一个个人开发者,建议你好好读读以下心得体会,相信会帮助你做出好看而实用的App.用一堆拥有丑陋UI的App充斥Window Store,不是我们高大上的程序员所为,被其他手机开发平台的开发者们耻笑. 三大纪律: 1)不乱用颜色.一个页面内不要超过3种颜色 2)不乱用大图片当背景.你是想让用户看你的背景图片呢,还

博客园客户端UAP开发随笔 – 让自己的App连接世界(2):WinRT中的内置分享

看到一篇眼前一亮的博文,是不是有一种希望其他小伙伴都能看到的感觉呢?有没有一种“不转不是程序员”的冲动呢?在 PC 浏览器上看到还好办,直接网址复制,另一边 IM 上就发过去了,但是如果是 App 中的内容,就没这么方便了,总不能那边 IM 上喊话:“隔壁老王,博客园上有篇叫‘博客园客户端(Universal App)开发随笔 – 为应用插上分享的翅膀’的博文超好看,要不你也瞅瞅?”.隔壁老王再去搜索就太麻烦了.可能你会说了,嗨,直接分享不就完了么.嗯,没错,就是分享功能.那么如何把分享功能引入

博客园客户端UAP开发随笔 -- 搭建App之间的桥梁

开发Windows Phone应用的同学们应该都注意到了,Windows Phone 为了安全性,对应用的限制还是比较多的.我记得一位360的同学很无奈的说:WP太安全了,我们这些做WP上360卫士的基本上没啥可做的.但是当WP360那个App出来后,还是有很多用户安装了呢,尽管真的对安全没什么太大的帮助,但是对用户了解自己的手机的使用情况还是有帮助的.其中一位用户的评价是:从android到PC,我一直用360, 所以在WP上我也用.呵呵,粉丝啊!具体有什么用其实他也不关心铁粉而已. 扯远了!

博客园客户端UAP开发随笔 -- 让你自己和你的App有国际范儿

大家是不是发现,在商店中看到的高大上的应用都有着多语言支持,可以根据操作系统的语言自动适配:或者可以通过用户的选择,显示对应的语言界面,确实很高大上呢.不过这个可不是什么难事,通过简单的几个步骤,让你的应用也高大上起来,支持多语言.这样在你以后的简历里,也可以写上:面向国际市场开发过多语言应用.听上去相当有底气! 0. 准备工作 在建立多语言支持前,首先要看一下 Manifest 文件中的默认语言选项,将它设置为你希望的默认显示语言. 接下来建立存放语言字符串的文件夹.如果是Universal

博客园客户端UAP开发随笔 -- 狡兔三窟:App内的三种通知消息的实现

使用应用时,总会有各种各样的交互,其中有些是需要和用户交互的,有些是仅仅告知用户某些信息的.对于前者,通常的解决方案都是弹出一个带有按钮(或其他控件)的对话框,上面有需要用户知晓的信息,以及需要用户通过按钮(或其他控件)做出的响应交互,这里就不再介绍.对于后者,那些不需要用户做出交互,仅仅是告知用户信息的,实现方式大家各有不同,本文将提出几种解决思路,抛砖引玉,希望通过交流,得到更好的人机交互解决方案. 1. 弹出窗口提示 这个方法比较简单粗暴,直接调用了系统的 MessageDialog 方法

博客园客户端UAP开发随笔 -- 让自己的App连接世界:WinRT中的微博分享

近年来社交app可谓红红火火,大家每天发微博,分享到朋友圈也是不亦乐乎.我们的Universal应用自然也希望加入社交分享的功能.然而国内主流的社交平台微博和微信还没有推出适用于Universal应用的SDK,怎么办呢?当然敲碗等开饭也是办法,另一方面我们也可以自己做一些努力.今天我们就想分享一下我们在Universal应用中实现分享到微博功能上的探索. 准备 想要分享到微博,先得注册成为微博开发者.我们在http://open.weibo.com/上注册好开发者账号,建立个测试应用,就可以拿到

博客园客户端UAP开发随笔 -- 奔跑吧,页面!

前言 页面导航,是App中的基本功,一般的App,一来一去,只需要简单的Navigate + Back就行了,一个复杂的App可能需要很多导航模式的混合才能实现最佳用户体验. SplashScreen 启动屏幕 我们先从最开始的SplashScreen说起吧.如果你把启动屏幕做成一个Page,启动时先显示一下,然后假装忙乎两秒,跳到下一个主页面开始进入正题,这个好像看上去也很美好.但是当用户玩命儿按Back键时,哦,露出马脚了,启动页面被唤出了.不过这个bug倒是不妨作为一个新奇的体验. MSD

博客园客户端UAP开发随笔--自定义控件的左膀右臂

前言 我们上一次说到了App的精灵:自定义控件.这一次,我们接着这一话题,说说自定义控件的两个得力助手: 选择器 - TemplateSelector 转换器 – Converter 这两个东西能帮助自定义控件更为简单方便地被使用,所以必须掌握. 数值转换器 Converter 这个大家可能不陌生,因为在MSDN里,介绍到Data Binding时,总会顺带着介绍一下数据转换,比如这个网页: http://msdn.microsoft.com/library/windows/apps/xaml/