软件项目技术点(9)——如何将gif动态图拆分绘制

摘要:
AxeSlide软件项目梳理画布绘图系列知识点,安排背景,介绍我们的软件支持插入gif图像,并在软件中动态显示它们,例如插入以下gif图像。

AxeSlide软件项目梳理   canvas绘图系列知识点整理

背景介绍

我们的软件支持插入gif图片,并且展示在软件里是动态的,例如插入下面这张gif图。

软件项目技术点(9)——如何将gif动态图拆分绘制第1张

在软件里显示的同样是这样的动态效果:

软件项目技术点(9)——如何将gif动态图拆分绘制第2张

那么这张动态的图是怎么绘制到canvas上面的呢,如果只是像绘制一张普通图片用context.drawImage(img,x,y),这样绘制出来的只是当前显示到img标签的一个静态画面。

下面介绍我们项目中使用的方法:

1. 解析gif文件信息

安装Node.js的gify-parse模块,该模块用于解析gif文件信息的API。

具体使用和介绍参见: https://www.npmjs.com/package/gify-parse

我们读取上面那张gif图到buffer然后用该模块解析出的结果如下图:

软件项目技术点(9)——如何将gif动态图拆分绘制第3张软件项目技术点(9)——如何将gif动态图拆分绘制第4张

注意:解析出来的结果有点小问题,宽高的值是颠倒的

利用上面图1中的gifInfo信息,我们用animated=true判定这张图确实是gif图,它是由24张图组成,每张图的宽高为384*288

利用上面图2中的delay这个属性值,它表示两张图变换的间隔时间,在接下来的第3步绘制大图到canvas中会用到这个属性。

2. 拼大图

我们的思路就是把gif中包含的24张图拼成一张大图片,拼大图我们利用canvas,将24张图挨个绘制到临时的一个canvas上面,最后将canvas保存成本地png文件。

下面的代码用来计算我们的画布tempCanvas的宽高:

 1 var tempCavas = <HTMLCanvasElement>document.createElement("canvas");
 2 //canvas元素的宽在大约40000的时候,将无法进行绘图
 3 //设置30000为最大值
 4 var shouldWidth = gifInfo.width * gifInfo.images.length;
 5 if (shouldWidth > 30000) {
 6     tempCavas.width = Math.floor(30000 / gifInfo.width) * gifInfo.width;
 7     tempCavas.height = Math.ceil(gifInfo.images.length / Math.floor(30000 / gifInfo.width))*gifInfo.height;
 8 }else {
 9     tempCavas.width = shouldWidth;
10     tempCavas.height = gifInfo.height;
11 }

gify-parse模块只解析出来了宽高等一部分有用信息那么,不能得到每张具体的图片。

我们需要引入gif模块,https://github.com/liufangfang/gif 从这里下载即可,该模块很简单只有一个函数function(gifSrcPath, callBack) {},传入gif图片文件路径和一个回调函数,回调函数接收错误信息和每个帧存储到本地的图片路径callBack(null, pathList)。

下面就看我们的回调函数如何利用这个文件列表files:

1)基本思路就是通过createElement("img")创建IMG标签

2)img.onload之后将图片绘制到canvas.context上,当然绘制的位置是需要根据当前图片是gif图中第几帧位置去计算的

3)绘制完最后一张后,将canvas转换成图片信息保存到本地

cxt.drawImage(tempImage, 0, 0, gifInfo.width, gifInfo.height, startx, starty, gifInfo.width, gifInfo.height);
 1 require('gif')(path,(error, files: Array<string>) => {
 2     if (error) {
 3         Logger.setErrLog(LogCode.image, "文件:File,方法:node_modules-gif,异常信息:" + error);
 4         callBack(null);
 5     }
 6     files.forEach((file, index) => {
 7         var targetDir = FileSytem.imageTempDir + id + index + ".jpg";
 8         
 9         FileSytem.copySync(file, targetDir);
10 
11         try
12         {
13             var tempImage = <HTMLImageElement>document.createElement("img");
14             var tempImageSrc = targetDir;
15             tempImage.id = index.toString();
16             tempImage.src = tempImageSrc;
17             tempImage.onload = (ev: Event) => {
18                 try
19                 {  //计算该张图片绘制到canvas上的位置
20                     var atWidth = gifInfo.width * Number(tempImage.id);
21                     var startx = atWidth % tempCavas.width;
22                     var starty = (atWidth / tempCavas.width | 0) * gifInfo.height;
23 
24                     cxt.drawImage(tempImage, 0, 0, gifInfo.width, gifInfo.height, startx, starty, gifInfo.width, gifInfo.height);
25                     FileSytem.remove(tempImageSrc, null);
26                     ev.target = null;
27                     loadCounter++;
28                     if (gifInfo.images.length == loadCounter) {
29                         var dataBuffer = new Buffer(tempCavas.toDataURL("image/png").replace(/^data:image/w+;base64,/, ""), 'base64');
30                         var dataPath = FileSytem.imageDir + id + ".png";
31                         FileSytem.fileSaveSync(dataPath, dataBuffer);
32                         callBack(dataPath, tempCavas.width);
33                         tempCavas.width = 0;
34                         tempCavas.height = 0;
35                     }
36                 }
37                 catch (e) {
38                     Logger.setErrLog(LogCode.image, "文件:File,方法:gifToPng_1,异常信息:" + e);
39                     callBack(null);
40                 }
41             }
42         }
43         catch (e) {
44             Logger.setErrLog(LogCode.image, "文件:File,方法:gifToPng_2,异常信息:" + e);
45             callBack(null);
46         }
47     });
48 });

最后我们拼成的一张大图如下,如果帧数多或者较宽,因为我们设置了最宽30000px 所以就会出现多行的大图。 

软件项目技术点(9)——如何将gif动态图拆分绘制第5张

 3.将大图绘制到canvas

插入gif到生成大图的过程已经写清楚了,那么怎么利用这张大图来绘制到canvas形成一张动态的效果图呢?

之前我们提到过插入的任何元素都继承自commonElement类,Image是继承commonElement,我们针对gif图插入的功能专门有一个类GifImage,而它继承自Image。这个类里面有个最主要的函数:将大图中的每一部分一张张的循环绘制,具体代码如下:

 1         private drawGif() {
 2             if (this.element && this.context) {
 3                 var lastFrame = this.gifInfo.images[this.currentFrame % this.gifInfo.images.length]
 4                 var nowTime = this.tempNowTime || Date.now();
 5                 if (nowTime- this.lastDrawTime >= lastFrame.delay) {//控制绘制的速度
 6                     this.currentFrame++;
 7                     this.lastDrawTime = nowTime;
 8                 }
 9                 var frameNum = this.currentFrame % this.gifInfo.images.length;//计算是该绘制第几张图
10 
11                 this.context.save();
12                 this.rotate();
13                 //计算截取大图某一部分绘制到画布的其实坐标
14                 var atWidth = this.gifInfo.width * frameNum;
15                 var startx = atWidth % this.totalWidth;
16                 var starty = (atWidth / this.totalWidth | 0) * this.gifInfo.height;
17                 this.context.drawImage(this.element, startx, starty, this.gifInfo.width, this.gifInfo.height, this.config.translate.x, this.config.translate.y, this.config.width, this.config.height);
18                 this.context.restore();
19             }
20         }

免责声明:文章转载自《软件项目技术点(9)——如何将gif动态图拆分绘制》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇接口自动化测试的"开胃小菜"---简单黑客攻击手段Android Studio主要目录及文件简介下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

CrossApp简介

简介 CrossApp简介     CrossApp是一款完全免费、开源、跨平台的移动app开发引擎。CrossApp以C++作为开发语言,图形渲染基于OpenGL ES 2.0,采用MVC框架模式。使用CrossApp开发的应用程序支持导出到各大主流移动平台,真正实现“一次编码,多处运行”的跨平台开发技术。     CrossApp主要由9miao.c...

RFS自动化测试工具安装与使用总结

转载:http://blog.csdn.net/a5650892/article/details/77826021 一,调试1,在调试时,总时提示“无法打开浏览器”解决办法:1,把浏览器的代理关闭2,把浏览器的显示比例调到100%3,在IE设置的安全选项中,把启用保护模式开启 4,去除进入系统弹出的登录提示   在浏览器中---工具---internet选...

安卓系统手机目录

一、文件夹 1./acct/    系统回收站,删除的系统文件。     2./cache/    缓存     3./data/    用户的所有程序相关数据     app/    所有用户安装的apk文件     app-private/     data/    每一个应用的数据         com.xx.appname/    每一个应用的数...

DDOS hulk,rudy

HULK (HTTP Unbearable Load King) HULK HULK是另一个DOS攻击工具,这个工具使用UserAgent的伪造,来避免攻击检测,可以通过启动500线程对目标发起高频率HTTP GET FLOOD请求,牛逼的是每一次请求都是独立的,可以绕过服务端的缓存措施,让所有请求得到处理。HULK是用Python语言编写,对获得源码...

触屏手机3G网站设计

随着智能手机iphone和Android的热潮,衍生出基于Safari和Chrome浏览器的触屏手机网站Touch Screen Mobile Website。 触屏手机网站在中国还属于起步阶段,从行业的现状来看,其体验参差不齐,笔者将从设备、系统、浏览器、网络、四个维度来与大家共同探讨触屏手机的网站设计: 一、设备 ·分辨率与屏幕尺寸 ·触屏机的交互特点...

Android Fastboot 与 Recovery 和刷机 千山万水迷了鹿

1. 首先来看下Android系统的分区:   Android系统的分区.jpg   Android分区解释.png 安卓系统一般把rom芯片分成7个区,如果再加上内置sd卡这个分区,就是8个: hboot分区----------负责启动。 radio分区----------负责驱动。 recovery分区-------负责恢复。...