[UWP]缓存Lottie动画帧

原文:[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动画,当然有能力的也可以自己制作。

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

原文地址:https://www.cnblogs.com/lonelyxmas/p/10199191.html

时间: 2024-08-23 23:25:46

[UWP]缓存Lottie动画帧的相关文章

[UWP]在UWP平台中使用Lottie动画

原文:[UWP]在UWP平台中使用Lottie动画 最近QQ影音久违的更新了,因为记得QQ影音之前体验还算不错(FFmepg的事另说),我也第一时间去官网下载体验了一下,结果发现一些有趣的事情. 是的,你没看错,QQ影音主界面上这个动画效果是使用Lottie动画实现的! 这让我大为惊奇,我对Lottie了解还算是比较多的,但是Lottie常见应用于移动端APP和网页,在传统桌面端见到还是第一次. 那就趁这个机会,来分享点关于Lottie动画的东西吧! Lottie动画的UWP实现 Lottie是

uwp 图片切换动画 使用帧动画

原文:uwp 图片切换动画 使用帧动画 上一篇博客使用了Timer来实现图片的切换,@lindexi_gd讨论了一下性能,我本人其实对性能这一方面不太熟,但我觉得还是有必要考虑一下,那么今天我们使用帧动画开实现以下 新建项目,添加一个Button和Image 在Page里定义资源 <Storyboard x:Key="std" x:Name="std" RepeatBehavior="Forever" AutoReverse="T

动画帧的使用

1.UIImageView动画帧的使用 通过加载UIImage序列的图片数据,然后设定个时间实现动画播放 2.UIImage的两种加载方式 UIImage imageName:fileName -----有缓存 UIImage imageWithContentsOfFile:path -----没有缓存 3.文档代码的注释 通过/** ......... */这样的注释叫文档注释 文档注释在你调用的时候会有显示注释,这样就不需要查定义的是什么

CSS3的自定义动画帧

CSS3新增的动画帧非常绚丽,可以简单实现一些动画效果,目前除IE外各大主流浏览器都支持 本文演示三个:transform: scale3d(x, y, z)-缩放;.transform: translate3d(x, y, z)-位移;.transform:rotateX/Y(?deg)-旋转; 演示地址:http://wjf444128852.github.io/demo02/css3/index.html @keyframes 动画名{} @-处理兼容性-keyframesanimatio

cocos3.0使用cocostudio动画帧结合地图对象键值创建精灵动画

内容如下: #include "cocos2d.h" #include "cocostudio/CocoStudio.h" //精灵猫和其他精灵的tag typedef enum{ catTag =1, }; //cocostudio 动画帧tag typedef enum{ catWalkTag = 1001, }; class Base :public cocos2d::Layer { public: Base(); ~Base(); //在地图中创建一个活多个

unity3d动画帧事件回调脚本必须直接挂在模型上

unity3d动画帧事件回调脚本必须直接挂在模型上,即与Animator同级,不能挂接在模型的父节点或者子节点,否则无法找到回调函数 以上在idle动作中新增帧事件skill,回调模型脚本中的skill()函数

Unity 3D 动画帧事件

前几天在项目开发中碰到一个这样的需求,RPG游戏中,特效和动画播放不同步的.假如主角在攻击NPC时,先实例化特效,后播放动画.动画毕竟是有一个时间长度的.等到动画播放攻击挥刀的那一瞬间时,特效可能早就播放完了.于是就想着在动画播放到某一帧的时刻才实例化特效,这样就解决了动画与特效不同步的问题.首先我们要做的是如何给动画添加事件? 1,建立一个空白的Unity工程,导入一个带有动画的模型. 前期准备基本上就这么多了,接下来就可以添加动画事件了.添加动画事件有两种:方法一:直接在模型中添加事件 到此

CSS3动画帧数科学计算法http://tid.tenpay.com/?p=5983

总结都浓缩在这个工具里了,想知道工具的地址或想窥探工具诞生的趣事请往下看 . —————————————————————–     华丽丽的开篇     —————————————————————- 本篇文章来自腾讯内部饭卡充值改版项目的CSS3动画经验总结.虽然你们访问不到我们的饭卡站点,不过可以小窥一下我们的动画示例哟. (请使用chrome.safari或firefox浏览器看效果,效果地址) 实现上面“嘀卡萌风骚乱舞”的动画,比较麻烦的是,要凭感觉自己算参数写代码,重复试个千百回,才能达

Lottie 动画里有图片怎么办?设计师小姐姐也能帮你减少开发量!

一.序 Hi,大家好,我是承香墨影! Lottie 是 Airbnb 开源的一套跨平台的完整解决方案,设计师只需要使用 After Effectes (之后简称 AE)设计出动画之后,使用 Lottic 提供的 Bodymovin 插件将设计好的动画导出成 JSON 格式,就可以直接运用在 iOS.Android 和 React Native之上,无需关心中间的实现细节. 这样,对于不同的工作角色来说,Lottie 带来的都是更简洁的工作流.开发小哥哥不再担心动画实现困难,设计师小姐姐也不需要担