本次技术调研来源于H5项目中的一个重要功能需求:实现微信长按网页保存为截图
。css
这里有个栗子(请用微信打开,长按图片便可保存):3分钟探索你的知识边界html
将整个网页保存为图片是一个十分有趣的功能,常见于H5活动页的结尾页分享。如下则是项目中调研和踩坑的一些小结和汇总。html5
现有已知可以实现网页保存为图片的方案包括:git
data URI
html2canvas.js
实现(可选搭配Canvas2Image.js
实现网页保存为图片)rasterizeHTML.js
实现方案1:须要手动计算每一个DOM元素的Computed Style
,而后须要计算好元素在canvas的大小位置等属性。github
方案1难点
:web
小结
: html2canvas是目前实现网页保存为图片功能的综合最佳选择。chrome
官方GitHub:https://github.com/niklasvh/h...canvas
如下描述针对html2canvas版本是0.5.0-beta4
segmentfault
基于html2canvas.js
可将一个元素渲染为canvas,只须要简单的调用html2canvas(element[, options]);
便可。下列html2canvas
方法会返回一个包含有<canvas>
元素的promise
:跨域
html2canvas(document.body).then(function(canvas) { document.body.appendChild(canvas); });
上一步生成的canvas即为包含目标元素的<canvas>
元素对象。实现保存图片的目标只须要将canvas转image便可。
这里的转换方案有2种
:
toDataURL
方法将canvas输出为data: URI
类型的图片地址,再将该图片地址赋值给<image>
元素的src属性便可Canvas2Image.js
,调用其convertToImage
方法便可(GitHub)实际上,Canvas2Image.js
也是基于canvas.toDataURL
的封装,相比原生的canvas API对于转为图片的功能上考虑更为具体(未压缩的包大小为7.4KB),适合项目使用。
最终图片的清晰度
取决于
第一步中html转换成的canvas的清晰度。
现有解决方案参考;
其基本原理为:
将canvas
的属性width
和height
属性放大为2倍(或者设置为devicePixelRatio
倍),最后将canvas的CSS样式width和height设置为原先1倍的大小。
例如:但愿在html中实际显示的<canvas>
宽高分别为160px
,90px
则可做以下设置
<canvas width="320" height="180" style="width:160px;height:90px;"></canvas>
参考上述文档具体的使用案例以下;
convert2canvas() { var shareContent = YourTargetElem; var width = shareContent.offsetWidth; var height = shareContent.offsetHeight; var canvas = document.createElement("canvas"); var scale = 2; canvas.width = width * scale; canvas.height = height * scale; canvas.getContext("2d").scale(scale, scale); var opts = { scale: scale, canvas: canvas, logging: true, width: width, height: height }; html2canvas(shareContent, opts).then(function (canvas) { var context = canvas.getContext('2d'); var img = Canvas2Image.convertToImage(canvas, canvas.width, canvas.height); document.body.appendChild(img); $(img).css({ "width": canvas.width / 2 + "px", "height": canvas.height / 2 + "px", }) }); }
上述设置能够解决一般状况下图片不清晰的问题,不过探索并无结束。
实际在咱们的项目中,即便做出2.1节
的设置后,大果粒通常的渲染结果依然尴尬。
下面直接给出3条进一步的优化策略:
百分比布局
为px布局
(若是原先是百分比布局的话)关闭
canvas默认的抗锯齿设
置width
和height
为素材原有宽高,而后经过transform: scale
进行缩放。这里scale
的数值由具体需求决定。基本原理
px为单位
的宽高,避免样式二次计算致使的模糊关闭抗锯齿
来实现图像的锐化(MDN: imageSmoothingEnabled )css样式
的scale
来实现一样的缩放例: html2canvas配置
convert2canvas() { var cntElem = $('#j-sec-end')[0]; var shareContent = cntElem;//须要截图的包裹的(原生的)DOM 对象 var width = shareContent.offsetWidth; //获取dom 宽度 var height = shareContent.offsetHeight; //获取dom 高度 var canvas = document.createElement("canvas"); //建立一个canvas节点 var scale = 2; //定义任意放大倍数 支持小数 canvas.width = width * scale; //定义canvas 宽度 * 缩放 canvas.height = height * scale; //定义canvas高度 *缩放 canvas.getContext("2d").scale(scale, scale); //获取context,设置scale var opts = { scale: scale, // 添加的scale 参数 canvas: canvas, //自定义 canvas // logging: true, //日志开关,便于查看html2canvas的内部执行流程 width: width, //dom 原始宽度 height: height, useCORS: true // 【重要】开启跨域配置 }; html2canvas(shareContent, opts).then(function (canvas) { var context = canvas.getContext('2d'); // 【重要】关闭抗锯齿 context.mozImageSmoothingEnabled = false; context.webkitImageSmoothingEnabled = false; context.msImageSmoothingEnabled = false; context.imageSmoothingEnabled = false; // 【重要】默认转化的格式为png,也可设置为其余格式 var img = Canvas2Image.convertToJPEG(canvas, canvas.width, canvas.height); document.body.appendChild(img); $(img).css({ "width": canvas.width / 2 + "px", "height": canvas.height / 2 + "px", }).addClass('f-full'); }); }
例: DOM元素样式:
.targetElem {width: 54px;height: 142px;margin-top:2px;margin-left:17px;transform: scale(0.5)}
因为canvas对于图片资源的同源限制
,若是画布中包含跨域的图片资源则会污染画布,形成生成图片样式混乱或者html2canvas方法不执行等问题。
如下主要解决两类跨域的图片资源:包括已配置过CORS的CDN
中的图片资源和微信用户头像
图片资源。
CORS
。CDN
配置好后,经过chrome开发者工具能够看到响应头中应含有Access-Control-Allow-Origin
的字段。html2canvas
的useCORS
配置项。即做以下设置:
var opts = {useCORS: true}; html2canvas(element, opts);
注意
:
若是没有开启html2canvas
的useCORS
配置项,html2canvas
会正常执行且不会报错,可是不会输出对应的CDN图片
(已测试同时包含CDN的图片
和本地图片
的资源的页面,可是只有本地图片
可以被正常渲染出来)
若是须要将微信
平台中的用户头像一并保存为图片,3.1
的方案无能为力。可经过配置服务端代理转发
(forward)实现,此处不赘述。
微信中,唤出长按保存图片
的菜单要求长按的对象直接是<image>
元素,若是<image>
元素上方存在遮挡,则不会唤出菜单。
而事实上,引起遮挡的并不仅是非<image>
元素,还多是margin
属性。例如:若在页面底部,对一个绝对定位的元素设置了数值很大的margin-top
,则margin-top
所涉及的区域,均没法长按唤出菜单。解决方案:将margin-top
改用为top
便可。
canvas2img
默认保存图片的格式为png
,而在安卓版微信中所生成的图片尽管能长按唤出保存图片的菜单,可是没法正确保存到本地相册。 解决方案
:设置canvas2img
的生成图片格式配置项为jpeg
便可。
设置canvas2img
输出格式为jpeg
,会有必定概率致使生成的图片包含大量的黑色块。可能的解决方案
:缩减部分图片元素的体积和尺寸大小。
在图片的转化前
,必须中止
或者删除动效后才能正确渲染出图片,不然生成的图片是破裂的。