zoukankan      html  css  js  c++  java
  • [UWP]用Win2D和CompositionAPI实现文字的发光效果,并制作动画

    1. 成果

    献祭了周末的晚上,成功召唤出了上面的番茄钟。正当我在感慨“不愧是Shadow大人,这难道就是传说中的五彩斑斓的黑?”

    “那才不是什么阴影效果,那是发光效果。”被路过的老婆吐槽了。

    系系系,老婆说的都系对的。我还以为我在做阴影动画,现在只好改博客标题了?

    要实现上面的动画效果,首先使用CompositionDrawingSurface,在它上面用DrawTextLayout画出文字,然后用GaussianBlurEffect模仿成阴影,然后用CanvasActiveLayer裁剪文字的轮廓,然后用这个CompositionDrawingSurface创建出CompositionSurfaceBrush,然后创建一个CompositionMaskBrush,将CompositionSurfaceBrush作为它的Mask,然后用CompositionLinearGradientBrush创建出渐变,再用BlendEffect将它变成四向渐变,再用ColorKeyFrameAnimation和ScalarKeyFrameAnimation在它上面做动画并把它作为CompositionMaskBrush的Source,然后创建SpriteVisual将CompositionMaskBrush应用上去,然后使用两个PointLight分别从左到右和从右到左照射这个SpriteVisual,再创建一个AmbientLight模仿呼吸灯。

    仔细想想……好吧,老婆说得对,我还真的没有用到任何Shadow的Api,这里和Shadow大人半毛钱关系都没有。

    这个番茄钟源码可以在这里查看:

    OnePomodoro_ShadowTextView.xaml at master

    也可以安装我的番茄钟应用试玩一下,安装地址:

    一个番茄钟

    这篇文章将介绍其中几个关键技术。

    2. 使用GaussianBlurEffect模仿阴影

    上一篇文章已经介绍过怎么在CompositionDrawingSurface上写字,这里就不再重复。为了可以为文字添加阴影,需要用到CanvasRenderTargetGaussianBlurEffect

    CanvasRenderTarget是一个可以用来画图的渲染目标。实现文字阴影的步骤如下:将文字画到CanvasRenderTarget,然后用它作为GaussianBlurEffect.Source产生一张高斯模糊的图片,这样看上去就和文字的阴影一样。然后再在这张模糊的图片的前面画上原本的文字。

    代码如下所示:

    using (var session = CanvasComposition.CreateDrawingSession(drawingSurface))
    {
        session.Clear(Colors.Transparent);
        using (var textLayout = new CanvasTextLayout(session, Text, textFormat, width, height))
        {
            var bitmap = new CanvasRenderTarget(session, width, height);
            using (var bitmapSession = bitmap.CreateDrawingSession())
            {
                bitmapSession.DrawTextLayout(textLayout, 0, 0, FontColor);
            }
            var blur = new GaussianBlurEffect
            {
                BlurAmount = (float)BlurAmount,
                Source = bitmap,
                BorderMode = EffectBorderMode.Hard
            };
    
            session.DrawImage(blur, 0, 0);
            session.DrawTextLayout(textLayout, 0, 0, FontColor);
        }
    }
    

    效果如下(因为我用了白色字体,这时候已经不怎么像阴影了):

    关于CavasRenderTaget,死鱼的这篇文章有详细介绍。他的这个专栏的文章都很有趣。

    3. 使用CanvasActiveLayer裁剪文字

    关于裁剪文字,有几件事需要做。

    首先获取需要裁剪的文字的轮廓,这使用上一篇文章介绍过的CanvasGeometry.CreateText就可以了,这个函数的返回值是一个CanvasGeometry。然后使用CanvasGeometry.CreateRectangle获取整个画布的CanvasGeometry,将他们用CombineWith相减得出文字以外的部分,具体代码如下:

    var fullSizeGeometry = CanvasGeometry.CreateRectangle(session, 0, 0, width, height);
    var textGeometry = CanvasGeometry.CreateText(textLayout);
    var finalGeometry = fullSizeGeometry.CombineWith(textGeometry, Matrix3x2.Identity, CanvasGeometryCombine.Exclude);
    

    这里之所以不直接使用textGeometry,是因为我们并不是真的裁剪出文字的部分,而是像WPF的OpacityMask那样用透明度控制显示的部分。CanvasActiveLayer就是用来实现这个功能。CanvasDrawingSession.CreateLayer函数使用透明度和CanvasGeometry创建一个CanvasActiveLayer,在创建Layer后CanvasDrawingSession的操作都会应用这个透明度,直到Layer关闭。

    using (var layer = session.CreateLayer(1, finalGeometry))
    {
        //DrawSth
    }
    

    最后效果如下:

    关于CanvasActiveLayer的更多用法, 可以参考Lindexi的这篇文章

    4. 制作有复杂颜色的阴影

    如上图所示,UWP中的DropShadow的Color只能有一种颜色,所以DropShadow不能使用复杂的颜色。这时候就要用到CompositionMaskBrush,CompositionMaskBrush有两个主要属性:Mask和Source。其中Mask是一个CompositionBrush类型的属性,它指定不透明的蒙板源。简单来说,CompositionMaskBrush的形状就是它的Mask的形状。而Source属性则是它的颜色,这个属性可以是 CompositionColorBrush、CompositionLinearGradientBrush、CompositionSurfaceBrush、CompositionEffectBrush 或 CompositionNineGridBrush 类型的任何 CompositionBrush。可以使用前面创建的CompositionDrawingSurface创建出CompositionSurfaceBrush,最后创建一个CompositionMaskBrush,将CompositionSurfaceBrush作为它的Mask。

    var maskBrush = Compositor.CreateMaskBrush();
    maskBrush.Mask = Compositor.CreateSurfaceBrush(DrawingSurface);
    maskBrush.Source = Compositor.CreateLinearGradientBrush();
    

    本来还想做到大紫大红的,但被吐槽和本来低调内敛的目的不符合,所以复用了以前这篇文章的配色,CompositionLinearGradientBrush加BlendEffect做成了有些复杂的配色(但实际上太暗了看不出来):

    这时候效果如下:

    5. 使用PointLight和AmbientLight制作动画

    我在使用PointLight并实现动画效果这篇文章里介绍了PointLight的用法及基本动画,这次豪华些,同时有从左到右的红光以及从右到左的蓝光,这两个PointLight的动画效果大致是这样:

    因为PointLight最多只能叠加两个,所以再使用AmbientLight并对它的Intensity属性做动画,这样动画就会变得复杂些,最终实现了文章开头的动画。

    var compositor = Window.Current.Compositor;
    var ambientLight = compositor.CreateAmbientLight();
    ambientLight.Intensity = 0;
    ambientLight.Color = Colors.White;
    
    var intensityAnimation = compositor.CreateScalarKeyFrameAnimation();
    intensityAnimation.InsertKeyFrame(0.2f, 0, compositor.CreateLinearEasingFunction());
    intensityAnimation.InsertKeyFrame(0.5f, 0.20f, compositor.CreateLinearEasingFunction());
    intensityAnimation.InsertKeyFrame(0.8f, 0, compositor.CreateLinearEasingFunction());
    intensityAnimation.Duration = TimeSpan.FromSeconds(10);
    intensityAnimation.IterationBehavior = AnimationIterationBehavior.Forever;
    
    ambientLight.StartAnimation(nameof(AmbientLight.Intensity), intensityAnimation);
    

    6. 参考

    CanvasRenderTarget Class

    CanvasGeometry Class

    CanvasActiveLayer Class

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

    组合照明 - Windows UWP applications Microsoft Docs

    Win2D - 知乎

  • 相关阅读:
    Github markdown页面内跳转
    github gist 无法访问
    Install pyaudio on Ubuntu
    删除链表的倒数第N个节点
    电话号码的字母组合
    最长公共前缀
    盛最多水的容器
    字符串转化整数与回文数
    Z 字形变换
    LeetCode1-5题
  • 原文地址:https://www.cnblogs.com/dino623/p/using_win2d_and_composition_API_to_implement_the_glow_effect_of_text_and_make_animation.html
Copyright © 2011-2022 走看看