Windows 10 UWP 26 of N: Visual layer part 2 Xaml Interrop and simple animation

  • 297
  • 0
  • UAP
  • 2021-04-30

介紹如何在Composition API中與XAML的控制元件互動以及使用基礎動畫效果。

今天要介紹的是Composition api與XAML互動的方法,說穿了也就是如何在既有的XAML Control上做些特效但使用的API非Xaml的特效。

先來個建立簡單一點的特效八~先開啟UWP專案然後在Mainpage.xaml的部分修改如下

<Page
    x:Class="BasicEffectUWP.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:BasicEffectUWP"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Slider x:Name="blurSilder" Header="Blur amount value" ValueChanged="Slider_ValueChanged"/>
        <Grid>
            <Image x:Name="targetImage" Source="ms-appx:///Assets/ninja-cat-windows-10.jpg" SizeChanged="Image_SizeChanged"/>
        </Grid>
    </StackPanel>
</Page>

然後在MainPage.xaml.cs的部分加入初始化Composition API的方法。

private void InitializeCompositor()
        {
            frameVisual = ElementCompositionPreview.GetElementVisual(Window.Current.Content);
            frameCompositor = frameVisual?.Compositor;
        }

來複習一下在Composition API中會一直常常需要用到的是Compositor的這個Class,這個Class就如同之前所說的是建立composition的root 所以之後的composition API會不斷使用到該class。

而這邊要介紹一個語法 ElementCompositionPreview 這個Class就是與XAML的Control作互動的Class所以只要和XAML互動的Composition就是少不了要用這個Class!這個Class提供幾個Method如下

public static Visual GetElementVisual(UIElement element);
public static Visual GetElementChildVisual(UIElement element);
public static void SetElementChildVisual(UIElement element, Visual visual);
public static CompositionPropertySet GetScrollViewerManipulationPropertySet(ScrollViewer scrollViewer);

以上的方法是從APIContract V2到V4都有支援的(也就是從10586到15063的SDK)而在V4的Contract中新增了如下的Method

public static CompositionPropertySet GetPointerPositionPropertySet(UIElement targetElement);
public static void SetImplicitHideAnimation(UIElement element, ICompositionAnimationBase animation);
public static void SetImplicitShowAnimation(UIElement element, ICompositionAnimationBase animation);
public static void SetIsTranslationEnabled(UIElement element, Boolean value);

這篇使用到的API是從API Contract V2而來的所以基本上都可以在最新的SDK實作。


剛剛上段的最後提到的這些方法中比較常用到的是GetElementVisual以及SetElementChidVisual這兩個方法!

基本上GetElementVisual就是把XAML tree上的Control取出該control的Visual!在UWP的持續改進的RoadMap中將會把所有的Xaml Control都會加上Composition API中的Visual。
然後SetElementChildVisual則是把Visual覆蓋到該Xaml control的Visual上!所以這邊會有個概念是所有的Xaml control都只能有一個Visual但是這個Visual可以藉由Visual自身的重疊來達到多個visual。

上面這樣說的似乎會讓大家有點模糊讓我們直接看Code以及執行結果來說明會比較清楚! 剛剛XAML的部分在Image的SizeChanged的Method程式碼如下

private void Image_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            imageRenderSize = new Vector2((float)e.NewSize.Width, (float)e.NewSize.Height);
            var visual = CreateBlurEffect();
            visual.Size = imageRenderSize;
            ElementCompositionPreview.SetElementChildVisual(targetImage, visual);
        }

這邊當Image 從Image uri或是Stream中讀取並且被decode完成後會得到實際的Render size!所以之所以會選用SizeChanged事件就是因為這樣。

然後來建立Blur的特效八~ 這邊指的Blur就是毛玻璃特效(等等API中會出現該名詞)在CreateBlurEffect的方法中加入如下的Code

private SpriteVisual CreateBlurEffect()
        {
            var blurEffect = new GaussianBlurEffect
            {
                Name = "Blur",
                BlurAmount = blurAmount,
                Source = new CompositionEffectSourceParameter("BackDrop"),
                BorderMode = EffectBorderMode.Hard
            };
            var factory = frameCompositor?.CreateEffectFactory(blurEffect, new List<string> { "Blur.BlurAmount" });
            brush = factory?.CreateBrush();
            var dropBrush = frameCompositor?.CreateBackdropBrush();
            brush.SetSourceParameter("BackDrop", dropBrush);
            var blurSprite = frameCompositor?.CreateSpriteVisual();
            blurSprite.Brush = brush;
            return blurSprite;
        }

這邊需要注意的是....GaussianBlurEffect是在Win2D的Nuget package中喔!所以記得先去Nuget 中加入Win2D.uwp的參考!這邊GaussianBlurEffect是產生毛玻璃特效非常有效的物件!因為Win2D就是把DirectX 2D的需多功能簡化給UWP以及Store app使用~

GaussianBlurEffect重點屬性為 Name、BlurAmount、Source,這三個屬性將可以使其與Composition API作互動並且在串接給Xaml control。
將effect建立好之後使用compositor建立effect 的Factory並且將該effect帶入進去然後把建立參數列表(也就是List<string>)這邊的意思是把該特效的屬性可以藉由工廠把與參數做連結。
接者使用factory產生brush繪製成筆刷準備給visual使用然後再建立backdropbrush會把effect的連動特效轉換傳入給Brush(筆刷)中。

最後藉由compositor產生出SpriteVisual,並且把SpriteVisual的Brush替換掉之後拋出去給Image_SizeChanged的程式碼片段,然後再把該Visual的Size做更新(與圖片相符的Size)最後再由SetElementChildVisual的方法把Visual放入XAML tree之中。

 

SpriteVisual是蠻基礎型別使用的Visual因為這個物件繼承了ContainerVisual所以可以這個Visual可以是個Visual的容器且該Visual是專門處理2D平面。

簡單來說明這個Method就是產生一個毛玻璃特效的2D物件!而這個2D物件的Brush也就是GaussianBlurBrush然後再透過CompositionBackDropBrush使GuassianBlurBrush的特效可以產生連動性!

最後來把xaml上的slider的ValueChanged的Method的Code補上八

float blurAmount => (float)blurSilder.Value;

private void Slider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
        {
            brush.Properties.InsertScalar("Blur.BlurAmount", blurAmount);
        }

這邊是建立一global的parameter 命名為blurAmount然後再Slider的ValueChanged的時候會再對SpriteVisual使用的Brush內使用到的GaussianBlurEffect的參數做更新!

整個MainPage.xaml.cs的程式碼如下

public sealed partial class MainPage : Page
    {
        Visual frameVisual;
        Compositor frameCompositor;
        CompositionEffectBrush brush;
        Vector2 imageRenderSize;

        float blurAmount => (float)blurSilder.Value;

        public MainPage()
        {
            this.InitializeComponent();
            InitializeCompositor();
        }

        private void InitializeCompositor()
        {
            frameVisual = ElementCompositionPreview.GetElementVisual(Window.Current.Content);
            frameCompositor = frameVisual?.Compositor;
        }

        private SpriteVisual CreateBlurEffect()
        {
            var blurEffect = new GaussianBlurEffect
            {
                Name = "Blur",
                BlurAmount = blurAmount,
                Source = new CompositionEffectSourceParameter("BackDrop"),
                BorderMode = EffectBorderMode.Hard
            };
            var factory = frameCompositor?.CreateEffectFactory(blurEffect, new List<string> { "Blur.BlurAmount" });
            brush = factory?.CreateBrush();
            var dropBrush = frameCompositor?.CreateBackdropBrush();
            brush.SetSourceParameter("BackDrop", dropBrush);
            var blurSprite = frameCompositor?.CreateSpriteVisual();
            blurSprite.Brush = brush;
            return blurSprite;
        }

        private void Slider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
        {
            brush.Properties.InsertScalar("Blur.BlurAmount", blurAmount);
        }

        private void Image_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            imageRenderSize = new Vector2((float)e.NewSize.Width, (float)e.NewSize.Height);
            var visual = CreateBlurEffect();
            visual.Size = imageRenderSize;
            ElementCompositionPreview.SetElementChildVisual(targetImage, visual);
        }
    }

最後來看看執行結果八

當Silder的Value為0的圖片效果並沒有毛玻璃特效

當Value拉到50的時候

拉到100的時候~基本上已經看不出甚麼東西啦~~


總結

這篇主要講到的是最常使用到的與Xaml control互動需要的API以及基礎的特效。後須第三篇會把這個基礎特效的部分補齊。

 

***以上Code以及說明以都有可能隨著Windows 10 的版本以及Visual Studio 版本有所調整!***

參考資料 MSDN

下次再分享Windows 10 的新技術拉~