双12前接了一个小项目“我的小纸条”,合作方中间各种延期改需求,好在两位师弟很给力很靠谱,一起把设计师的需求给完美实现,最终项目顺利上线。

最终效果如下(Gif图片有6MB,稍微多等会儿):

(如果看不了,可以尝试点击这里在页面观看效果图)

前期技术调研

  1. 存在着大量的图片和3个动画效果,这意味着需要使用大量的动图,那么gif、apng和webp这三种格式需要如何抉择?

    答:首先,gif格式的图片体积相对来说比较大,且gif图片每个像素只有8bit,显示效果不好[1] ,而webp的兼容性暂时没有apng好,最终在考虑了显示效果、性能、兼容性这三种情况[2],我们决定使用apng格式,且考虑到低版本的手淘不支持apng,所以引入了apng-js这个库来对apng的图片做处理,并使用canvas作为画布来把我们所需要的结果绘制出来,将使得我们可以放心大胆地使用apng。

  2. 页面性能如何兼顾?

    答:考虑飞机特效比较复杂,所以使用apng动图来实现,一开头的文字和后面的纸条展示使用transition并结合Promise做异步流程控制。

整体执行顺序图

成功
失败
获取apng图片
配置canvas运行时环境
获取player对象, 等待播放动画
降级为png纯图片
异步加载剩余的图片资源
用Promise控制整个动画流程

代码分析

项目是在weex环境下用Rax语法写的,Rax[3]相当于一个DSL,语法和React类。

这里要说到是,我们对普通的图片和apng图的处理方式是不同的,普通图片可以通过JS代码中Image对象来加载,而对于apng图片,我们需要将其转换为ArrayBuffer对象[4],这是一种通用的、固定长度的原始二进制数据格式,可用来处理声音、视频等其他信息。这里我们封装一个loadImg方法来处理这两种情况。

同时为了实现顺序的异步控制,我们用Promisethen方法来做异步控制,具体情况详见代码:

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;
}

总结

  1. 兼容性问题是个大坑,apng的效果还是杠杠的。
  2. 处理嵌套的Promise时,善用Promise.resolve

引用资料

  1. APNG 那些事

  2. UC内核支持更好的动画格式

  3. Rax介绍

  4. ArrayBuffer对象

玩转apng实现动画效果相关推荐

  1. 简单玩转ViewPager+Fragment动画效果,实现京东淘宝物流卡片效果 (附源码)

    物流卡片Demo 新版的京东和淘宝有一个交互感觉不错, 待收货订单会有类似探探那样的卡片效果, 滑动查看下一条物流的信息, 近期UI部门说要做这个效果, 于是我就写了一个Demo, 现在分享出来和大家 ...

  2. android 三维动画效果,9款令人惊叹的HTML5 3D动画应用

    原标题:9款令人惊叹的HTML5 3D动画应用 之前我们已经向大家分享了很多HTML5动画应用了,大部分都非常炫酷,也有一小部分是很实用的.今天我们要向各位HTML5动画爱好者介绍更多的HTML5 3 ...

  3. html5 css动画效果代码,超酷震撼 8个HTML5/CSS3动画应用及源码

    原标题:超酷震撼 8个HTML5/CSS3动画应用及源码 HTML5可以制作非常华丽的动画效果,这点通过之前的分享学习我们已经有深刻的了解了,今天我们主要来分享一些HTML5结合CSS3形成的超炫震撼 ...

  4. 前端实现图片快速反转替换_在canvas上实现元素图片镜像翻转动画效果的方法

    一.Canvas图片水平镜像翻转效果预览 demo页面中点击图片动画效果可见. 二.Canvas上实现图片镜像翻转的实现 CSS中要想实现元素的翻转效果,比较简单,例如我们希望某一张图片水平镜像翻转, ...

  5. [玩转UE4/UE5动画系统>Control Rig篇] 之 Control Rig + Fullbody IK版的足部IK实现(附项目代码)

    本教程采用图文教程+视频教程的多元化形式,我会为不同的知识点选择适当的表达方式.教程内容将同步免费发布于开发游戏的老王(知乎|CSDN)的专栏<玩转UE4/UE5动画系统>.教程中使用的资 ...

  6. html5 粒子动画效果制作,8款惊艳的HTML5粒子动画特效

    原标题:8款惊艳的HTML5粒子动画特效 HTML5确实非常强大,很多时候我们可以利用HTML5中的新技术实现非常炫酷的粒子动画效果,粒子动画在HTML5应用中也是比较消耗本地资源的,尤其是CPU,但 ...

  7. 【小程序动画合集】10种小程序动画效果实现方法,文章太长建议收藏!

    前言 一提小程序与动画,首先想到的是什么?嗯,微信小程序独创了一套动画玩法,官方支持3种动画方案,分别是 createAnimation . this.animate 和 CSS3动画 . 1. cr ...

  8. html 气泡动画效果,css3实现好看的气泡按钮动画特效

    CSS3在我们网页设计中是最关键的一环,为什么这么说呢?我们在浏览别人的网站时,经常会看到特别好看的动画效果,比如一个按钮啊,一个图片啊,每次看到都能够让人有种赏心悦目的感觉,这就使网站更具有吸引力和 ...

  9. [玩转UE4/UE5动画系统>Control Rig篇] 之 使用Control Rig实现目标偏移(Aim Offset)(附项目代码)

    本教程采用图文教程+视频教程的多元化形式,我会为不同的知识点选择适当的表达方式.教程内容将同步免费发布于开发游戏的老王(知乎|CSDN)的专栏<玩转UE4/UE5动画系统>.教程中使用的资 ...

最新文章

  1. 【原创】StreamInsight查询系列(六)——基本查询操作之分组聚合
  2. 流程的python-流畅的Python
  3. xshell复制粘贴
  4. QDir类cleanPath函数用法
  5. 真实AIS数据,解码,可视化
  6. 英语中十二个月名称的由来
  7. 腾讯云服务器CentOS安装JDK+Tomcat+MySQL详细步骤(以及遇到的各种坑)
  8. 温度补偿计算公式_管道布置设计原则、基本要求与补偿器的选择
  9. 安卓手机计算器应用java_安卓体重计算器java源程序 使用Intent在Activity间传输数据...
  10. King Arthur's Birthday Celebration
  11. java计算机毕业设计网上书店管理系统源码+系统+数据库+lw文档+mybatis+运行部署
  12. mysql报表展示工具_Navicat for MySQL 设计报表版面教程
  13. C++数据库编程 ODBC简介
  14. 抖音神器---python实现图片转字符
  15. java printf 格式_JAVA中Printf支持的格式
  16. Win xp IIS无法启动解决办法收集
  17. 10064---JVM GC 机制与性能优化
  18. 基因家族分析⑤:进化树构建
  19. 净利率远低同行,诺威健康如何在CRO跑道上争排位?
  20. 微信支付后台接口开发(扫码版)

热门文章

  1. 为什么要用段地址和偏移地址?
  2. 拔刀剑服务器文件,我的世界光宇世界服务器—生存多线||工业|食物工艺|拔刀剑|[1.7.10]...
  3. 什么是骨传导耳机?骨传导耳机对比一般耳机优势在哪?
  4. RNN的一些高级用法-以温度预测问题为例
  5. Qt开发之路39---Qt pro项目检测编译器版本(64位或32位)
  6. 基于STM32F411使用SPI+DMA驱动LCD
  7. 计算机网络基础崔冬,视频编辑领域中计算机技术的应用
  8. Apple M1与英特尔芯片:两个强大处理器的比较
  9. wampserver打开localhost显示域名重定向怎么办?localhost显示域名重定向解决办法
  10. [RTL-SDR] RTL-SDR原理图