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. 获取dataGridView双击时判断双击的是下面的行,还是列头

    private void dataGridView1_DoubleClick(object sender, EventArgs e) { Point hit = this.dataGridView1. ...

  2. HTML中title前面小图标和网站收藏现实的图标

    网站上的logo实际上是一个“favicon.ico”图片.实现步骤:第一步:制作favicon.ico,大小为16*16毫米:第二步:将“favicon.ico”放到项目的根路径下. 第三步:在所有 ...

  3. 高阶篇:6)设计评审Design reviews;

    本章目的:明白设计评审Design reviews的概念和流程,不犯常见错误. 1 设计评审概念: 所谓设计评审,是指对设计所作的正式的.综合性的和系统性的审查,并写成文件,以评定设计要求与设计能力是 ...

  4. 01-复杂度2 Maximum Subsequence Sum (25 分)

    Given a sequence of K integers { N​1​​, N​2​​, ..., N​K​​ }. A continuous subsequence is defined to ...

  5. 查看centos CPU、内存、版本等信息

    2018-12-29 查看当前linux的系统版本 cat /etc/redhat-release 查看内核版本 uname -a 查看CPU是32位还是64位 getconf LONG_BIT 查看 ...

  6. mysql数据类型 完整性约束 054

    创建用户和授权用户权限: # .创建用户: # 指定ip .109的fgf用户登录 '; # 指定ip .开头的fgf用户登录 '; # 指定任何ip的fgf用户登录 '; # .删除用户 drop ...

  7. 在win7系统设置SQL Server2014 express为远程数据

    如何设置远程访问到SQLserver服务器(局域网内的设置) 1.首先,使用Windows+R键 输入services.msc 打开本地服务. *说明:①MSSQLSERVER是正式使用的SQL创建实 ...

  8. java多线程-cas及atomic

    大纲: cas atomic 一.cas cas:compareAndSwap,一种乐观锁. cas思想:cas需要三个值,v是内存值,e是期望值,n是要修改的值.当内存中的值v等于预期值e(说明内存 ...

  9. 【测试的艺术】+测试分析&测试计划+模板

    一.项目概述 1.1.项目背景 #就是说一下为什么要做这个项目 1.2.项目目标 #这个项目最终要达到的目标是什么 二.项目整体分析 #项目分为哪些部分?各部分之间的关联是什么?各部分的目标是什么? ...

  10. 每一次要fix的pr

    1.TODO一定要加自己名字 2.写代码考虑别人的阅读,比如event这样很general的名字不要用,所以不用from sqlalchemy import event, 要用import sqlal ...