开拓:怎么复用呢?

于是便有了下面在浏览器端考试测验dom转图片的两种方案:

html2canvas

php长图生成web及时长图实践 Java

html2canvas一个在浏览器端通过JS对全体或部分页面进行“截屏”的库。

html2canvas利用方法大略,截屏的核心代码如下:

let imgBase64;

html2canvas(htm,{

onrendered : function(canvas){

//天生base64图片数据

imgBase64 = canvas.toDataURL();

});

利用大略,但是坑不少,碰着的坑及办理方案:

1.截图模糊

紧张办理思路:

1)将canvas的width和height属性放大为2倍。

2)将canvas的CSS样式width和height设置为原来1倍的大小。

<canvas width=\"大众200\"大众 height=\"大众100\"大众 style=\"大众width:100px;height:50px;\"大众></canvas>

2.截图不全

源码获取dom高度不准确,修正源码,获取高度夹帐动传,修正办法如下:

源码:

return renderDocument(node.ownerDocument, options, node.ownerDocument.defaultView.innerWidth, node.ownerDocument.defaultView.innerHeight, index).then(function(canvas) {

if (typeof(options.onrendered) === \公众function\"大众) {

log(\公众options.onrendered is deprecated, html2canvas returns a Promise containing the canvas\"大众);

options.onrendered(canvas);

}

return canvas;

});

修正后

//添加自定义高度宽度

var width = options.width != null ? options.width : node.ownerDocument.defaultView.innerWidth;

var height = options.height != null ? options.height : node.ownerDocument.defaultView.innerHeight;

return renderDocument(node.ownerDocument, options, width, height, index).then(function (canvas) {

if (typeof(options.onrendered) === \"大众function\公众) {

log(\"大众options.onrendered is deprecated, html2canvas returns a Promise containing the canvas\公众);

options.onrendered(canvas);

}

return canvas;

});

3.截图慢

截图慢得从html2canvas的事理提及,html2canvas并不是真正的截图,而是遍历加载的页面DOM,网络所有元素的信息,然后基于从DOM读取的属性利用canvas来绘制。

基于这个截图事理,慢的问题优化空间不大,而且html2canvas还有些CSS的限定,它只能精确地呈现它支持的CSS属性,完全的CSS属性支持列表,可以在官网查看。

关于慢,最大略的办理方案是在用户操作条件前天生截图。

4.crash

html2canvas截图后,将图片的base64传到客户真个分享组件,当base64超过500k可能导致客户端卡去世或crash,如果慢的问题还能忍,那这个问题是真的没法接管的。

svg

除了html2canvas网上也有更轻量更快的库,这些库是基于svg的,考试测验了下确实比html2canvas快很多。

svg方案的考试测验:

//要转成图片的dom

let htm = '<svg xmlns=\公众http://www.w3.org/2000/svg\公众 width=\"大众100%\公众 height=\"大众auto\公众><foreignObject width=\"大众100%\"大众 height=\"大众100%\公众><div xmlns=\公众http://www.w3.org/1999/xhtml\"大众><div>这里是页面内容...</div></div></foreignObject></svg>';

let DOMURL = window.URL || window.webkitURL || window;

let canvas = document.createElement('canvas');

let ctx = canvas.getContext('2d');

let img = new Image();

let svg = new Blob([htm], {type: 'image/svg+xml;charset=utf-8'});

let url = DOMURL.createObjectURL(svg);

let imgBase64;

img.onload = function () {

ctx.drawImage(img, 0, 0);

imgBase64 = canvas.toDataURL();

}

img.src = url;

svg方案没法绕过的坑:

1.ios下不支持跨域图片

由于安全限定,ios下跨域图片加crossOrigin属性也没法绕过跨域问题。

2.crash

和html2canvas一样,svg转图片后终极也是转base64传分享组件,base64超过500K可能导致的卡去世和crash问题也存在。

做事器端实现方案

开拓:浏览器真个方案crash问题不能忍,不如在做事器端天生图片,传图片URL到分享组件?

本着最大限度复用代码的初衷,首选了无头浏览器phantomjs截图的方案。

PhantomJS

PhantomJS是基于WebKit内核的无头浏览器,供应浏览器环境的命令行接口,我们可以进行网页截图、抓取网页数据等操作,更多详情可以去PhantomJS官网查看。

安装PhantomJS时,把稳安装以下依赖:

sudo yum -y install gcc gcc-c++ make flex bison gperf ruby openssl-devel freetype

做事器端方案选择的是phantomjs-node库,实现截图的核心代码如下:

var sitepage = null;

var phInstance = null;

phantom.create()

.then(instance => {

phInstance = instance;

return instance.createPage();

})

.then(page => {

let htm = [

'<!DOCTYPE html>',

'<html>',

'<head>',

'<meta charset=\"大众utf-8\"大众>',

'</head>',

'<body style=\公众background:#fff\公众>',

'<div>'+ new Date() +'</div>',

'</body>',

'</html>'

].join(\"大众\"大众);

page.property('content',htm);

page.render('./test.png').then((err) => {

phInstance.exit()

}).catch(err => {

phInstance.exit();

})

})

.catch(error => {

phInstance.exit();

});

PhantomJS碰着的坑也不少,紧张是环境问题:

1.没截图天生

开拓:在mac上和windows上天生截图正常,支配到测试环境后不能天生截图,打印PhantomJS日志,没有明确的报错信息。
linux下权限问题?

查看PhantomJS和目录权限,PhantomJS没有写权限,修复权限问题,图片仍旧不能天生。

开拓:字母命名的截图正常天生,不支持图片文件名包含数字?

一番验证,截图名包含数字phantomjs-node不能正常天生图片文件。

2.截图空缺

开拓:颜色和图案均能够渲染到截图中,只有笔墨不能渲染,字体有问题?

确认测试机中字体目录为空,更新字体,笔墨终于能正常渲染到截图中。

3.截图模糊

又是模糊问题…

css利用相对rem单位,PhantomJS截图是设置缩放参数:

//css

html{font-size: 100px;}

.owner_avatar{width:.30rem;height: .30rem;border-radius: .30rem;margin-right: .10rem;}

.events_img{width: .50rem;height:.50rem;}

//phantomjs缩放处理

page.property('viewportSize',{width:828,height:736});

page.property('zoomFactor',2)

page.property('content',htm);

.截图加载慢

模糊问题设置2倍图后,图片大小暴涨到6M+,导致加载慢,设置截图质量:

page.render(fileName,{quality:85}).then((err) => {

phInstance.exit();

})

5.截图慢

PhantomJS天生一个最大略的截图,耗时2S旁边,这个速率显然是不能接管的,暂时没找到比较好的优化办法。

node canvas

node canvas扩展了canvas API以供应与节点的接口,例如流式传输PNG数据,转换为Buffer实例等,更多先容可以去node canvas官网查看。

node canvas的环境搭建比较麻烦,依赖库与PhantomJS类似,这里就不列举了。

绘制图片的核心代码:

const { createCanvas, loadImage } = require('canvas');

const canvas = createCanvas(200, 200);

const ctx = canvas.getContext('2d');

ctx.font = '30px';

ctx.fillText('test', 50, 100);

loadImage('test.jpg').then((image) => {

ctx.drawImage(image, 0, 0, 70, 70);

})

node canvas与下面imagemagick的方案比拟,imagemagick的性能更好,node canvas没再深入只实现了大略demo,踩坑不多。

ImageMagick 与 GraphicsMagick

ImageMagick是一套功能强大、稳定而且免费的工具集和开拓包,可以用来读、写和处理超过90种的图片文件,包括盛行的TIFF、JPEG、GIF、 PNG、PDF以及PhotoCD等格式。

ImageMagick可以根据web运用程序的须要动态天生图片, 还可以对一个(或一组)图片进行改变大小、旋转、锐化、逊色或增加殊效等操作,并将操作的结果以相同格式或其它格式保存,对图片的操作,即可以通过命令行进行,也可以用C/C++、Perl、Java、PHP、Python或Ruby编程来完成。
更多详情可在ImageMagick官网查看。

GraphicsMagick是从 ImageMagick 5.5.2 分支出来的,听说它变得更稳定和精良,更多详情可在GraphicsMagick官网查看。

看起来GraphicsMagick是更好的选择,但是由于node gm这个库没有实现GraphicsMagick的半透明和圆角支持,而且针对专辑的大事宜长图做了一些性能比拟两者差异不大,以是选择利用ImageMagick。

node gm切换ImageMagick的办法非常大略,只要加以下设置:

var gm = require('gm');

var imageMagick = gm.subClass({ imageMagick: true });

不可避免的,利用ImageMagick也碰着一些坑:

1.半透明遮罩

设计:专辑封面背景利用白透明遮罩,遮罩的颜色根据封面图来定,深色封面图用白色笔墨,浅色封面图用玄色笔墨。

开拓:OK,先canvas获取封面图颜色信息,再判断颜色深浅

//RGB与YUV互转,Y>=128 为浅色Y'= 0.299R' + 0.587G' + 0.114B'U'= -0.147R' - 0.289G' + 0.436B' = 0.492(B'- Y')V'= 0.615R' - 0.515G' - 0.100B' = 0.877(R'- Y')R' = Y' + 1.140V'G' = Y' - 0.394U' - 0.581V'B' = Y' + 2.032U'//ImageMagick设置透明色.fill(\"大众rgba(0,0,0,.5)\"大众)

2.头像圆角

设计:这些头像要用圆角哦。

开拓:OK(还好ImageMagick支持圆角)

.fill(\"大众avatar.jpg\"大众).drawCircle(80,120,30,120)

ImageMagick圆角图片实现办法与canvas类似,画一个圆,然后用头像图片去添补来实现头像圆角。

3.昵称emoji表情

ImageMagick绘制昵称中的表情图比较麻烦,利用支持emoji的字体,考试测验过Twitter的彩色emoji字体,但是ImageMagick有BUG,不能还原为彩色的。

终极办理方案:

1)利用等宽字体,方便打算精确的emoji位置

2)ImageMagick绘制昵称中的表情图片

.draw(\"大众image Over \公众 + size + \"大众 \"大众 + url)

ImageMagick性能优化:

优化前:

优化后:

ImageMagick天生单张图片耗时100ms旁边,但是并发要求多了均匀耗时就暴涨到3S+,这个速率显然是不能接管的,经由一番优化后将均匀耗时降到1S旁边,紧张优化点如下:

1.gm代码拼接,VM中实行

多次调用gm多次操作图片,严重影响性能,将图片操作代码拼接成字符串,在VM中实行,只调用一次gm,核心代码如下:

let sandbox = { gm : imageMagick, start : Date.now()}//打算图片高度let offset = getOffset();let qrcodeStr = getQrcodeStr();let titleStr = (function(){ return [ '.fontSize(24)', '.fill(\"大众gray\"大众)', '.drawText(164,152,\公众我是标题\"大众)' ];})();let str = 'gm(828,'+ offset.height +',\"大众#fff\"大众).font(\"大众'+ FONTS +'\公众,48)'+ titleStr + qrcodeStr +'.quality(90).write(\"大众test.jpg\公众,function(err){console.log(err || Date.now() - start)})';let script = new vm.Script(str);let context = vm.createContext(sandbox);script.runInContext(context);

2.mpc格式

mpc是ImageMagick供应的一种持久高速缓存格式,减少对图像格式进行解码和编码像素的开销。

mpc天生两个文件:

1)一个扩展名.mpc保留了与图像或图像序列干系的所有属性(例如宽度,高度,色彩空间等)。

2)一个扩展名.cache,是本地原始格式的像素缓存。

读取mpc图像文件时,ImageMagick读取图像属性,并将内存映射到磁盘上的像素缓存,无需解码图像像素,不过mpc的文件大小比其他图像格式大。

mpc图像文件适用于一次写入,多次读取模式,利用mpc将图像直接映射到内存,而不是每次重新读取和解压源图像。

3.Q8版本

ImageMagick Q16版本许可在不缩放的情形下读写16位图像,但像素缓存花费的资源是Q8版本的两倍,Q8版本的实行速率常日比Q16版本要快。

像素缓存花费 = 宽度高度位深度/ 8 通道Q8位深 = 8Q16位深 = 16通道 = 红 + 绿 + 蓝 + 阿尔法强度

更详细的性能优化信息可在ImageMagick Architecture查看

引用

[1]、html2canvas (https://html2canvas.hertzen.com/features)

[2]、PhantomJS官网 (http://phantomjs.org/)

[3]、phantomjs-node (https://github.com/amir20/phantomjs-node)

[4]、node canvas官网 (https://github.com/Automattic/node-canvas)

[5]、node gm (http://aheckmann.github.io/gm/docs.html)

[6]、Twitter的彩色emoji字体 (https://github.com/eosrei/twemoji-color-font)

[7]、ImageMagick Architecture (http://www.imagemagick.org/script/architecture.php)