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. TX2 五种功耗模式

    工作模式介绍 Jetson TX2由一个GPU和一个CPU集群组成,CPU集群由双核丹佛2处理器和四核ARM Cortex-A57组成,通过高性能互连架构连接. 拥有6个CPU核心和一个GPU,您可以 ...

  2. 从零开始完整搭建 Spring-Boot 项目开发框架的教程

    前言 难度:简单 类型:step-by-step 适用:初学者,完全没有接触过 Spring-Boot 开发环境:jdk 1.8 关键词:java, sring-boot, spring-mvc, r ...

  3. centos的基本命令01

    01:创建用户命令 useradd liulaoshi # 创建用户 passwd liulaoshi # 设置密码, 回车后会提示让你输入两次密码 1.用户列表文件:/etc/passwd/ 2.用 ...

  4. ORM中的一对一和多对多

    ORM中的一对一和多对多 Django ORM  ORM 一对一 什么时候用一对一? 当 一张表的某一些字段查询的比较频繁,另外一些字段查询的不是特别频繁 把不怎么常用的字段 单独拿出来做成一张表 然 ...

  5. 进阶篇:2)DFMA方法的运用

    本章目的:DFMA方法运用,引导后面的章节.(运用比只理解重要!) 1.DFMA概述 1.1 DFMA的由来 工艺粗略可分为装配工艺和制造工艺.在这里,我们所讲的“制造”是指产品或部件的某个零件的制造 ...

  6. CentOS&.NET Core初试-2-安装.NET Core SDK和发布网站

    系列目录 CentOS的安装和网卡的配置 安装.NET Core SDK和发布网站 Nginx的安装和配置 安装守护服务(Supervisor) 安装.NET Core SDK 注册Microsoft ...

  7. 后缀自动机 && 题目

    因为明天要讲解后缀自动机了,所以只能抱抱佛脚,临时做做题目.其实很久以前看过,但是不太懂,看的是clj的原文,不太懂.现在只能临时看看是怎么弄的,应付下. ---------------------- ...

  8. ubuntu 18 常用软件安装

    主要内容 1.安装 Ubuntu 18.04 LTS 2.安装 Google Chrome 3.安装 OpenVPN Client 4.安装 Docker CE 5.安装 MySQL Server 转 ...

  9. reduce的用法

    在不增加变量的情况下,统计数组中各元素出现的次数. ```jsfunction countItem (arr) { // 写入你的代码}countItem(['a', 'b', 'a', 'c', ' ...

  10. Python对日期进行格式化

    Python对日期进行格式化 把当前时间输出为2017-04-07 19:00:00.进入python交互命令行输入: > import datetime > currtime = dat ...