大多數使用 Visual Layer 功能的 WinUI 和 Windows App SDK 應用程式會使用 XAML 來定義主要的 UI 內容。 WinUI 在 XAML 框架與視覺層中提供功能,使結合這兩種技術更容易創造出令人驚豔的使用者體驗。 XAML 與視覺層互操作功能可用於創造僅靠 XAML API 無法達成的進階動畫與特效。 這包括:
- 刷子效果,例如模糊和磨砂玻璃效果
- 動態光影效果
- 捲軸驅動的動畫與視差
- 自動版面動畫
- 像素精準陰影
這些效果和動畫可以套用到現有的 XAML 內容上,所以你不必大幅重組 WinUI 應用程式來利用這些功能。 版面動畫、陰影與模糊效果則在下方的食譜區段有介紹。 關於實作視差的程式碼範例,請參見 ParallaxingListItems 範例。 WindowsCompositionSamples 資料庫還有其他幾個用於實作動畫、陰影與特效的範例。
XamlCompositionBrushBase 類別
Microsoft.UI.Xaml.Media.XamlCompositionBrushBase 提供了一個基底類別,用於用 CompositionBrush 繪製區域的 XAML 筆刷。 這可以輕鬆地將模糊或磨砂玻璃等合成效果套用到 XAML UI 元素上。
關於在 XAML UI 中使用筆刷的更多資訊,請參考 筆刷 部分。
關於程式碼範例,請參閱 XamlCompositionBrushBase 的參考頁面。
XamlLight 系列
Microsoft.UI.Xaml.Media.XamlLight 提供了一個 XAML 燈光效果的基底類別,能用 CompositionLight 動態點亮區域。
有關使用燈光的更多資訊,包括 XAML UI 元素的光照,請參見 Lighting 章節。
關於程式碼範例,請參閱 XamlLight 的參考頁面。
使用 WinUI XAML
ElementCompositionPreview 是一個靜態類別,提供 XAML 與視覺層互操作功能。 關於視覺層及其功能的概述,請參見 視覺層。 ElementCompositionPreview 類別提供以下 WinUI 互操作方法:
- GetElementVisual:取得用來渲染此元素的範例視覺效果
- SetElementChildVisual:將「handin」視覺化設定為該元素視覺樹的最後一個子節點。 這個視覺會在元素的其他部分上方繪製。
- GetElementChildVisual:使用 SetElementChildVisual 取得視覺集合
- GetScrollViewerManipulationPropertySet:取得一個物件,可以用來根據 ScrollViewer 的捲動偏移量製作 60fps 動畫
GetElementVisual
ElementCompositionPreview.GetElementVisual 會回傳一個「handout」視覺化,用於渲染給定 的 UIElement。 像Visual.Opacity、Visual.Offset和Visual.Size這些屬性,是由 XAML 框架根據 UIElement 的狀態來設定的。 這使得隱含的重新定位動畫(見 配方)等技術得以實現。
請注意,由於 Offset 和 Size 是 XAML 框架佈局的結果,開發者在修改或動畫這些屬性時應謹慎。 開發者應僅在元素的左上角在佈局中與其父元素的位置相同時,才應修改或動畫化偏移。 尺寸通常不應被更改,但訪問該屬性可能會有幫助。 例如,下方的 Drop Shadow 和 Frosted Glass 範例使用簡報視覺的大小作為動畫的輸入。
另外,更新的講義視覺化屬性不會反映在對應的 UIElement 中。 舉例來說,將 UIElement.Opacity 設為 0.5,會將相應的 Visual 元素的不透明度也設為 0.5。 然而,將講義視覺的 不透明度 設為 0.5 會讓內容呈現為 50% 不透明度,但不會改變對應 UIElement 的不透明度屬性值。
偏移動畫範例
不對
<Border>
<Image x:Name="MyImage" Margin="5" />
</Border>
// Doesn’t work because Image has a margin!
ElementCompositionPreview.GetElementVisual(MyImage).StartAnimation("Offset", parallaxAnimation);
正確
<Border>
<Canvas Margin="5">
<Image x:Name="MyImage" />
</Canvas>
</Border>
// This works because the Canvas parent doesn’t generate a layout offset.
ElementCompositionPreview.GetElementVisual(MyImage).StartAnimation("Offset", parallaxAnimation);
設置元素子視覺
ElementCompositionPreview.SetElementChildVisual 允許開發者提供一個「handin」視覺化,該視覺化會作為元素視覺樹的一部分出現。 這讓開發者能建立一個「合成島」,讓視覺化內容能出現在 XAML 介面中。 開發者在使用此技術時應保持謹慎,因為視覺化內容無法享有 XAML 內容同等的無障礙性與使用者體驗保證。 因此,通常建議僅在必要時使用此技術來實現如下方配方區段所見的自訂效果。
GetAlphaMask 方法
Image、 TextBlock 和 Shape 各自實作一個叫 GetAlphaMask 的方法,該方法會回傳一個 CompositionBrush ,代表帶有元素形狀的灰階影像。 這個 CompositionBrush 可以作為 Composition DropShadow 的輸入,讓陰影能反映元素的形狀,而不是長方形。 這使得文字、帶有 alpha 的影像和形狀都能呈現像素完美、輪廓為基礎的陰影。 請參見下方的 Drop Shadow 範例。
食譜
重新定位動畫
利用 Composition Implicit Animations,開發者可以自動為元素的版面相對於其父元素的變化進行動畫效果。 例如,如果你更改下方按鈕的 邊距 ,它會自動動畫到新的版面位置。
實作概觀
- 取得目標元素的手冊 Visual
- 建立一個 ImplicitAnimationCollection ,自動對 Offset 屬性的變更進行動畫化。
- 將 ImplicitAnimationCollection 與 支援的 Visual 連結起來
<Button x:Name="RepositionTarget" Content="Click Me" />
public MainPage()
{
InitializeComponent();
InitializeRepositionAnimation(RepositionTarget);
}
private void InitializeRepositionAnimation(UIElement repositionTarget)
{
var targetVisual = ElementCompositionPreview.GetElementVisual(repositionTarget);
Compositor compositor = targetVisual.Compositor;
// Create an animation to animate targetVisual's Offset property to its final value
var repositionAnimation = compositor.CreateVector3KeyFrameAnimation();
repositionAnimation.Duration = TimeSpan.FromSeconds(0.66);
repositionAnimation.Target = "Offset";
repositionAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue");
// Run this animation when the Offset Property is changed
var repositionAnimations = compositor.CreateImplicitAnimationCollection();
repositionAnimations["Offset"] = repositionAnimation;
targetVisual.ImplicitAnimations = repositionAnimations;
}
陰影效果
將像素完美的投影應用到 UIElement 上,例如包含圖片的 橢圓。 由於陰影需要應用程式建立的 SpriteVisual ,我們需要建立一個「宿主」元素,並使用 ElementCompositionPreview.SetElementChildVisual 使其包含 SpriteVisual。
實作概觀
- 取得主機元素的視覺講義 Visual
- 建立 Microsoft.UI.Composition DropShadow
- 設定 DropShadow,使其透過遮罩從目標元素獲取形狀
- DropShadow 預設是矩形的,所以如果目標是矩形就不需要這個
- 將陰影附加到新的 SpriteVisual 上,並將 SpriteVisual 設為主機元素的子節點
- 使用 ExpressionAnimation 將 SpriteVisual 的大小綁定到主機的大小
<Grid Width="200" Height="200">
<Canvas x:Name="ShadowHost" />
<Ellipse x:Name="CircleImage">
<Ellipse.Fill>
<ImageBrush ImageSource="Assets/Images/2.jpg" Stretch="UniformToFill" />
</Ellipse.Fill>
</Ellipse>
</Grid>
public MainPage()
{
InitializeComponent();
InitializeDropShadow(ShadowHost, CircleImage);
}
private void InitializeDropShadow(UIElement shadowHost, Shape shadowTarget)
{
Visual hostVisual = ElementCompositionPreview.GetElementVisual(shadowHost);
Compositor compositor = hostVisual.Compositor;
// Create a drop shadow
var dropShadow = compositor.CreateDropShadow();
dropShadow.Color = Color.FromArgb(255, 75, 75, 80);
dropShadow.BlurRadius = 15.0f;
dropShadow.Offset = new Vector3(2.5f, 2.5f, 0.0f);
// Associate the shape of the shadow with the shape of the target element
dropShadow.Mask = shadowTarget.GetAlphaMask();
// Create a Visual to hold the shadow
var shadowVisual = compositor.CreateSpriteVisual();
shadowVisual.Shadow = dropShadow;
// Add the shadow as a child of the host in the visual tree
ElementCompositionPreview.SetElementChildVisual(shadowHost, shadowVisual);
// Make sure size of shadow host and shadow visual always stay in sync
var bindSizeAnimation = compositor.CreateExpressionAnimation("hostVisual.Size");
bindSizeAnimation.SetReferenceParameter("hostVisual", hostVisual);
shadowVisual.StartAnimation("Size", bindSizeAnimation);
}
以下列表展示了使用相同 XAML 結構的 C++/WinRT 先前 C# 程式碼的對應版本。
#include <winrt/Microsoft.UI.Composition.h>
#include <winrt/Microsoft.UI.Xaml.h>
#include <winrt/Microsoft.UI.Xaml.Hosting.h>
#include <winrt/Microsoft.UI.Xaml.Shapes.h>
...
MainPage()
{
InitializeComponent();
InitializeDropShadow(ShadowHost(), CircleImage());
}
int32_t MyProperty();
void MyProperty(int32_t value);
void InitializeDropShadow(Microsoft::UI::Xaml::UIElement const& shadowHost, Microsoft::UI::Xaml::Shapes::Shape const& shadowTarget)
{
auto hostVisual{ Microsoft::UI::Xaml::Hosting::ElementCompositionPreview::GetElementVisual(shadowHost) };
auto compositor{ hostVisual.Compositor() };
// Create a drop shadow
auto dropShadow{ compositor.CreateDropShadow() };
dropShadow.Color(Microsoft::UI::ColorHelper::FromArgb(255, 75, 75, 80));
dropShadow.BlurRadius(15.0f);
dropShadow.Offset(Windows::Foundation::Numerics::float3{ 2.5f, 2.5f, 0.0f });
// Associate the shape of the shadow with the shape of the target element
dropShadow.Mask(shadowTarget.GetAlphaMask());
// Create a Visual to hold the shadow
auto shadowVisual = compositor.CreateSpriteVisual();
shadowVisual.Shadow(dropShadow);
// Add the shadow as a child of the host in the visual tree
Microsoft::UI::Xaml::Hosting::ElementCompositionPreview::SetElementChildVisual(shadowHost, shadowVisual);
// Make sure size of shadow host and shadow visual always stay in sync
auto bindSizeAnimation{ compositor.CreateExpressionAnimation(L"hostVisual.Size") };
bindSizeAnimation.SetReferenceParameter(L"hostVisual", hostVisual);
shadowVisual.StartAnimation(L"Size", bindSizeAnimation);
}
磨砂玻璃
創造一種能模糊和調色背景內容的效果。 請注意,開發者需要安裝 Win2D NuGet 套件才能使用效果。 請參閱 Win2D 首頁 的安裝說明。
實作概觀
- 取得主機元素的視覺講義
- 使用 Win2D 和 CompositionEffectSourceParameter 建立模糊效果樹
- 根據效果樹建立一個 CompositionEffectBrush
- 將 CompositionEffectBrush 的輸入設定為 CompositionBackdropBrush,這樣就能將效果套用到 SpriteVisual 後方的內容
- 將 CompositionEffectBrush 設定為新 SpriteVisual 的內容,並將 SpriteVisual 設為主機元素的子節點。 你可以選擇用 XamlCompositionBrushBase 作為替代方案。
- 使用 ExpressionAnimation 將 SpriteVisual 的大小綁定到主機的大小
<Grid Width="300" Height="300" Grid.Column="1">
<Image
Source="Assets/Images/2.jpg"
Width="200"
Height="200" />
<Canvas
x:Name="GlassHost"
Width="150"
Height="300"
HorizontalAlignment="Right" />
</Grid>
public MainPage()
{
InitializeComponent();
InitializeFrostedGlass(GlassHost);
}
private void InitializeFrostedGlass(UIElement glassHost)
{
Visual hostVisual = ElementCompositionPreview.GetElementVisual(glassHost);
Compositor compositor = hostVisual.Compositor;
// Create a glass effect, requires Win2D NuGet package
var glassEffect = new GaussianBlurEffect
{
BlurAmount = 15.0f,
BorderMode = EffectBorderMode.Hard,
Source = new ArithmeticCompositeEffect
{
MultiplyAmount = 0,
Source1Amount = 0.5f,
Source2Amount = 0.5f,
Source1 = new CompositionEffectSourceParameter("backdropBrush"),
Source2 = new ColorSourceEffect
{
Color = Color.FromArgb(255, 245, 245, 245)
}
}
};
// Create an instance of the effect and set its source to a CompositionBackdropBrush
var effectFactory = compositor.CreateEffectFactory(glassEffect);
var backdropBrush = compositor.CreateBackdropBrush();
var effectBrush = effectFactory.CreateBrush();
effectBrush.SetSourceParameter("backdropBrush", backdropBrush);
// Create a Visual to contain the frosted glass effect
var glassVisual = compositor.CreateSpriteVisual();
glassVisual.Brush = effectBrush;
// Add the blur as a child of the host in the visual tree
ElementCompositionPreview.SetElementChildVisual(glassHost, glassVisual);
// Make sure size of glass host and glass visual always stay in sync
var bindSizeAnimation = compositor.CreateExpressionAnimation("hostVisual.Size");
bindSizeAnimation.SetReferenceParameter("hostVisual", hostVisual);
glassVisual.StartAnimation("Size", bindSizeAnimation);
}