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

背景介绍

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

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

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

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

1. 解析gif文件信息

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

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

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

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

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

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

2. 拼大图

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

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

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

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);
 require('gif')(path,(error, files: Array<string>) => {
if (error) {
Logger.setErrLog(LogCode.image, "文件:File,方法:node_modules-gif,异常信息:" + error);
callBack(null);
}
files.forEach((file, index) => {
var targetDir = FileSytem.imageTempDir + id + index + ".jpg"; FileSytem.copySync(file, targetDir); try
{
var tempImage = <HTMLImageElement>document.createElement("img");
var tempImageSrc = targetDir;
tempImage.id = index.toString();
tempImage.src = tempImageSrc;
tempImage.onload = (ev: Event) => {
try
{  //计算该张图片绘制到canvas上的位置
var atWidth = gifInfo.width * Number(tempImage.id);
var startx = atWidth % tempCavas.width;
var starty = (atWidth / tempCavas.width | 0) * gifInfo.height; cxt.drawImage(tempImage, 0, 0, gifInfo.width, gifInfo.height, startx, starty, gifInfo.width, gifInfo.height);
FileSytem.remove(tempImageSrc, null);
ev.target = null;
loadCounter++;
if (gifInfo.images.length == loadCounter) {
var dataBuffer = new Buffer(tempCavas.toDataURL("image/png").replace(/^data:image\/\w+;base64,/, ""), 'base64');
var dataPath = FileSytem.imageDir + id + ".png";
FileSytem.fileSaveSync(dataPath, dataBuffer);
callBack(dataPath, tempCavas.width);
tempCavas.width = 0;
tempCavas.height = 0;
}
}
catch (e) {
Logger.setErrLog(LogCode.image, "文件:File,方法:gifToPng_1,异常信息:" + e);
callBack(null);
}
}
}
catch (e) {
Logger.setErrLog(LogCode.image, "文件:File,方法:gifToPng_2,异常信息:" + e);
callBack(null);
}
});
});

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

3.将大图绘制到canvas

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

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

         private drawGif() {
if (this.element && this.context) {
var lastFrame = this.gifInfo.images[this.currentFrame % this.gifInfo.images.length]
var nowTime = this.tempNowTime || Date.now();
if (nowTime- this.lastDrawTime >= lastFrame.delay) {//控制绘制的速度
this.currentFrame++;
this.lastDrawTime = nowTime;
}
var frameNum = this.currentFrame % this.gifInfo.images.length;//计算是该绘制第几张图 this.context.save();
this.rotate();
//计算截取大图某一部分绘制到画布的其实坐标
var atWidth = this.gifInfo.width * frameNum;
var startx = atWidth % this.totalWidth;
var starty = (atWidth / this.totalWidth | 0) * this.gifInfo.height;
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);
this.context.restore();
}
}

软件项目技术点(9)——如何将gif动态图拆分绘制的更多相关文章

  1. 软件项目技术点(1)——Tween算法及缓动效果

    AxeSlide软件项目梳理   canvas绘图系列知识点整理 Tween算法及缓动效果 软件里在切换步序时需要有过渡动画效果,从当前位置的画面缓动到目标位置的画面.动画效果可重新查看文章系列第一篇 ...

  2. 软件项目技术点(1)——d3.interpolateZoom-在两个点之间平滑地缩放平移

    AxeSlide软件项目梳理   canvas绘图系列知识点整理 软件参考d3的知识点 我们在软件中主要用到d3.js的核心函数d3.interpolateZoom - 在两个点之间平滑地缩放平移.请 ...

  3. 软件项目技术点(5)——在canvas上绘制动态网格线

    AxeSlide软件项目梳理   canvas绘图系列知识点整理 grid类的实现 当鼠标在画布上缩放时,网格能跟着我的鼠标滚动而相应的有放大缩小的效果. 下面是具体实现的代码,draw函数里计算出大 ...

  4. 软件项目技术点(6)——结合鼠标操作绘制动态canvas画布

    AxeSlide软件项目梳理   canvas绘图系列知识点整理 我们创建一个类封装了所有鼠标需要处理的事件. export class MouseEventInfo { el: HTMLElemen ...

  5. 软件项目技术点(7)——在canvas上绘制自定义图形

    AxeSlide软件项目梳理   canvas绘图系列知识点整理 图形种类 目前我们软件可以绘制出来的形状有如下这几种,作为开发者我们一直想支持用户可以拖拽的类似word里面图形库,但目前还没有找到比 ...

  6. 软件项目技术点(2)——Canvas之平移translate、旋转rotate、缩放scale

    AxeSlide软件项目梳理   canvas绘图系列知识点整理 画布操作介绍 画布绘图的环境通过translate(),scale(),rotate(), setTransform()和transf ...

  7. 软件项目技术点(2)——Canvas之坐标系转换

    AxeSlide软件项目梳理   canvas绘图系列知识点整理 默认坐标系与当前坐标系 canvas中的坐标是从左上角开始的,x轴沿着水平方向(按像素)向右延伸,y轴沿垂直方向向下延伸.左上角坐标为 ...

  8. 软件项目技术点(2)——Canvas之获取Canvas当前坐标系矩阵

    AxeSlide软件项目梳理   canvas绘图系列知识点整理 前言 在我的另一篇博文 Canvas坐标系转换 中,我们知道了所有的平移缩放旋转操作都会影响到画布坐标系.那在我们对画布进行了一系列操 ...

  9. 软件项目技术点(8)—— canvas调用drawImage绘制图片

    AxeSlide软件项目梳理   canvas绘图系列知识点整理 html5中标签canvas,函数drawImage(): 使用drawImage()方法绘制图像.绘图环境提供了该方法的三个不同版本 ...

随机推荐

  1. andorid avd 下 使用 fiddler 抓包

                Fiddler 设置:  

  2. Maven web项目(简单的表单提交) 搭建(eclipse)

    我们将会搭建一个,基于Maven管理的,具有简单的表单提交功能的web项目,使用DAO--service--WEB三层结构,服务器使用Tomcat 1 项目基本结构的搭建 左上角File---> ...

  3. python之类与对象(1)

    面向对象编程是最有效的软件编写方法之一.编写类时,定义一群对象都有的通用行为.基于类创建对象时,每个对象都自动具备这种通用行为,然后可以根据需要赋予每个对象的独特的个性. 1. 类与对象的语法规范 关 ...

  4. springboot(九)-log配置

    spring项目放到tomcat中运行,我们可以在tomcat的logs文件夹下面生成log文件.那么我们的springboot项目没有放到系统安装的tomcat容器中,怎么设置生成log文件呢? 有 ...

  5. 认识CSS中高级技巧之元素的显示与隐藏

    前端之HTML,CSS(八) CSS高级技巧 元素的显示与隐藏 CSS中有三个属性可以设置元素的显示于隐藏,分别是:display.visibility和overflow. display 隐藏元素: ...

  6. excel时间戳转化为标准日期(日期转化为日期戳)

    最近在学习python将数据导入到excel,发现日期变成数字而不是日期格式的问题. 第一眼看去肯定是excel单元格格式问题,一般excel单元格格式为常规,而常规处理日期时就显示为数字,所以就想到 ...

  7. WordCount C语言实现求文本的字符数,单词数,行数

    1.码云地址: https://gitee.com/miaomiaobobo/WordCount 2.psp表格 PSP2.1表格 PSP2.1 PSP阶段 预估耗时 (分钟) 实际耗时 (分钟) P ...

  8. spring cloud知识点

    eureka注册中心(zookeeper,eureka) 也要集群,可以相互注册,网状结构.后面很多高可用的服务都是用这种方式.Eureka的客户端缓存技术 spring boot actuator ...

  9. JWT(Json Web Token)初探与实践

    前言什么是JWT?为什么使用JWT?什么时候使用JWT?JWT的基本结构HeaderPayloadSignature将他们放在一起项目实践JWT后端前端关于安全性总结参考 协议标准:https://t ...

  10. Case When ELSE END语句

    一.简介.Case  When   ELSE   END共有两种用法: 说实话,这种就是数据库版的switch语句,但是只是形式上很像,实际上还是有差别的!!! Create Table Test6( ...