• [UWP]使用GetAlphaMask制作阴影


    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

  • 相关阅读:
    背景图片自适应大小(平铺)
    墨卡托投影示意图
    C# 两个类的实例之间相同属性的值的复制
    C# 并行编程 Task
    C# 并行编程 PLINQ
    C# 并行编程 Parallel
    仰望星空
    Ubuntu的人道精神
    神经网络简介
    并行计算简介
  • 原文地址:https://www.cnblogs.com/dino623/p/create_dropshadow_using_GetAlphaMask.html
Copyright © 2020-2023  润新知