【WPF學習】第五十二章 動畫性能

  一般,爲用戶界面應用動畫只不過是建立並配置正確的動畫和故事板對象。但在其餘狀況下,特別是同時發生多個動畫時,可能須要更加關注性能。特定的效果更可能致使這些問題——例如,那些涉及視頻、大位圖以及多層透明等的效果一般須要佔用更多CPU開銷。若是不謹慎實現這類效果,運行它們使可能形成明顯抖動,或者會從其餘同時運行的應用程序搶佔CPU時間。緩存

  幸運的是,WPF提供了幾個可提供幫助的技巧。接下來的幾節將學習下降最大幀率以及緩存計算機顯卡中的位圖,這兩種技術能夠減輕CPU的負擔。安全

1、指望的幀率佈局

  正如前面所學習的,WPF試圖保持以60幀/秒的速度運動動畫。這樣可確保從開始到結束獲得平滑流暢的動畫。固然,WPF可能達不到這個目標。若是同時運行多個複雜的動畫,而且CPU或顯卡不能承受的話,整個幀率可能會降低(最好的情形),甚至可能會跳躍以進行補償(最壞的情形)。性能

  儘管不多提升幀率,但可能會選擇下降幀率,這多是由於如下兩個緣由之一:學習

  •   動畫使用更低的幀率看起來也很好,因此不但願浪費額外的CPU週期。
  •   應用程序運行在性能較差的CPU或顯卡上,並知道使用高的幀率時整個動畫的渲染效果還不如使用更低的幀率的渲染效果好。

  調整幀率很容易。只須要爲包含動畫的故事板使用Timeline.DesiredFrameRate附加屬性。下面的示例將幀率減半:測試

<Storyboard Timeline.DesiredFrameRate="30">

  下圖顯示了一個簡單的測試程序,該程序爲一個小球應用動畫,使其在Canvas控件上沿一條曲線運動。動畫

  這個應用程序開始在Canvas上繪製Ellipse對象。Canvas.ClipToBounds屬性被設置爲true,因此圓的邊緣不會超出Canvas控件的邊緣而進入窗口的其餘部分。spa

<Canvas ClipToBounds="True">
     <Ellipse Name="ellipse" Fill="Red" Width="10" Height="10"></Ellipse>
</Canvas>

  爲在Canvas控件上移動圓,須要同時進行兩個動畫——一個動畫用於更新Canva.Left屬性(從左向右移動圓),另外一個動畫用於改變Canvas.Top屬性(使圓上升,而後降低)。Canvas.Top動畫是可反轉的——一旦圓達到最高點,就會降低。Canvas.Left動畫不是可反轉的,但持續時間是Canvas.Top動畫的兩倍,從而使得這兩個動畫能夠同時移動圓。最後的技巧是爲Canvas.Top動畫使用DeceleartionRatio屬性。這樣,當圓達到最高點是上升的速度會更慢,這會建立更逼真的效果。3d

  下面是動畫的完整標記:code

<Window.Resources>
        <BeginStoryboard x:Key="beginStoryboard">
            <Storyboard Timeline.DesiredFrameRate="{Binding ElementName=txtFrameRate,Path=Text}">
                <DoubleAnimation Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(Canvas.Left)"
                         From="0" To="300" Duration="0:0:5">
                </DoubleAnimation>
                <DoubleAnimation Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(Canvas.Top)"
                         From="300" To="0" AutoReverse="True" Duration="0:0:2.5"
                         DecelerationRatio="1">
                </DoubleAnimation>
            </Storyboard>
        </BeginStoryboard>
    </Window.Resources>

 這個示例的真正目的是嘗試不一樣的幀率。爲查看某個特定幀率的效果,只須要在文本框中輸入合適的數值,而後單擊Repeat按鈕便可。而後動畫就會使用新的幀率(經過數據綁定表達式獲取新的幀率)觸發,從而能夠觀察動畫的效果。在更低的幀率下,橢圓不會均勻移動——而會在Cavans控件中的跳躍。

  也可以使用代碼調整Timeline.DesiredFrame屬性。例如,可能但願讀取靜態屬性RenderCapability.Tier以肯定顯卡支持的渲染級別。

2、位圖緩存

  位圖緩存通知WPF獲取內容的當前位圖圖像,並將其複製到顯卡的內存中。這時,顯卡能夠控制位圖的操做和顯示的刷新。這個處理過程比讓WPF完成全部工做要快不少,而且和顯卡不斷通訊。

  若是運用得當,位圖緩存能夠改善應用程序的繪圖性能。但若是運用不當,就會浪費顯存而且實際上會下降性能。因此,在使用位圖緩存以前,須要確保真正合適。下面列出一些指導原則:

  •   若是正在繪製的內容須要頻繁地從新繪製,使用位圖緩存多是合理的。由於每次後續的從新繪製將更快。一個例子是當其餘一些具備動畫的對象浮動在形狀表面上時,使用BitmapCacheBrush畫刷繪製形狀的表面。儘管形狀沒有變化,可是形狀的不一樣部分被遮擋住或顯露出來,從而須要從新繪製。
  •   若是元素的內容常常變化,使用位圖緩存可能不合理。由於可視化內容每次改變時,WPF須要從新渲染位圖將其發送到顯卡緩存,而這須要耗費時間。該規則有些晦澀,由於某些改變不會致使緩存無效。安全操做的例子包括使用變換旋轉以及從新縮放元素、剪裁元素、改變元素的透明度以及應用效果。另外一方面,改變元素的內容、佈局以及各式將強制從新渲染位圖。
  •   儘可能少緩存內容。位圖越大,WPF存儲緩存副本所需的時間越長,須要的顯存越多。一旦耗盡顯存,WPF將被迫使用更慢的軟件渲染。

  爲更好地理解位圖緩存,使用一個簡單示例是有幫助的,下圖例舉一個示例,一個動畫推進一個簡單的圖像——正方形——在Canvas面板上移動,Canvas面板包含一條具備複雜集合圖形的路徑。但正方形在Canvas面板表面上移動時,強制WPF從新計算路徑並填充丟失的部分。這會帶來極大的CPU負擔,而且動畫甚至可能開始變得斷斷續續。

 

   可採用幾種方法解決該問題。一種選擇是使用一幅位圖替換背景,WPF可以更高效地管理位圖。更靈活的選擇是使用位圖緩存,這種方法可繼續將存活的、可交互的元素做爲背景。

  爲啓用位圖緩存功能,將相應元素的CacheMode屬性設置爲BitmapCache。每一個元素都提供了CacheMode屬性,這意味着能夠精確選擇爲哪一個元素使用這一特徵。

<Path CacheMode="BitmapCache" ...></Path>

  經過這個簡單修改,可當即看到區別。首先,窗口顯示的事件要稍長一些。但動畫的運行將更平滑,而且CPU的負擔將顯著下降。可經過Windows任務管理器進行檢查——常常能夠看到CPU的負擔從接近100%減小到20%一下。

  一般,當啓用位圖緩存時,WPF採用元素當前尺寸的快照並將其位圖複製到顯卡中。若是以後使用ScaleTransform放大元素,這會變成一個問題。在這種狀況下,將放大緩存的位圖,而不是實際的元素,當放大元素時這會致使模糊放大以及色塊。

  例如,設想一個修訂過的示例。在這個示例中,第二個同步動畫擴展Path使其爲原始尺寸的10倍,而後縮回原始尺寸。爲確保具備良好的顯示質量,可以使用5倍於Path原始尺寸的尺寸緩存其位圖:

<Path ...>
    <Path.CacheMode>
        <BitmapCache RenderAtScale="5"></BitmapCache>
    </Path.CacheMode>
</Path>

  這樣可解決像素化問題。雖然緩存的位圖仍比Path的最大動畫尺寸(最大尺寸達10倍於其原始尺寸)小,但顯卡能使位圖的尺寸加倍,從5倍到10倍,而不會有任何明顯的縮放問題。更重要的是,這可以使應用避免過多地使用顯存。

相關文章
相關標籤/搜索