近几年能明显感觉到,互联网产品已经越来越离不开动效了:不管是app里会动的加载动画ui,还是直播间里华丽的礼物,都需要经历动效上线的过程。而负责这些动效上线的前端同学应该有过这样的体验:各种加班写代码上线动画效果,并且在动效还原过程中反复和设计师联调效果。
最近接触到一个开源项目可以帮助大家解决这个问题,它就是pag动效组件。
pag 简介:认识 pag动效
pag是来自腾讯的一套完整的动画工作流解决方案,助力于将 ae 动画方便快捷的应用于各平台终端。pag 的流程图下图所示,设计师在 ae 上设计出动画后,可以通过导出插件导出 pag 文件,同时 pag 提供了桌面端预览工具,支持实时预览效果,在确认效果后,通过运行配置上线,各平台终端可以通过 pag sdk 加载渲染 pag 动画。
img
pag 特点及优势 :和其他动效上线方案相比,它强在哪里
● 开源项目:无需担心团队维护问题。
● 文件更小:pag采用针对 ae 时间轴属性设计的二进制文件编码器,能够使用动态比特位紧凑存储,冗余信息极少,文件体积最小,解码速度最快,且支持单文件集成图片和音频等外部资源。
● 全 ae 特性支持:在纯矢量的导出模式下,无论是哪种实现方案,在众多的 ae 特性面前,都只支持将有限的 ae特性导出渲染,pag 方案提供了 bmp 预合成的解决方案,支持将特定图层截图导出成透明视频,实现了对于所有 ae 特性导出的支持。
● 渲染架构:相对于 lottie 、svga 依赖于平台端相关的渲染接口,pag 使用了跨平台一致的 c++ 架构,平台层面仅仅提供渲染环境,渲染的主体位于 c++ 层,可以实现跨平台的渲染一致性。
● 支持的平台更多:相比 lottie 支持 android、ios、web 和 macos, svga 支持 android、ios、web,pag 实现了 android、ios、web、macos、windows、linux 和 微信小程序,支持的平台更多。
这里放一张对比图,大家就一目了然了:
img
想进一步了解也可以去开源地址:https://github.com/tencent/libpag
pag 使用:核心类分析及常用方法解读
在浏览过开源地址后,这里帮大家整理出核心类的分析:
类 分析
paglayer pag 的渲染图层,pag 是一个树状结构,paglayer 相当于树状结构中的叶子节点。pag 对外暴露的渲染图层包括 pagimagelayer (图片图层)、pagtextlayer (文本图层)、pagsolidlayer(实色图层),只有这些图层可以二次改编辑。paglayer 主要提供了以下能力:以 c++ 层接口为例 ( pag 的对外接口保持各平台一致性),通过 setmatrix 可以控制图层的位移、缩放和扭曲,通过 setvisible 控制图层的显示与隐藏,通过 localtimetoglobal 获取该图层相对于主渲染时间轴的具体时间,通过 setstarttime 设置图层相对于主时间轴的开始时间,通过设置 setexcludedfromtimeline 可以控制该图层渲染是否脱离主时间轴由接入方控制。
pagimagelayer pagimageview 为图片图层,支持通过 replaceimage 的方法替换默认占位图,同时支持通过 imagebytes 获取默认占位图的数据。它支持用户自己创建,支持指定开始时间和时长,典型的应用场景中将一个外部视频添加到 pag 渲染数据。
pagtextlayer pagtextlayer 为文本图层,支持用户修改默认的文本信息、文本颜色、更换字体、字体大小等。为了方便修改文本数据,c++ 封装了 textdocument 类,支持修改文本的排版、斜体、描边信息等,对应 ios、android 平台的 pagtext 类。
pagsolidlayer pagsolidlayer 为实色图层,支持修改实色图层的颜色
pagcompostion pagcomponsition 是渲染树中的容器,继承于 paglayer,可以包含多个 paglayer,支持用户自己创建,支持增加、删除、更换渲染顺序,支持通过图层名称获取该名称对应的图层。
pagfile pagfile 继承于 pagcomposition,不支持自己创建,通过加载 pag 文件获得,相对比 pagcomposition,pagfile 增加了替换文本图层和图片图层内容的接口,因此如果需要编辑文本图层和图片图层的内容,一方面可以通过图层自身的接口,另一方面可以通过 pagfile 的接口。
pagsurface pagsurface 是 pag 的渲染画布。pagsurface 的创建, ios 平台可以通过 cvpixelbufferref、尺寸(内部同样创建的是 cvpixelbufferref),android 平台可以通过 surface、surfacetexture、textureid、尺寸等创建,不同的创建方法对应不同的应用场景。pagsurface 提供了获取单帧渲染数据的接口,支持获取 bgra 数据、cvpixelbufferref 和bitmap。
pagplayer pag 的播放控制类,持有 pagsurface 和 pagcomposition, 可以通过 setprogress 控制渲染进度, flush 完成当前帧的渲染,可以通过 getbounds 接口获取各 paglayer 相对于 pagsurface 渲染画布的位置信息, 通过 getlayersunderpoint 获取特定位置下的所有图层。pagcomposition、pagsurface、pagplayer 为 pag 的三个组件,pagcomposition 提供渲染数据,pagsurface 提供渲染画布,pagplayer 控制渲染进度。在视频后编辑场景,这种使用方式经常被用到。
pagview 主要使用在 ui 动画场景,存在 android、ios、macos、web、微信小程序等平台,ios 平台继承于 uiview, andoroid 平台继承于 textview, pagview 支持通过本地路径 和 pagcomposition 加载, 调用 play 方法进行播放,内部有一个定时器,同时也提供了 pagviewlistener 的接口监听动画播放的状态,pagview 内部实现了 pagplayer、pagsurface、pagcomposition 的封装处理。
pagdecoder pagdecoder 目前存在于 ios 和 android 平台, 用于获取 pag 文件的渲染数据,支持通过 pagcomposition 创建,渲染的尺寸和 pagcompositon 的尺寸一致,同时增加 sacle 参数支持用户设置渲染尺寸,通过 maxframerate 的参数设置最大渲染帧率。在数据读取层面,支持获取数据为 uiimage 、bitmap 和 rgba 数据。
pagimageview pagimageview 主要应用于 ui 列表以及页面中含有多个 pag 文件同时渲染的场景。这些场景使用 pagview 实现时,页面中需要含有多个 pagview,每一个 pagview 都需要有一个 gpu 的渲染环境,任何一个 gpu 渲染的方案都会有一个初始的屏幕缓冲区开销,从而造成 内存占用的增加。pagimageview 增加了磁盘缓存的功能,针对渲染过的内容,都会缓存到本地,当所有帧的数据缓存完成后,就会销毁 pag 的渲染环境,剩余的就是磁盘数据的读取,实现了pag 文件渲染与素材的无关性。如果 pag 文件的相关内容没有被编辑过(如编辑文本、替换占位图等),下次加载就会直接读取缓存数据,无需创建 pag 渲染环境。pagimageview 的缓存支持两种模式:全磁盘和全内存, 默认为全磁盘模式,此时内存占用是最小的,cpu 占用相对较低,全内存模式的 cpu 占用最低,但内存占用较高,适用于对 cpu 占用要求较高、pag 文件帧数较少的场景。
这些pag常用方法解读也能帮助大家使用起来更轻松:
pag 运行时编辑
pag 的运行时编辑主要分为两类:1)修改文本图层的文本信息、替换图片图层中的占位图、修改实色图层中的颜色
// c++std::shared_ptr replaceimageortext() { auto pagfile = pag::load(../../assets/test2.pag); if (pagfile == nullptr) { return nullptr; } for (int i = 0; i numimages(); i++) { auto pagimage = pag::frompath(../../assets/scene.png); pagfile->replaceimage(i, pagimage); } for (int i = 0; i numtexts(); i++) { auto textdocumenthandle = pagfile->gettextdata(i); textdocumenthandle->text = hah哈 哈哈哈哈; pagfile->replacetext(i, textdocumenthandle); } return pagfile;} 2)渲染树编辑
渲染树编辑指的是通过使用 pagcomposition 的相关接口,完成多个图层、多个 pag 文件的自由组合。具体如何使用可以参考下面的代码:
// 以 oc 版本为例- (pagcomposition *)makecomposition { pagcomposition* compostion = [pagcomposition make:self.view.bounds.size]; float itemwidth = self.view.bounds.size.width / 5; float itemheight = 100; for (int i = 0; i 0) { [self.pagimageview setpath:filepath]; [self.pagimageview setcurrentframe:0]; [self.pagimageview setrepeatcount:-1]; [self.pagimageview play]; }} pag 字体注册
pag 除了支持修改文本图层的文本信息外,还支持修改字体。具体方法如下:
(1)通过 pagfont 获取字体的相关信息,然后赋值给 pagtext,使用到的接口如下:
获取字体信息
// c++ /** * registers a font required by the text layers in pag files, so that they can be rendered as * designed. */ static pagfont registerfont(const std::string& fontpath, int ttcindex, const std::string& fontfamily = , const std::string& fontstyle = ); /** * registers a font required by the text layers in pag files, so that they can be rendered as * designed. */ static pagfont registerfont(const void* data, size_t length, int ttcindex, const std::string& fontfamily = , const std::string& fontstyle = ); pagfont(std::string fontfamily, std::string fontstyle) : fontfamily(std::move(fontfamily)), fontstyle(std::move(fontstyle)) { } /** * a string with the name of the font family. **/ const std::string fontfamily; /** * a string with the style information — e.g., “bold”, “italic”. **/ const std::string fontstyle; (2)fontfamlily 和 fontstyle 赋值给 pagtext,pagtext 再填充到 pagtextlayer 中。
// c++ for (int i = 0; i numtexts(); i++) { auto textdocumenthandle = pagfile->gettextdata(i); textdocumenthandle->text = hah哈 哈哈哈哈; // use special font auto pagfont = pag::registerfont(../../resources/font/notosanssc-regular.otf, 0); textdocumenthandle->fontfamily = pagfont.fontfamily; textdocumenthandle->fontstyle = pagfont.fontstyle; pagfile->replacetext(i, textdocumenthandle); } 如果使用了特定字体而又没有注册或字体文件中没有包含该字符,pag 内部(android、ios、macos、windows 平台)有一个默认字体列表(同时支持外部设置字体回退列表,外部设置时会覆盖默认设置),会回退到 pag 的默认字体列表中,此时使用那种字体对于业务方而言是不确定的。
// c++ /** * resets the fallback font names. it should be called only once when the application is being * initialized. */ static void setfallbackfontnames(const std::vector& fontnames); /** * resets the fallback font paths. it should be called only once when the application is being * initialized. */ static void setfallbackfontpaths(const std::vector& fontpaths, const std::vector& ttcindices); pag 视频编辑场景
在视频编辑场景,使用的不是 pagview,而是 pagplayer、pagsurface 和 pagcomposition。
pagsurface 可以通过 cvpixelbufferref 或纹理创建,方便快捷的与视频后编辑中的 cvpixelbuffer 或 纹理进行整合。同时 pagimage 也支持通过 cvpixelbufferref 或 纹理创建,通过 pagplayer 控制播放进度,将视频内容填充进图片图层的占位图。
// oc/** * creates a pagimage object from the specified cvpixelbuffer, return null if the cvpixelbuffer is * invalid. */+ (pagimage*)frompixelbuffer:(cvpixelbufferref)pixelbuffer;// javapublic static pagimage fromtexture(int textureid, int texturetarget, int width, int height);public static pagimage fromtexture(int textureid, int texturetarget, int width, int height, boolean flipy); pag 软解注入
为什么会有软解注入?pag 的导出方式中支持 bmp 预合成导出,在 pag 文件中,如果含有 bmp 预合成,一个 bmp 预合成相当于一个视频,视频则需要解码。在 pag sdk 中默认使用硬件解码,但硬件解码存在两个问题:
1)移动端硬件解码器的瞬时存在数目是有限制的,不能无限的创建,如果创建硬件解码器的数目超过限制,就会出现解码器创建失败的情况,在视频编辑场景中需要关注;
2)android 平台由于机型兼容性、碎片化验证,不能保证所有的机型都能硬件解码成功,因此 android 平台软解是必须的。
于是,在提供的制品库中, ios 平台由于没有兼容性的问题,默认是不带软解的,android 提供了两个包,普通的包默认是内置软解的,noffavc 的包是没有内置软解的。
具体怎么注入软解呢:android 平台可以选择完整制品库,ios 平台可以引入 ffavc。
pod 'ffavc'
通过如下方法完成软件解码器的注册:
// oc-(void)registersoftwaredecoder{ // 注册软件解码器工厂指针 auto factory = ffavc::gethandle(); [pagvideodecoder registersoftwaredecoderfactory:(void*)factory]; } 如果接入方自己的 app 中已经内置了软解库,可以通过外部注入的方式注入软解。
pag 官网提供了下面这个软解注入接口,需要业务方基于自己的解码器实现,pag bmp 预合成中的视频编码格式为 h264。
https://github.com/libpag/ffavc/blob/main/vendor/libpag/include/pag/decoder.h
具体的实现方式可以参考:
ffavc/ffavcdecoder.cpp at main · libpag/ffavc · github
然后自己通过上面提到的方式注入软解。
pag 服务端渲染
和 lottie、svga 不同的是,pag 支持服务端渲染,尽管 pag 的渲染依赖 opengl 环境,但 服务端却可以继续使用 cpu 服务器。具体实现层面,pag 通过 swiftshader 在 cpu 服务器上构建出了 opengl 环境,使得 pag 文件可以在 cpu 环境中正常渲染。
在服务端的具体使用如下,获取到的是 bgra 的数据,该数据可以用于视频编码。
// c++ auto pagfile = pag::load(../../assets/test2.pag); auto pagsurface = pag::makeoffscreen(pagfile->width(), pagfile->height()); if (pagsurface == nullptr) { printf(---pagsurface is nullptr!!!); return -1; } auto pagplayer = new pag::pagplayer(); pagplayer->setsurface(pagsurface); pagplayer->setcomposition(pagfile); auto totalframes = timetoframe(pagfile->duration(), pagfile->framerate()); auto currentframe = 0; int byteslength = pagfile->width() * pagfile->height() * 4; while (currentframe setprogress(currentframe * 1.0 / totalframes); auto status = pagplayer->flush(); // pag 渲染数据读取 auto data = new uint8_t[byteslength]; pagsurface->readpixels(pag::bgra_8888, pag::premultiplied, data, pagfile->width() * 4); delete[] data; currentframe++; } delete pagplayer; 开始使用 :如何接入
接入使用分为开发部分和设计部分,这里分别为大家介绍下:
(1) 开发者——接入sdk
在pag的github wiki中有非常详细的接入指引(包括android、ios、web等平台的接入方法和范例工程)
img
(2)设计师——下载导出插件和预览工具
设计师想要使用pag,只需在官网下载预览工具 pagviewer和 ae 导出插件即可//pag.art/docs/install.html
img
整体看来,pag的核心价值如下:
上线难点 传统方案痛点 pag动效价值
研发成本 每个动效都需要研发通过代码来还原,需要大量的研发人力持续投入,由于研发人力有限,导致这个流程无法批量化生产素材。 研发只有一次性接入 sdk的成本,在后续整个素材生产流程都无需研发人力介入。整套工作流不在受制于研发的人力瓶颈,就能够开放给更多的设计师使用,批量化的进行素材生产。
生产周期 设计师和研发人员的联调成本高,效果还原度需要反复确认,中间产生较长的上线周期,拖延产品运营节奏。 由于砍掉了研发成本,最耗时的研发和设计的联调环节也不存在了。设计师可以所见即所得地生产素材,极大地缩短了生产周期,能够快速响应运营热点。
动效视觉 ae里有很多复杂动效,使用纯代码还原起来非常困难,设计师只能不断简化效果以达到跟开发成本的平衡,导致上线的视觉效果大打折扣。 pag的sdk完全还原了ae整个动效的渲染系统,接入一次,设计师就可以充分利用ae动效的原子能力,组合出无限的视觉动效,不用因为代码还原成本的问题而打折扣。
据官网显示已经有很多头部应用接入使用(如微信、王者荣耀、小红书、知乎等),稳定性应该很有保证,如果有动效上线相关业务的朋友非常值得试试。
MMKP82电容器的全称是什么?
中兴通讯发布新一代全屋光纤组网系列产品
AX88179千兆以太网 USB3.0转RJ45-集佰睿
法国巴黎国际汽车零部件工业博览会
关于快充技术科普和应用说明
什么是PAG动效组件 PAG动效方案使用总结
华为不想做传统家电 意图三年内成中国智能家居第一生态
笔形电烙铁与尖头电烙铁特点介绍
全国计算机等级考试二级C语言考试大纲
BlackBerry进军无人驾驶领域,以执行委员身份加入 OmniAir 联盟
“福禄克早间知识讲堂”微信号上线啦
据报告,2月国内市场5G手机出货量超1500万部
按键复位是什么 单片机按键复位程序
分类门禁控制器_门禁控制器使用说明
New RS-232 ICs Feature 1µ
如何解决工控机的电磁干扰问题
小米空调的“第二次试水”雷军和董明珠的逐鹿之战爆发
科大讯飞全球1024开发者节最新内容发布 AI向新 数智万物
机床是一个国家制造业水平的象征
I2C总线驱动程序的实现