玩转apng实现动画效果
双12前接了一个小项目“我的小纸条”,合作方中间各种延期改需求,好在两位师弟很给力很靠谱,一起把设计师的需求给完美实现,最终项目顺利上线。
最终效果如下(Gif图片有6MB,稍微多等会儿):
(如果看不了,可以尝试点击这里在页面观看效果图)
前期技术调研
存在着大量的图片和3个动画效果,这意味着需要使用大量的动图,那么gif、apng和webp这三种格式需要如何抉择?
答:首先,gif格式的图片体积相对来说比较大,且gif图片每个像素只有8bit,显示效果不好[1] ,而webp的兼容性暂时没有apng好,最终在考虑了显示效果、性能、兼容性这三种情况[2],我们决定使用apng格式,且考虑到低版本的手淘不支持apng,所以引入了apng-js这个库来对apng的图片做处理,并使用
canvas
作为画布来把我们所需要的结果绘制出来,将使得我们可以放心大胆地使用apng。页面性能如何兼顾?
答:考虑飞机特效比较复杂,所以使用apng动图来实现,一开头的文字和后面的纸条展示使用transition并结合Promise做异步流程控制。
整体执行顺序图
代码分析
项目是在weex环境下用Rax语法写的,Rax[3]相当于一个DSL,语法和React类。
这里要说到是,我们对普通的图片和apng图的处理方式是不同的,普通图片可以通过JS代码中Image
对象来加载,而对于apng图片,我们需要将其转换为ArrayBuffer
对象[4],这是一种通用的、固定长度的原始二进制数据格式,可用来处理声音、视频等其他信息。这里我们封装一个loadImg方法来处理这两种情况。
同时为了实现顺序的异步控制,我们用Promise
的then
方法来做异步控制,具体情况详见代码:
function loadImg(url, useFetch = false) {if (useFetch) {// 转化为ArrayBuffer对象return fetch(url).then(a => a.arrayBuffer());} else {return new Promise((resolve, reject) => {let img = new Image();img.onload = () => resolve(img);img.onerror = (e) => reject(e);img.src = url;});}
}
关键代码展示:
import apng from 'apng-js';const images = {bg: 'https://gw.alicdn.com/tfs/TB17A2BrQPoK1RjSZKbXXX1IXXa-1125-2001.jpg',aText: 'https://gw.alicdn.com/tfs/TB1IULErQzoK1RjSZFlXXai4VXa-1125-900.png',aBtn: 'https://gw.alicdn.com/tfs/TB1aDIMshTpK1RjSZR0XXbEwXXa-1125-315.png',aPlane: {apng: 'https://gw.alicdn.com/tfs/TB1zEjwrSzqK1RjSZFLXXcn2XXa-750-1334.png?getAvatar=1',png: 'https://gw.alicdn.com/tfs/TB1XLCCppzqK1RjSZFvXXcB7VXa-750-1334.png',},bFrames: ['https://gw.alicdn.com/tfs/TB1KyTBrMHqK1RjSZJnXXbNLpXa-1125-2001.png?getAvatar=1', // 02'https://gw.alicdn.com/tfs/TB1mKHCrQvoK1RjSZFDXXXY3pXa-1125-2001.png?getAvatar=1', // 03'https://gw.alicdn.com/tfs/TB1R9fArHrpK1RjSZTEXXcWAVXa-1125-2001.png?getAvatar=1', // 04'https://gw.alicdn.com/tfs/TB1pwjzrOrpK1RjSZFhXXXSdXXa-1125-2001.png?getAvatar=1', // 05'https://gw.alicdn.com/tfs/TB12T6zrFzqK1RjSZFoXXbfcXXa-1125-2001.png?getAvatar=1', // 06'https://gw.alicdn.com/tfs/TB1h5_VrNnaK1RjSZFBXXcW7VXa-1125-2001.png?getAvatar=1', // 07]
};this.loadData().then(data => {this.imageLoader[0] = loadImg(images.aPlane.apng, true).then(buf => {// 使用apng-js加载apng图片const imgObj = apng(buf);if (imgObj instanceof Error) {throw imgObj;}let retry = 0;const waitCanvas = () => new Promise((resolve, reject) => {// 配置canvas环境const ctx = this.setupCanvas();if (ctx) {resolve(ctx);} else if (++retry > 5) {reject(new Error('cannot get canvas in .5s'));} else {setTimeout(() => {resolve(waitCanvas());}, 100);}});return imgObj.createImages().then(waitCanvas)// 获取player对象.then(ctx => imgObj.getPlayer(ctx));}).catch(err => {// 加载 apng 过程出错,降级为纯图片console.error(err);return loadImg(images.aPlane.png).then(() => null);});[images.bg, images.aText, images.aBtn, data.imgUrl, data.shareImgUrl].forEach(url => this.imageLoader.push(loadImg(url)));// images.bFrames 存放的是纸飞机展开过程的几张图片this.imageLoader.push(Promise.all(images.bFrames.map(url => loadImg(url))));// 使用Promise.all来等待全部图片加载完成,并做后续处理Promise.all(this.imageLoader).then(([apngPlayer, bg, aText, aBtn, result, share, bFrames]) => {this.bFrames = bFrames;if (apngPlayer) {this.apngPlayer = apngPlayer;}setTimeout(() => {this.play();}, 500);}).catch(e => console.error(e));});
import { findDOMNode } from 'rax';
import transition from 'universal-transition';...// 文字淡入
displayTexts = (duration = 400) => {const $textDoms = this.textRefKeys.map(refKey => findDOMNode(this.refs[refKey]));// 将上一个流程中的Promise置为完成,获取当前操作流程中的Promise,准备展示textlet sequence = Promise.resolve();$textDoms.forEach(dom => {// 更新当前的操作顺序里的Promisesequence = sequence.then(() => new Promise(resolve => {// 使用transition做动画,动画完成后调用resolve方法transition(dom,{ opacity: 1 },{ duration, timingFunction: 'ease-in-out' },resolve);}));});return sequence;
}
总结
- 兼容性问题是个大坑,apng的效果还是杠杠的。
- 处理嵌套的
Promise
时,善用Promise.resolve
引用资料
APNG 那些事
UC内核支持更好的动画格式
Rax介绍
ArrayBuffer对象
玩转apng实现动画效果相关推荐
- 简单玩转ViewPager+Fragment动画效果,实现京东淘宝物流卡片效果 (附源码)
物流卡片Demo 新版的京东和淘宝有一个交互感觉不错, 待收货订单会有类似探探那样的卡片效果, 滑动查看下一条物流的信息, 近期UI部门说要做这个效果, 于是我就写了一个Demo, 现在分享出来和大家 ...
- android 三维动画效果,9款令人惊叹的HTML5 3D动画应用
原标题:9款令人惊叹的HTML5 3D动画应用 之前我们已经向大家分享了很多HTML5动画应用了,大部分都非常炫酷,也有一小部分是很实用的.今天我们要向各位HTML5动画爱好者介绍更多的HTML5 3 ...
- html5 css动画效果代码,超酷震撼 8个HTML5/CSS3动画应用及源码
原标题:超酷震撼 8个HTML5/CSS3动画应用及源码 HTML5可以制作非常华丽的动画效果,这点通过之前的分享学习我们已经有深刻的了解了,今天我们主要来分享一些HTML5结合CSS3形成的超炫震撼 ...
- 前端实现图片快速反转替换_在canvas上实现元素图片镜像翻转动画效果的方法
一.Canvas图片水平镜像翻转效果预览 demo页面中点击图片动画效果可见. 二.Canvas上实现图片镜像翻转的实现 CSS中要想实现元素的翻转效果,比较简单,例如我们希望某一张图片水平镜像翻转, ...
- [玩转UE4/UE5动画系统>Control Rig篇] 之 Control Rig + Fullbody IK版的足部IK实现(附项目代码)
本教程采用图文教程+视频教程的多元化形式,我会为不同的知识点选择适当的表达方式.教程内容将同步免费发布于开发游戏的老王(知乎|CSDN)的专栏<玩转UE4/UE5动画系统>.教程中使用的资 ...
- html5 粒子动画效果制作,8款惊艳的HTML5粒子动画特效
原标题:8款惊艳的HTML5粒子动画特效 HTML5确实非常强大,很多时候我们可以利用HTML5中的新技术实现非常炫酷的粒子动画效果,粒子动画在HTML5应用中也是比较消耗本地资源的,尤其是CPU,但 ...
- 【小程序动画合集】10种小程序动画效果实现方法,文章太长建议收藏!
前言 一提小程序与动画,首先想到的是什么?嗯,微信小程序独创了一套动画玩法,官方支持3种动画方案,分别是 createAnimation . this.animate 和 CSS3动画 . 1. cr ...
- html 气泡动画效果,css3实现好看的气泡按钮动画特效
CSS3在我们网页设计中是最关键的一环,为什么这么说呢?我们在浏览别人的网站时,经常会看到特别好看的动画效果,比如一个按钮啊,一个图片啊,每次看到都能够让人有种赏心悦目的感觉,这就使网站更具有吸引力和 ...
- [玩转UE4/UE5动画系统>Control Rig篇] 之 使用Control Rig实现目标偏移(Aim Offset)(附项目代码)
本教程采用图文教程+视频教程的多元化形式,我会为不同的知识点选择适当的表达方式.教程内容将同步免费发布于开发游戏的老王(知乎|CSDN)的专栏<玩转UE4/UE5动画系统>.教程中使用的资 ...
最新文章
- 【原创】StreamInsight查询系列(六)——基本查询操作之分组聚合
- 流程的python-流畅的Python
- xshell复制粘贴
- QDir类cleanPath函数用法
- 真实AIS数据,解码,可视化
- 英语中十二个月名称的由来
- 腾讯云服务器CentOS安装JDK+Tomcat+MySQL详细步骤(以及遇到的各种坑)
- 温度补偿计算公式_管道布置设计原则、基本要求与补偿器的选择
- 安卓手机计算器应用java_安卓体重计算器java源程序 使用Intent在Activity间传输数据...
- King Arthur's Birthday Celebration
- java计算机毕业设计网上书店管理系统源码+系统+数据库+lw文档+mybatis+运行部署
- mysql报表展示工具_Navicat for MySQL 设计报表版面教程
- C++数据库编程 ODBC简介
- 抖音神器---python实现图片转字符
- java printf 格式_JAVA中Printf支持的格式
- Win xp IIS无法启动解决办法收集
- 10064---JVM GC 机制与性能优化
- 基因家族分析⑤:进化树构建
- 净利率远低同行,诺威健康如何在CRO跑道上争排位?
- 微信支付后台接口开发(扫码版)
热门文章
- 为什么要用段地址和偏移地址?
- 拔刀剑服务器文件,我的世界光宇世界服务器—生存多线||工业|食物工艺|拔刀剑|[1.7.10]...
- 什么是骨传导耳机?骨传导耳机对比一般耳机优势在哪?
- RNN的一些高级用法-以温度预测问题为例
- Qt开发之路39---Qt pro项目检测编译器版本(64位或32位)
- 基于STM32F411使用SPI+DMA驱动LCD
- 计算机网络基础崔冬,视频编辑领域中计算机技术的应用
- Apple M1与英特尔芯片:两个强大处理器的比较
- wampserver打开localhost显示域名重定向怎么办?localhost显示域名重定向解决办法
- [RTL-SDR] RTL-SDR原理图