解决使用html2canvas过程中的一些问题

以下基于v1.0.0-rc5版本(此版本已经解决了网络上一些常见的问题)

为了做一个类似分享海报的demo,方便起见(不会canvas)使用html2canvas将html转为canvas再分享为图片。但是在实际使用中发现了h2c的一些问题,记录在下。

demo说明

demo基于angular,通过填入数据,再将数据显示在对应位置,在需要时通过点击下载卡片或者其他形式将图片下载到本地。

项目地址:GitHub/JikeCard

演示链接:Jellow Card 项目被我暂停运行了

更多说明见项目README

图片效果如下:

jike-show-demo

以下将说明遇到的问题。

box-shadow样式导出后缺失

v1.0.0-rc2后h2c加入了导出box-shadow的功能,但是当你同时对一个元素设置了border-radiusbox-shadow后,导出会出现异常(及样式丢失)

例如:

1
2
3
4
5
6
7
.avatar {
display: flex;
width: 14%;
border-radius: 50%;
border: solid 4px white;
box-shadow: 0 0.2px 0.5px gainsboro;
}

其中的box-shadow: 0 0.2px 0.5px gainsboro;在导出结果中丢失。

html2canvas项目的pull1848下,你可以找到由wangkaiwen提出的解决办法
该方案对源码(node_modules/html2canvas/dist/html2canvas.js)做出如下修改(将对应源码做替换):

在line 6500 附近:

1
2
3
4
5
6
7
8
9
10
11
CanvasRenderer.prototype.mask = function (paths) {
var x=this.options.x,y=this.options.y;
this.ctx.beginPath();
this.ctx.moveTo(x, y);
this.ctx.lineTo(this.canvas.width+x, y);
this.ctx.lineTo(this.canvas.width+x, this.canvas.height+y);
this.ctx.lineTo(x, this.canvas.height+y);
this.ctx.lineTo(x, y);
this.formatPath(paths.slice(0).reverse());
this.ctx.closePath();
};

在line 6800附近:

1
_this.ctx.shadowOffsetX = shadow.offsetX.number + maskOffset*window.devicePixelRatio;

图片跨域问题

以下示例来自 html2canvas示例网页

HTML:

1
2
3
<div id="capture" style="padding: 10px; background: #f5da55">
<h4 style="color: #000; ">Hello world!</h4>
</div>

JavaScript:

1
2
3
html2canvas(document.querySelector("#capture")).then(canvas => {
document.body.appendChild(canvas)
});

在js中使useCROS=true即可,如:

1
2
3
4
5
html2canvas(document.querySelector("#capture"),{
useCROS: true
}).then(canvas => {
document.body.appendChild(canvas)
});

导出图片清晰度不高

v1.0.01-rc5版本中,不需要通过设置宽度、高度、scale来提高清晰度,使用上面的代码已经可以得到足够清晰的图片。

导出图片高度异常

此异常有两种情况:

  1. div高度过高时生成图片出现白边
  2. div在页面上方引起的上半部分丢失

div高度过高时生成图片出现白边

js加入如下参数:

1
2
3
4
5
6
html2canvas(document.querySelector("#capture"),{
useCROS: true,
height: document.getElementById('capture').scrollHeight
}).then(canvas => {
document.body.appendChild(canvas)
});

div在页面上方引起的上半部分丢失

曲线救国,在保存图片前将页面回复到最顶部,即:

1
2
3
4
5
6
7
8
document.documentElement.scrollTop = 0;
document.body.scrollTop = 0;
html2canvas(document.querySelector("#capture"),{
useCROS: true,
height: document.getElementById('capture').scrollHeight
}).then(canvas => {
document.body.appendChild(canvas)
});

可能存在更优解。

导出svg(二维码)错误

使用qrcode.js(实际安装的依赖为angularx-qrcode(1.7.0-beta5))生成二维码,因为图片格式清晰度不高,所以采用svg形式。但是svg导出会出现问题(具体说明问题视你的svg图案,总之就是会出现异常)。

异常由svg内未经设置的width引起(应该是,所以下面的解决方案不一定适合非二维码的svg),因此通过设置元素宽度来消除异常:

1
2
3
4
5
6
7
8
9
/**
* 设置元素宽度(消除异常)
*/
function setSvgElementsWidth() {
const svgElements = document.body.querySelectorAll('svg');
svgElements.forEach(item => {
item.setAttribute('width', item.getBoundingClientRect().width.toString());
});
}

在生成二维码前调用此方法即可消除异常。

在此值得一提的是,angularx-qrcode(1.7.0-b5)插件[usesvg]="true"时直接用html2canvas导出会引发错误,错误原因由旧版本不兼容es2015引起。插件2.x版本虽修复此问题,但因2.x版本去除了svg选项,导出二维码清晰度不高,故将 tsconfig.json的target 改为 es5。

另一点是,svg生成二维码后,二维码的点之间有间隙(像素视觉差异),可以在GitHub/JikeCard中的qrcode 组件找到对应的css和utils/QRCode.ts找到css异常的解决方法。


土豪将鼓励我继续创作和搬运!