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

原文: [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),布局如下:

 
 
Copy
<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的可视化层:

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

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

 
 
Copy
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像素的圆角你不说我不说又有谁会知道呢。

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

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

3. 淡出的阴影#

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

 
 
Copy
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

猜你喜欢

转载自www.cnblogs.com/lonelyxmas/p/11791566.html