软件项目技术点(9)——如何将gif动态图拆分绘制
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的宽高:
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 所以就会出现多行的大图。
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 }
转载于:https://www.cnblogs.com/fangsmile/p/6273714.html
软件项目技术点(9)——如何将gif动态图拆分绘制相关推荐
- 软件项目技术点(21)——自动保存和恢复
AxeSlide软件项目梳理 canvas绘图系列知识点整理 自动保存的基本思路 1)软件每次打开都会创建一个保存画布元素信息的文件,文件名是在打开软件时生成的唯一字符串.可同时打开多个窗口,所以 ...
- 软件项目技术路线图_创建基本的项目路线图
软件项目技术路线图 Continuing from my previous article, at this checkpoint, I have two things with me: 上一篇文章的 ...
- 软件项目技术路线图_为您的项目创建路线图
软件项目技术路线图 Scrum has a tool for roadmap and release planning as well. We use two different mechanisms ...
- 软件项目技术点(4)——实现点击画布上元素
AxeSlide软件项目梳理 canvas绘图系列知识点整理 元素和影子 我们在主画布上绘制所有添加到画布上的元素,主画布上的所有元素存储在commonElements中. 在hitCanvas上 ...
- 软件项目技术点(1)——Tween算法及缓动效果
AxeSlide软件项目梳理 canvas绘图系列知识点整理 Tween算法及缓动效果 软件里在切换步序时需要有过渡动画效果,从当前位置的画面缓动到目标位置的画面.动画效果可重新查看文章系列第一篇 ...
- 软件项目技术点(25)——提升性能之检测绘制范围
AxeSlide软件项目梳理 canvas绘图系列知识点整理 软件里的一个画面包含很多个元素,但是当缩放到某个局部位置时,需要绘制的元素个数就很少.那么怎么判断某个元素是否需要进行绘制呢? 我们在 ...
- 软件项目技术点(7)——在canvas上绘制自定义图形
AxeSlide软件项目梳理 canvas绘图系列知识点整理 图形种类 目前我们软件可以绘制出来的形状有如下这几种,作为开发者我们一直想支持用户可以拖拽的类似word里面图形库,但目前还没有找到比 ...
- 软件项目技术点(20)——导出视频
AxeSlide软件项目梳理 canvas绘图系列知识点整理 导出的视频和播放器自动播放效果时一样的,这样用户就可以传到视频网站分享出去,或者mp4文件发送分享给朋友. 导出视频过程 我们导出视频 ...
- 软件项目技术点(19)——文件的保存和打开(解压缩)
AxeSlide软件项目梳理 canvas绘图系列知识点整理 保存文件 保存内容有哪些? 我们需要保存的内容信息 1)context.json 存储画布状态信息和所有元素的配置信息(这个文件在过程 ...
最新文章
- php远程下载头像,远程使用gravatar头像
- Apache Software Foundation Distribution Directory
- 查看python进程_[原创] 如何查看一个Python进程在”干什么”: py-spy 来帮忙 – 编码无悔 / Intent Focused...
- 1600k 打印头测试软件,更换LQ-1600K打印头断针的技巧
- 不缓存调用js的方法
- 自托管websocket和webapi部署云服务器域名及远程访问
- 苹果用户每天解锁iPhone手机80次
- 【2018蓝桥省赛A组C/C++】全球变暖(两次bfs+状态标记 or 一次dfs)
- ubuntu更新pip
- MATLAB简单实现ID3算法
- 透明这种颜色的html,透明颜色HTML5帆布
- 冷门游戏脚本开发软件-TC
- Chromedriver适用谷歌浏览器的各个版本
- 微信js-sdk引入!绝对干货
- PDF文件如何转成HTML格式?说一种思路
- 重学Android基础系列篇(五):Android虚拟机指令
- ssm毕设项目水果生鲜销售系统7826c(java+VUE+Mybatis+Maven+Mysql+sprnig)
- 一分钟了解Java Attach机制
- 2021年入职体检有哪些项目?
- 模态逻辑中的必然和可能
热门文章
- jsp文件第一行报错The superclass javax.servlet.http.HttpServlet was not found on the Jav
- 51单片机模拟交通信号灯控制系统设计
- 【毕业设计】爱琴海——基于HTML5的婚庆用品商城网页设计
- 源码分析 @angular/cdk 之 Portal 1
- 世界级压力传感器国际十大排名榜(干货)
- Springer出版社Word模板
- PyQt5实例 画板小程序
- Vue3.0 难产?Vue 最黑暗的一天
- 用友t3选择服务器无响应,用友T3用友通客户端访问服务器时响应很慢
- seaborn 0.9 文档翻译活动期待大家的参与~