对于很多的营销活动,需要提供给用户一张可下载二维码,但是二维码要组合一些特定的业务信息,然后便于用户分享、传播,达到营销效果。这一类的图片下载有一个特点,二维码里蕴含了用户信息,所以是每个人的二维码是不一样的,而业务背景图都是一样的,而且用户下载的最终图片样式是可以确定的,这时候前端则需要自己的能力组合两张图,下载给用户。
有的网站信息内容专利、防窃取的要求,为了让用户下载分享的内容不被随意对外分享,则需要将用户下载的图片上盖上一层署名信息,从而防止有人下载图片后冒充作者,侵犯作者权益。
解决方案的前提是利用客户端的能力,纯前端来解决。作为前端开发者,对于画图来说,Canvas能力是完全足够的,2D的、3D的都是可以完成,我这里的解决方案就是利用Canvas来做的。首先要利用canvas画图能力,前提两张图要合并的相对的位置,大小需要先知道,这需要你先跟视觉同学沟通好,实际涉及到的Canvas的api有如下这三个getContext、drawImage、toDataURL,下面来认识一下这三个API。
var ctx = canvas.getContext(contextType, contextAttributes);
HTMLCanvasElement.getContext() 方法会返回当前canvas的上下文,注意是HTMLCanvasElement元素的方法,可以是页面上的canvas标签,也可以是javascript的createElement方法创建的元素。
看一下getContext方法的参数,contextType是想要在画布上绘制的类型,可以选的值有2d,webgl,webgl2等。
参数值 |
含义 |
2d | 创建 CanvasRenderingContext2D 二维渲染上下文,像图片这种二维空间的选这个类型。 |
webgl/experimental-webgl | 创建 WebGLRenderingContext 三维渲染上下文对象,适用于三维动画制作开发。 |
webgl2/experimental-webgl2 | 创建一个 WebGL2RenderingContext 三维渲染上下文对象,webgl的升级版本。 |
bitmaprenderer | 将创建将canvas内容替换为指定ImageBitmap功能的ImageBitmapRenderingContext,canvas与位图的生成,笔者也没接触过。 |
drawImage方法是上面getContext获取的上下文里的方法,但是它只在contextType为2d的时候才可以被调用,也就是只能在二维图片的时候使用,可以根据其调用入参控制进行画图、组合图、剪切图。
void ctx.drawImage(image, x, y);
在画布上将image图像定位到x坐标长度与y坐标长度,画上去的图片大小就是图片本身的大小,不能控制。
void ctx.drawImage(image, x, y, width, height);
在画布上将image图像定位到x坐标长度与y坐标长度,并且指定image图像宽与高。
void ctx.drawImage(image, x, y, width, height, dx, dy, dWidth, dHeight);
在画布上将image图像定位到x坐标长度与y坐标长度,并且指定image图像宽与高,然后截取出来,放在(dx,dy)的坐标处,并且指定宽高,感觉可以很快做个文字放大镜。
canvas.toDataURL(type, encoderOptions);
toDataURL方法也是HTMLCanvasElement元素提供的方法,当调用HTMLCanvasElement.toDataURL() 方法时,会返回一个包含图片展示的data URL,像这样的格式data:[
该方法可以接受两个参数。
type: 生成dataURL类型,默认是image/png的的类型,如果需要生成jpg或者其他类型传入即可。
encoderOptions:可选参数,生成的dataURL质量参数,默认是0.92,可以传入0-1之间的小数,越大质量越高,生成的串信息也越多,越长。
先生成一块canvas画布,并且把第一张图,一般是背景图画进画布里。这里要注意,drawImage方法的第一个参数是HTMLImageElement元素,你可以通过document.querySelector获取页面上img标签元素,也可以通过createElement('img')去生成,取决于你的图是否在页面上展示着。
// html
// javascript
class Canvas {
// 创建canvas
constructor (config = {}) {
this.canvas = document.createElement('canvas');
this.canvas.width = config.bgWidth;
this.canvas.height = config.bgHeight;
this.ctx = this.canvas.getContext('2d');
}
// 画图到画布上
async run(config) {
// images是HTMLImageElement元素,而非链接地址
const image = await getImage(config.src);
this.ctx.drawImage(image, config.x, config.y, config.width, config.height);
}
// 验证使用,将生产的图呈现到页面上
print() {
document.querySelector('#img').setAttribute('src', this.canvas.toDataURL());
}
}
// 调用
const mycanvas = new Canvas({
bgWidth: 500,
bgHeight: 600
})
mycanvas.run({
src: 'https://n.sinaimg.cn/ent/transform/460/w630h630/20180824/Zaob-hicsiaw3749625.jpg',
x: 0,
y: 0,
width: 250,
height: 600
}).then(() => {
// 画到页面上
mycanvas.print()
})
在同一个ctx上进行两次将两张图进行合并drawImage,进行合并完成。
// html
// javascript
// canvas合成操作
class Canvas {
...
async run2(img1, img2) {
const images = [img1, img2];
const imgSrcs = images.map(({ src }) => getImage(src));
const imgEles = await Promise.all(imgSrcs);
imgEles.map((ele, i) => {
if (ele) {
const { x = 0, y = 0, width = 0, height = 0 } = images[i];
this.ctx.drawImage(ele, x, y, width, height);
}
});
}
...
}
// 调用
const mycanvas = new Canvas({
bgWidth: 500,
bgHeight: 600
})
mycanvas.run({
src: 'https://n.sinaimg.cn/ent/transform/460/w630h630/20180824/Zaob-hicsiaw3749625.jpg',
x: 0,
y: 0,
width: 250,
height: 600
}, {
src: 'https://n.sinaimg.cn/ent/transform/460/w630h630/20180824/Zaob-hicsiaw3749625.jpg',
x: 250,
y: 0,
width: 250,
height: 600
}).then(() => {
// 画到页面上
mycanvas.print()
})
class Canvas {
...
download() {
// 文件流式下载
this.canvas.toBlob(blob => {
const a = document.createElement('a');
a.download = 'image.png';
a.style.display = 'none';
a.href = URL.createObjectURL(blob);
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
}
...
}
目前我已经将上述组合图片的功能做成了开源package,上传至npm源上,如果有有需要的可以使用以下命令直接使用。
npm包地址
https://www.npmjs.com/package/composite-image
issue地址
https://github.com/FantasyGao/composite-image/issues
// 直接安装使用
npm/cnpm i --save composite-image
// 也可以在html文件直接引入
欢迎各位使用与提一些建议,感谢。