1.前言
最近产品提了一个内部需求,为了产品的快速推广,需要给内部市场部门提供一个 H5 网页,内置多个海报背景,预留一个上传二维码的区域,方便给市场部门上传自定义的二维码并生成多种海报样式。
该需求还是比较简单的,在站内也能查到各种资料,但是万万没想到,就算照着抄作业都还能踩着坑。
2. 实现逻辑梳理:
- 构建页面,并将要渲染的
dom脱离文档流 - 获取像素比
dpr - 上传二维码图片并将图片转为
base64本地预览 - 使用
html2canvas将dom渲染到canvas - 利用像素比计算图片倍率,方便生成图片
- 生成图片并利用微信浏览器长按保存功能将图片渲染在页面
3. 代码实现
<div class="view">
<img src="" alt="" class="bg">
<img src="" alt="" class="qr-code">
</div>
<!-- 只允许上传图片格式文件 -->
<input type="file" class="file" accept="image/*">
// 先抄个作业
// 获取像素比
function DPR() {
if (window.devicePixelRatio && window.devicePixelRatio > 1) {
return window.devicePixelRatio;
}
return 1;
}
// 将传入值转为 10 进制整数
function parseValue(value) {
return parseInt(value, 10);
}
// 绘制 canvas
function drawCanvas(selector) {
// 获取想要转换的 DOM 节点
var dom = document.querySelector(selector);
var box = window.getComputedStyle(dom);
// DOM 节点计算后宽高
var width = parseValue(box.width);
var height = parseValue(box.height);
// 获取像素比
var scaleBy = DPR();
// 创建自定义 canvas 元素
var canvas = document.createElement('canvas');
// 设定 canvas 元素属性宽高为 DOM 节点宽高 * 像素比
canvas.width = width * scaleBy + 1;
canvas.height = height * scaleBy;
// 设定 canvas css宽高为 DOM 节点宽高
canvas.style.width = `${width * scaleBy + 1}px`;
canvas.style.height = `${height * scaleBy}px`;
// 背景透明,这里设置透明其实是无效的,需要在 options 里面单独配置
canvas.style.backgroundColor = 'rgba(0,0,0,0)';
// 获取画笔
var context = canvas.getContext('2d');
context.fillStyle = 'rgba(0,0,0,0)';
// 将所有绘制内容放大屏幕倍率
// 因为UI提供了海报图的不同倍率图片,这段代码就注释了
// context.scale(scaleBy, scaleBy);
// 将自定义 canvas 作为配置项传入,开始绘制
return html2canvas(dom, {
canvas
});
}
// 作业抄完
// 根据屏幕倍率渲染不同倍率的图片
document.querySelector('.bg').setAttribute('src', './image/pic_1x'+DPR()+'.png');
// 选择图片并本地预览功能
// file 标签选择二维码图片并填充到页面预留容器中
document.querySelector('.file').onchange = function() {
var file = this.files[0],
reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = (e) => {
var code = document.querySelector('.qr-code')
code.setAttribute('src', e.target.result);
// 重置 input file 因为有多个海报样式选择,这里是为了选择相同的文件再次触发 change 事件
this.val('');
}
}
// 给二维码容器添加 onload 事件,触发 drawCanvas 方法
document.querySelector('.qr-code').onchang = function() {
var canvas = drawCanvas('.view'),
wrap = document.querySelector('.view');
canvas.then((c) => {
// 调用 canvasAPI 的 toDataURL 方法,生成 base64
var base64 = c.toDataURL('image/png');
// 将容器内容替换为生成的图片
wrap.innerHTML = `<img src="${base64}" >`
})
}
遇到的问题
- 生成的海报图本来是圆角
png图片,但是居然有白边,图片不透明
// 解决方案
html2canvas(dom, {
useCORS: true,
allowTaint: false,
// canvas,
// 这里是生成 png 是否为透明的关键,
// 不然 html2canvas 会自动将背景设为白色,生成的 png 图片就会在透明的位置自带白色
backgroundColor: null,
})
4.参考资料
参考资料:
一次 H5 「保存页面为图片」 的踩坑之旅