• [UWP]使用GetAlphaMask和ContainerVisual制作长阴影(Long Shadow)


    1. 什么是长阴影

    前几年扁平化设计(Flat Design)十分流行,后来在扁平化的基础上又流行起了长阴影(Long Shadow)。长阴影其实就是扩展了对象的投影,感觉是一种光线照射下的影子,通常采用角度为 45 度的投影,给对象添加了一份立体感。长阴影快速发展为流行的设计趋势,并经常被应用到扁平设计方案的对象。它很适合用在较小的元素上,一时之间几乎应用的图标都加上了长阴影。(不过现在又不流行了)

    2. 使用GetAlphaMask和ContainerVisual制作长阴影

    要在UWP中制作长阴影的话,可以用GetAlphaMask拿到轮廓,做成灰色,然后复制一百几十个摆在后面。不过当然并不是直接创建这么多个UIElement,而是使用ContainerVisual类实现这个功能。ContainerVisual用于组合子级的Visual,只需简单地调用VisualCollection.InsertAtBottom即可。

    下面是具体的实现例子,和上篇文章介绍的一样,首先在需要阴影的元素(TitleElement)后面放一个用于显示阴影的UIElement(ShadowElement),布局如下:

    <Grid Background="#FFE87A69" Padding="40" Margin="50" x:Name="ShadowRoot">
        <Rectangle x:Name="ShadowElement" />
        <TextBlock Text="One Pomodoro" Foreground="#FFfee8da"  FontSize="64" x:Name="TitleElement"/>
    </Grid>
    

    然后在代码里创建一个ContainerVisual并使用ElementCompositionPreview.SetElementChildVisual将这个ContainerVisual设置到ShadowElement的可视化层:

    var textVisual = ElementCompositionPreview.GetElementVisual(TitleElement);
    compositor = textVisual.Compositor;
    containerVisual = compositor.CreateContainerVisual();
    ElementCompositionPreview.SetElementChildVisual(ShadowElement, containerVisual);
    

    之后调用TitleElement的GetAlphaMask获取轮廓,然后将这个轮廓作为Mask设置到CompositionMaskBrush的Mask属性,重复一百次:

    var mask = TitleElement.GetAlphaMask();
    
    var shadowColor = Color.FromArgb(255, 160, 59, 49);
    int depth = 100;
    for (int i = 0; i < depth; i++)
    {
        //创建Brush
        var maskBrush = compositor.CreateMaskBrush();
        maskBrush.Mask = mask;
        maskBrush.Source = compositor.CreateColorBrush(shadowColor);
    
        //创建Visual
        var visual = compositor.CreateSpriteVisual();
        visual.Brush = maskBrush;
        visual.Offset = new Vector3(i + 1, i + 1, 0);
    
        //将Visual添加到ContainerVisual
        containerVisual.Children.InsertAtBottom(visual);
    
        //同步Visual和TextVisual的尺寸
        var bindSizeAnimation = compositor.CreateExpressionAnimation("textVisual.Size");
        bindSizeAnimation.SetReferenceParameter("textVisual", textVisual);
        visual.StartAnimation("Size", bindSizeAnimation);
    }
    

    然后终于……

    ……还是没完成,还需要为ContainerVisual设置Clip以免阴影越界。看起来需要写很多代码去实现,但其实有个简单的方法,将外面那层的ShadowRoot的CornerRadius设置为1,Grid就会自动自觉把超出范围的内容裁剪掉。虽然不知道具体原理,但有需要的话其它情况也可以偷懒这样做,也许有人不喜欢圆角,但区区1像素的圆角你不说我不说又有谁会知道呢。

    Grid Background="#FFE87A69" Padding="40" Margin="50" x:Name="ShadowRoot" CornerRadius="1">
    

    这样一个漂亮的长阴影就完成了。

    3. 淡出的阴影

    要实现淡出的阴影原理也很简单,别想太复杂,就只是准备好多个颜色渐渐改变的Visual,插进去ContainerVisual里就完成了。代码及效果如下(顺便一提这种情况下Vector3真好用):

    int depth = 60;
    float opacity = 0.3f;
    Vector3 background = new Vector3(232, 122, 105);
    
    
    var maskBrush = compositor.CreateMaskBrush();
    maskBrush.Mask = mask;
    
    //计算阴影的颜色
    Vector3 shadowColor = background - (background - new Vector3(0, 0, 0)) * opacity;
    shadowColor = Vector3.Max(Vector3.Zero, shadowColor);
    shadowColor += (background - shadowColor) * i / depth;
    
    maskBrush.Source = compositor.CreateColorBrush(Color.FromArgb(255, (byte)shadowColor.X, (byte)shadowColor.Y, (byte)shadowColor.Z));
    

    4. 实际应用

    就只是一个静态的长阴影的话,那还不如用PS做成图片在放进UWP里。GetAlphaMask另一个好处是它并不仅获取静态的轮廓,而是一直和源头同步。这样可玩性就大多了,例如我把长阴影的设计元素添加到番茄钟里面,成果如下:

    5. 结语

    虽然长阴影已经有点过时了,但自己做起来还是觉得很酷很酷。刚开始还担心这样搞性能会很差,实际运行起来后发觉超级OK。上面的动画可以安装我的番茄钟应用试玩一下,安装地址:

    一个番茄钟

    6. 参考

    [ContainerVisual Class (Windows.UI.Composition) - Windows UWP applications Microsoft Docs]((https://docs.microsoft.com/en-us/uwp/api/windows.ui.composition.containervisual)

    VisualCollection Class (Windows.UI.Composition) - Windows UWP applications Microsoft Docs

    Vector3 Struct (Windows.Foundation.Numerics) - Windows UWP applications Microsoft Docs

    7. 源码

    OnePomodoro_LongShadow.xaml.cs at master

  • 相关阅读:
    pytest+allure详情版
    【Django】django.core.exceptions.ImproperlyConfigured: mysqlclient 1.4.0 or newer is required
    Docker-Portainer
    滑动解锁和截图
    调用JavaScript(浏览器滚动条)
    WebDriver操作cookie
    下载文件
    多表单切换
    利用parameterized模块进行unittest参数化
    关于软件测试必备的技能
  • 原文地址:https://www.cnblogs.com/dino623/p/create_longshadow_using_GetAlphaMask_and_ContainerVisual.html
Copyright © 2020-2023  润新知