zoukankan      html  css  js  c++  java
  • [UWP]缓存Lottie动画帧

    在上一篇博文《[UWP]在UWP平台中使用Lottie动画》中我简单介绍了一下LottieUWP项目以及如何使用它呈现Lottie动画,这篇文章里我们来讲点进阶的东西——缓存Lottie动画帧。

    为什么会有这样的需求呢?

    有两方面原因:

    • 直接在XAML中使用Lottie动画时,是边播放边渲染,计算量比较大,某些Lottie文件会非常吃性能!另外也会存在渲染不正确(有黑色区域)的情况,但是如果我们把每一帧缓存下来,自己控制播放的话,性能会提升很多!
    • 应用于视频合成时(给视频添加Lottie动画挂件),需要获取每一时刻的动画帧图像(UWP媒体编辑以及视频合成的相关知识也很多,有时间我会整理一下,分享点干货)。

    获取Lottie动画帧

    在上一篇中我们使用了LottieAnimationView控件来播放Lottie动画,其实另一个类LottieDrawable也可以完成同样的工作,并且更易扩展。

    下面我们就来修改下LottieDrawable类,让它可以返回给我们某一时刻的帧图像。

    LottieDrawable类中,Lottie动画的播放进度由Progress属性控制,而实际上的呈现则是使用了Win2D中的CanvasAnimatedControl控件来承载绘制目标。

    这样的话,其实我们要做的就很简单了。我们可以新增一个GetCurrentFrame方法,使用CanvasRenderTarget作为绘制目标,将CanvasAnimatedControl的Draw事件中的绘制逻辑转移过来即可。

    具体代码如下:

            /// <summary>
            /// 获取当前进度时的Lottie图像
            /// </summary>
            /// <param name="resourceCreator"></param>
            /// <param name="scaleX">横向缩放倍数</param>
            /// <param name="scaleY">纵向缩放倍数</param>
            /// <returns></returns>
            public CanvasBitmap GetCurrentFrame(ICanvasResourceCreator resourceCreator, float scaleX, float scaleY)
            {
                lock (this)
                {
                    var width = _composition.Bounds.Width * scaleX;
                    var height = _composition.Bounds.Height * scaleY;
                    var commandList = new CanvasRenderTarget(resourceCreator, (float)width, (float)height, 96f);
                    using (var session = commandList.CreateDrawingSession())
                    {
    
                        if (_bitmapCanvas == null || _bitmapCanvas.Width < width || _bitmapCanvas.Height < height)
                        {
                            _bitmapCanvas?.Dispose();
                            _bitmapCanvas = new BitmapCanvas(width, height);
                        }
    
                        using (_bitmapCanvas.CreateSession(resourceCreator.Device, (float)width,
                            (float)height, session))
                        {
                            _bitmapCanvas.Clear(Colors.Transparent);
                            LottieLog.BeginSection("Drawable.Draw");
                            if (_compositionLayer == null)
                            {
                                return null;
                            }
    
                            _matrix.Reset();
                            _matrix = MatrixExt.PreScale(_matrix, scaleX, scaleY);
                            _compositionLayer.Draw(_bitmapCanvas, _matrix, _alpha);
                            LottieLog.EndSection("Drawable.Draw");
                        }
    
                    }
    
                    return commandList;
                }
            }
    

    有一点要注意的是这里的绘制目标使用了CanvasRenderTarget,切勿使用CanvasCommandList,区别在于CanvasRenderTarget有固定大小的尺寸,而CanvasCommandList则没有固定的尺寸(实际上时无限大的),使用CanvasCommandList作为绘制目标将会引起某些Lottie动画绘制时丢失部分内容,具体可参见我在LottieUWP项目上提的这个Issue

    缓存Lottie动画帧

    有了上面添加的GetCurrentFrame方法后,我们就可以通过修改Progress来获取Lottie动画中每一时刻的帧图像了。

    我编写了一个缓存Lottie动画帧的方法:

            protected List<CanvasBitmap> CacheLottieFrames;
            /// <summary>
            /// 缓存Lottie动画帧
            /// </summary>
            /// <param name="width">缓存图像的宽</param>
            /// <param name="height">缓存图像的高</param>
            /// <param name="frameRate">缓存的帧率</param>
            /// <returns></returns>
            private async Task InitLottieFrame(double width, double height, int frameRate)
            {
                await Task.Run(() =>
                {
                    lock (_lockObj)
                    {
                        if (lottieDrawable != null)
                        {
                            var duration = lottieDrawable.Composition.Duration;
                            var scaleX = width / lottieDrawable.Composition.Bounds.Width;
                            var scaleY = height / lottieDrawable.Composition.Bounds.Height;
                            var timeGap = 1d / frameRate;
                            CacheLottieFrames = new List<CanvasBitmap>();
                            var device = CanvasDevice.GetSharedDevice();
                            for (var i = 0d; i < duration; i += timeGap)
                            {
                                lottieDrawable.Progress = (float)(i / duration);
                                var renderTarget =
                                    new CanvasRenderTarget(device, (float)CanvasWidth, (float)CanvasHeight, 96f);
                                using (var session = renderTarget.CreateDrawingSession())
                                {
                                    session.Clear(Colors.Transparent);
                                    var effectImg = lottieDrawable.GetCurrentFrame(device, (float)scaleX, (float)scaleY);
                                    if (effectImg != null)
                                        session.DrawImage(effectImg);
                                }
    
                                CacheLottieFrames.Add(renderTarget);
                            }
                        }
                    }
    
                });
            }
    

    我们也可以在LottieDrawable.Composition中获取到帧的总数量以及帧率,以此为依据来获取帧,但是我在这个方法里没有使用,因为我想依据传入的帧率来控制获取的帧数量,避免缓存多余的帧占据内存空间。

    结尾

    有关于UWP使用Lottie动画的相关博文到这里就结束了,这段时间接触下来,我的感受是Lottie动画真的挺好玩效果也很棒。在LottieFiles网站大家可以找到各种有趣好玩的Lottie动画,当然有能力的也可以自己制作。

    本篇博客到此结束!谢谢大家阅读!

  • 相关阅读:
    ubuntu下安装maven
    159.Longest Substring with At Most Two Distinct Characters
    156.Binary Tree Upside Down
    155.Min Stack
    154.Find Minimum in Rotated Sorted Array II
    153.Find Minimum in Rotated Sorted Array
    152.Maximum Product Subarray
    151.Reverse Words in a String
    150.Evaluate Reverse Polish Notation
    149.Max Points on a Line
  • 原文地址:https://www.cnblogs.com/hhchaos/p/10196788.html
Copyright © 2011-2022 走看看