在前面一篇「新年快樂」的隨筆中,咱們介紹了WinRT中的簡單動畫實現。其實在使用Windows/Windows Phone時,咱們都會看到一些動畫,最簡單的好比按下一個button時,該button的狀態變化就是動畫的一種。再好比彈出式窗口或菜單,也是一種動畫。WinRT中的動畫種類不少,可是分類有點兒讓初學者摸不着頭腦:主題過渡,主題動畫,視覺轉換,情節提要動畫。這些咱們就不說了,這裏主要說說自定義動畫,或者說是情節提要動畫(Storyboard Animation),由於這種動畫是咱們要經常使用的。git
可是在一個非遊戲類的App中添加動畫是有原則的:在UI狀態之間進行快速流暢的過渡,但不會使用戶分心;超出用戶的預期,可是又不會讓用戶厭煩。固然最大的前提是你的App的基本功能比較完美。若是有兩個App實現了相同的功能,一個有動畫,一個沒有,你會喜歡哪一個呢?答案顯而易見。何況在WinRT中,動畫實現比較簡單,效果又很好,因此just do it!程序員
今天咱們按實現方式介紹三類動畫:單一動畫,複合動畫,關鍵幀動畫。其中還分別介紹了用XAML/Code如何實現動畫。github
在這個頁中,點擊三個藍色的收藏類別條(分類/博主/博文),都會觸發兩個動畫:windows
1)類別條自己作360度的X軸旋轉app
2)對應的類別條下方的ListView作FadeIn/FadeOut的顯示/隱藏過渡ide
先說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的細節:this
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屬性來讓使用該控件的人指定是否須要動畫,而後在控件內部根據設置調用或不調用動畫。
該部分第二個動畫是顯示或隱藏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啦。哎,純屬刁民小技,讓各位看官見笑了。
咱們再看看稍微複雜些的動畫:在一個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 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