背景
前几天,公司项目中有这么一个需求,说是上传图片给图片加水印的功能,我说那还不简单,因为公司图片存储采用的是七牛云,当然七牛云后台是可以设置图片的水印;
但是我一看需求,好家伙果真不简单,原来不是几个字,而是多个元素叠加在一起的;水印的数据也是动态的,而七牛云后台只能设置一些
静态属性或者图片地址,如果url地址
加一些参数的情况,效果也是不可观的
而通过url
地址拼接参数的情况:/image/<encodedkodocheme>
: 水印的源路径,目前支持 kodo 资源。kodo 资源可由 kodo://<bucketname>/<key>
表示(此时 bucketname 需要与输入源在同一区域),均需要经过 urlsafe_base64_encode。
注意:更换图片水印时,建议更换图片的文件名。
这样的情况目前只支持 kodo
的图片资源,如果使用这样的参数,那么我在上传前就要先上传动态数据的水盈图片,之后在上传要上传的图片;
虽然这样是可以做到,但是对于七牛云的存储容量和流量来讲,消耗是很大的; 所以这样的实现方式是不可取的;
水印效果图:

传统的canvas
也可以采用传统的 canvas 实现,但是将上传的问图片也是可以通过下面的方式转为url
,然后在通过生成canvas
画图功能将上传的图片文件生成
canvas。然后在通过文字的定位定位在图片的左下角;
但是这样的方式就会大量计算文本x, y
在 canvas 中的位置;因为数据是动态的,所以理论上是可以实现的,但是要考虑多行文本的问题; 所以这里不建议采用该方式;
使用 html2canvas
将html
元素组合
大致的思路就是:
- 先将要生成
canvas
的容器在页面可视区域不可见,这里就是将它定位出去的;
- 然后上传前,现将图片文件生成地址,赋值给
new Image()
, 在去监听加载完成; 之后就是将定位出去的元素样式化;
- 通过
html2canvas
将跟元素生成带水印的canvas
, 接下来就是对canvas
转为文件,从而实现水印图片的上传;
首先,在上传文件前先将图片文件转为对象地址,然后兼容图片的加载完成。
将之前定位出去的元素的图片赋值,设置跟元素的宽度和高度为图片的宽度和高度;
然后在通过html2canvas
插件将跟元素生成canvas
,在将canvas
转化为blob
, 接着new File
将blob
转成文件;
下面就是大概的思路
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| this.getDom("#file-input").addEventListener("change", function (e) { const file = e.target.files[0];
const tempImg = new Image(); tempImg.src = URL.createObjectURL(file); tempImg.addEventListener("load", function () {
const node = _this.getDom("#node"); const imgDom = _this.getDom("img");
imgDom.setAttribute("src", tempImg.src); node.style.width = tempImg.width + "px"; node.style.height = tempImg.height + "px";
html2canvas && html2canvas(node).then(function (canvas) { canvas.toBlob(blob => { const newFile = new File([blob], file.name, { type: file.type }); console.log(newFile);
let imgSrc = canvas.toDataURL("image/jpeg", 1); console.log(imgSrc); _this.getDom( ".temp-container" ).innerHTML = `<img class="show" style="width: ${tempImg.width}px; height: ${tempImg.height}px" src="${imgSrc}" alt="" />`; }); }); }); });
|
svg
代码片段
还有一种方式,就是将svg
代码片段,而这代码片段就是左下角的水印元素,将它生成 canvas,再将上传图片生成 canvas;然后俩张canvas
合并;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| let data = ` <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"> <foreignObject width="100%" height="100%"> <div xmlns="http://www.w3.org/1999/xhtml"> <div style="color: red">这里就是你渲染的元素</div> </div> </foreignObject> </svg> `;
let imgSvg = new Image(); let blob = new Blob([data], { type: "image/svg+xml;charset=utf-8" }); imgSvg.src = URL.createObjectURL(blob);
function img2Canvas(img) { const canvas = document.createElement("canvas"); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0);
imgSvg.onload = function () { ctx.drawImage(imgSvg, 20, canvas.height - 100 - 20); URL.revokeObjectURL(imgSvg.src); }; imgSvg.src = URL.createObjectURL(blob);
document.body.append(canvas);
return canvas; }
this.getDom("#file-input2").addEventListener("change", function (e) { const file = e.target.files[0]; let tempImg = new Image(); tempImg.src = URL.createObjectURL(file); tempImg.addEventListener("load", () => { img2Canvas(tempImg).toBlob(blob => { const newFile = new File([blob], file.name, { type: file.type }); console.log(newFile); }); }); });
|