大多数使用视觉层功能的应用都将使用 XAML 来定义主要 UI 内容。 在 Windows 10 周年更新中,XAML 框架和视觉层中新增了一些新功能,可更轻松地组合这两种技术来创建令人惊叹的用户体验。 XAML 和视觉层互作功能可用于创建不能单独使用 XAML API 的高级动画和效果。 这包括:
- 画笔效果,如模糊和霜冻玻璃
- 动态照明效果
- 滚动驱动的动画和视差
- 自动布局动画
- 像素完美阴影
这些效果和动画可以应用于现有 XAML 内容,因此无需大幅调整 XAML 应用以利用新功能。 下面的“食谱”部分介绍了布局动画、阴影和模糊效果。 有关实现视差的代码示例,请参阅 ParallaxingListItems 示例。 WindowsCompositionSamples 存储库还包含用于实现动画、阴影和效果的其他几个示例。
XamlCompositionBrushBase 类
XamlCompositionBrush 为 XAML 画笔提供基类,该基类使用 CompositionBrush绘制区域。 这可用于轻松地将合成效果(如模糊或霜冻玻璃)应用于 XAML UI 元素。
有关将画笔与 XAML UI 配合使用的详细信息,请参阅 “画笔 ”部分。
有关代码示例,请参阅 XamlCompositionBrushBase 的参考页。
XamlLight 类
XamlLight 为 XAML 照明效果提供一个基类,该基类可使用 CompositionLight动态照亮一个区域。
有关使用灯光的详细信息,请参阅 “照明 ”部分,包括照明 XAML UI 元素。
有关代码示例,请参阅 XamlLight 的参考页。
ElementCompositionPreview 类
ElementCompositionPreview 是一个静态类,提供 XAML 和 Visual Layer 互作功能。 有关视觉层及其功能的概述,请参阅 视觉层。 ElementCompositionPreview 类提供以下方法:
- GetElementVisual:获取用于呈现此元素的“讲义”视觉对象
- SetElementChildVisual:将“handin”视觉对象设置为此元素的视觉树的最后一个子节点。 此视觉对象将覆盖在其他元素之上。
- GetElementChildVisual:使用 SetElementChildVisual 检索视觉对象集
- GetScrollViewerManipulationPropertySet:获取一个对象,该对象可用于在 ScrollViewer 中基于滚动偏移量创建 60帧每秒的动画。
关于 ElementCompositionPreview.GetElementVisual 的备注
ElementCompositionPreview.GetElementVisual 返回一个“输出”视觉对象,该对象用于呈现给定的 UIElement。 Visual.Opacity、Visual.Offset 和 Visual.Size 等属性由 XAML 框架根据 UIElement 的状态设置。 这可实现隐式重新定位动画等技术(请参阅 示例)。
请注意,由于 偏移量 和 大小 设置为 XAML 框架布局的结果,因此在修改或设置这些属性动画时,开发人员应小心。 当元素的左上角与父元素在布局中具有相同的位置时,开发人员才应修改或设置 Offset 动画。 通常不应修改大小,但访问该属性可能会有用。 例如,下面的 Drop Shadow 和 Frosted Glass 示例使用讲义视觉对象的大小作为动画的输入。
作为其他注意事项,讲义视觉对象的更新属性不会反映在相应的 UIElement 中。 例如,将 UIElement.Opacity 设置为 0.5 会将对应的讲义可视对象的透明度设置为 0.5。 但是,将讲义视觉对象的 不透明度 设置为 0.5 将导致内容以 50% 不透明度显示,但不会更改相应 UIElement 的 Opacity 属性的值。
偏移 动画示例
不正确
<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 方法
ElementCompositionPreview.SetElementChildVisual 允许开发人员提供“handin”视觉对象,该视觉对象将作为元素可视化树的一部分显示。 这样,开发人员就可以创建一个“合成岛”,其中基于视觉的内容可以显示在 XAML UI 中。 开发人员应该保守地使用此技术,因为基于视觉对象的内容不会具有相同的 XAML 内容的辅助功能和用户体验保证。 因此,通常建议仅在必要时使用此技术来实现自定义效果,如下面的“食谱”部分中找到的效果。
GetAlphaMask 方法
Image、TextBlock,Shape 每个实现 一种称为 GetAlphaMask 的方法,该方法返回一个 CompositionBrush,该方法表示具有元素形状的灰度图像。 此 CompositionBrush 可用作 Composition DropShadow的输入,因此阴影可以反映元素的形状,而非矩形。 这为文本、带有 alpha 和形状的图像启用像素完美、基于轮廓的阴影。 有关此 API 的示例,请参阅下面的 阴影。
食谱
重新定位动画
使用合成隐式动画,开发人员可以自动为元素相对于其父元素的布局变化创建动画。 例如,如果更改以下按钮的 边距,它将自动过渡到其新布局位置。
实现概述
- 获取目标元素的讲义 Visual
- 创建一个 ImplicitAnimationCollection,以自动在 Offset 属性中实现更改的动画处理。
- 将 ImplicitAnimationCollection 与后台视觉对象相关联
<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
- 创建 Windows.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# 代码的 C++/WinRT 和 C++/CX 等效项。
#include <winrt/Windows.UI.Composition.h>
#include <winrt/Windows.UI.Xaml.h>
#include <winrt/Windows.UI.Xaml.Hosting.h>
#include <winrt/Windows.UI.Xaml.Shapes.h>
...
MainPage()
{
InitializeComponent();
InitializeDropShadow(ShadowHost(), CircleImage());
}
int32_t MyProperty();
void MyProperty(int32_t value);
void InitializeDropShadow(Windows::UI::Xaml::UIElement const& shadowHost, Windows::UI::Xaml::Shapes::Shape const& shadowTarget)
{
auto hostVisual{ Windows::UI::Xaml::Hosting::ElementCompositionPreview::GetElementVisual(shadowHost) };
auto compositor{ hostVisual.Compositor() };
// Create a drop shadow
auto dropShadow{ compositor.CreateDropShadow() };
dropShadow.Color(Windows::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
Windows::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);
}
#include "WindowsNumerics.h"
MainPage::MainPage()
{
InitializeComponent();
InitializeDropShadow(ShadowHost, CircleImage);
}
void MainPage::InitializeDropShadow(Windows::UI::Xaml::UIElement^ shadowHost, Windows::UI::Xaml::Shapes::Shape^ shadowTarget)
{
auto hostVisual = Windows::UI::Xaml::Hosting::ElementCompositionPreview::GetElementVisual(shadowHost);
auto compositor = hostVisual->Compositor;
// Create a drop shadow
auto dropShadow = compositor->CreateDropShadow();
dropShadow->Color = Windows::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
Windows::UI::Xaml::Hosting::ElementCompositionPreview::SetElementChildVisual(shadowHost, shadowVisual);
// Make sure size of shadow host and shadow visual always stay in sync
auto bindSizeAnimation = compositor->CreateExpressionAnimation("hostVisual.Size");
bindSizeAnimation->SetReferenceParameter("hostVisual", hostVisual);
shadowVisual->StartAnimation("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);
}