[UWP]使用GetAlphaMask制作阴影

  • 2019 年 10 月 30 日
  • 筆記

1. 前言

最近常常接触到GetAlphaMask,所以想写这篇文章介绍下GetAlphaMask怎么使用。其实GetAlphaMask的使用场景十分有限,Github上能搜到的内容都是用来配合DropShadow的,所以这篇文章也以介绍DropShadow为主。

2. 合成阴影

先介绍一下合成阴影。Compositor.CreateDropShadow()可以创建一个DropShadow,将这个DropShadowDropShadow赋值到SpriteVisual的Shadow属性,然后使用ElementCompositionPreview.SetElementChildVisual 将这个SpriteVisual设置到某个UIElement的可视化层里,再将这个UIElement放到需要阴影的元素后面,这样基本的合成阴影就完成了。

具体代码如下:

<Grid VerticalAlignment="Center"        HorizontalAlignment="Center">      <Grid x:Name="BackgroundGrid"/>      <Grid Background="Turquoise"            x:Name="Host">          <TextBlock Text="I need shadow"                     Foreground="White"                     HorizontalAlignment="Center"                     VerticalAlignment="Center"                     FontSize="36"                     Margin="16"/>      </Grid>  </Grid>
private readonly Compositor _compositor;  private readonly SpriteVisual _backgroundVisual;  private readonly DropShadow _dropShadow;  public MainPage() : base()  {      InitializeComponent();      _compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;        //创建并配置DropShadow      _dropShadow = _compositor.CreateDropShadow();      _dropShadow.BlurRadius = 16;      _dropShadow.Offset = new Vector3(8);        //创建SpriteVisual并设置Shadow      _backgroundVisual = _compositor.CreateSpriteVisual();      _backgroundVisual.Shadow = _dropShadow;        //将SpriteVisual放到可视化树      ElementCompositionPreview.SetElementChildVisual(BackgroundGrid, _backgroundVisual);        Host.SizeChanged += OnHostSizeChanged;  }    private void OnHostSizeChanged(object sender, SizeChangedEventArgs e)  {      Vector2 newSize = new Vector2(0, 0);      Vector3 centerPoint = new Vector3(0, 0, 0);      if (Host != null)      {          newSize = new Vector2((float)Host.ActualWidth, (float)Host.ActualHeight);          centerPoint = new Vector3((float)Host.ActualWidth / 2, (float)Host.ActualHeight / 2, 0);      }      _backgroundVisual.CenterPoint = centerPoint;      _backgroundVisual.Size = newSize;  }

3. 使用GetAlphaMask裁剪阴影

上面的代码需要可以实现阴影,但只能实现矩形的阴影,在WPF和Silverlight中常用的Shape的阴影,或者文字的阴影都做不出来。

例如将XAML改成这样的话,结果绝不是我想要的东西:

<Grid VerticalAlignment="Center"        HorizontalAlignment="Center">      <Grid x:Name="BackgroundGrid"/>      <TextBlock Text="I need shadow"                     x:Name="Host"                     Foreground="Turquoise"                     HorizontalAlignment="Center"                     VerticalAlignment="Center"                     FontSize="36"/>  </Grid>

这时候就需要用到GetAlphaMask这个函数。

Image、 TextBlock和Shape分别实现一个名为GetAlphaMask的方法, 该方法返回一个CompositionBrush , 该方法表示具有元素形状的灰度图像。

官当文档 中是这样描述GetAlphaMask函数的,简单来说就是拿到一个Image、TextBlock或Shape的轮廓,这个轮廓可以作为DropShadow.Mask的值,这样DropShadow的形状就可调用GetAlphaMask的元素的形状一样了。

具体代码和结果如下,这才是我想要的效果:

_dropShadow.Mask = Host.GetAlphaMask();

4. 使用DropShadowPanel

如果觉得自己写代码太过复杂, 可以使用Toolkit中的DropShadowPanel

DropShadowPanel继承自ContentControl,当它的Cotnent为Shape、TextBlock、Image之一(或Toolkit中实现了GetAlphaMask的其它控件)时,它就调用GetAlphaMask获取阴影的形状,否则就是简单的举行,代码如下:

CompositionBrush mask = null;    if (Content is Image)  {      mask = ((Image)Content).GetAlphaMask();  }  else if (Content is Shape)  {      mask = ((Shape)Content).GetAlphaMask();  }  else if (Content is TextBlock)  {      mask = ((TextBlock)Content).GetAlphaMask();  }  else if (Content is ImageExBase imageExBase)  {      imageExBase.ImageExInitialized += ImageExInitialized;        if (imageExBase.IsInitialized)      {          imageExBase.ImageExInitialized -= ImageExInitialized;            mask = ((ImageExBase)Content).GetAlphaMask();      }  }    _dropShadow.Mask = mask;

之后它的做法和上面介绍的一样,把这个阴影设置到一个元素放在ContentPresenter后面,看起来就实现了Content的阴影:

_border = GetTemplateChild(PartShadow) as Border;    if (_border != null)  {      ElementCompositionPreview.SetElementChildVisual(_border, _shadowVisual);  }
<Grid Background="{TemplateBinding Background}"        BorderBrush="{TemplateBinding BorderBrush}"        BorderThickness="{TemplateBinding BorderThickness}"        HorizontalAlignment="{TemplateBinding HorizontalAlignment}"        VerticalAlignment="{TemplateBinding VerticalAlignment}">      <Border x:Name="ShadowElement"              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"              VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />      <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"                        VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>  </Grid>

5. 参考

将可视化层与 XAML 结合使用 – Windows UWP applications Microsoft Docs

Shape.GetAlphaMask Method (Windows.UI.Xaml.Shapes) – Windows UWP applications Microsoft Docs

DropShadow.Mask Property (Windows.UI.Composition) – Windows UWP applications Microsoft Docs

WindowsCommunityToolkit_Microsoft.Toolkit.Uwp.UI.Controls_DropShadowPanel at master

DropShadowPanel XAML Control – Windows Community Toolkit Microsoft Docs