本文正在参与「金石方案 . 分割6万现金大奖」
写在开头
哈喽,各位倔友们又见面了,本章咱们继续来共享一个实用小技巧,给图片加水印功用,水印功用的目的是为了保护网站或作者版权,防止内容被他人运用或白嫖。
可是网络中,是没有绝对安全的,咱们只能尽可能去完善安全机制,像水印功用也只能是防君子,防不了小人。
下面小编画了一张增加水印的简易流程图:
制作图片
接下来,进入文章主题,既然是要给图片增加水印,那么咱们先来把图片制作到 canvas
上,详细如下:
<template>
<div>
<input type="file" @change="upload" /><br /><br />
<canvas id="canvas" />
</div>
</template>
<script>
export default {
methods: {
upload(e) {
const file = e.target.files[0];
if (!file) return;
const filePath = window.URL.createObjectURL(file); // 创建文件的暂时途径: blob:http://localhost:8081/0cd115e2-9d4a-4c67-a86b-77e84d6f61db
const img = new Image();
img.src = filePath;
img.onload = () => {
this.addWaterMark(img);
}
},
addWaterMark(img) {
// const canvas = document.createElement('canvas');
const canvas = document.getElementById('canvas');
const imgWidth = img.width;
const imgHeight = img.height;
canvas.width = imgWidth;
canvas.height = imgHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0); // 制作图片
}
},
};
</script>
<style scoped>
#canvas {
border: 1px solid red;
}
</style>
整体代码不难,为了便利演示,小编直接把 canvas
放在 template
中,但真实运用场景下你能够运用 document.createElement('canvas')
来创建 Dom
并在运用完毕后删除相关 DOM
,这样才是比较好的办法唷。(✪✪)
还有便是运用 ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) API
来制作图片。
作为一名前端人员小编期望你对 canvas 多多少少要有一点了解哦,不能说通晓,可是基础知识咱们要把握哦。
制作水印
把图片制作到 canvas
后,接下来咱们来把水印也给整上。
<script>
export default {
methods: {
upload(e) { ... },
addWaterMark(img) {
const canvas = document.getElementById('canvas');
const imgWidth = img.width;
const imgHeight = img.height;
canvas.width = imgWidth;
canvas.height = imgHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
// 画笔款式
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.font = '12px Microsoft Yahei';
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
ctx.fillText('橙或人', 0, 0);
ctx.fillText('2022年11月22日 09:22:30', 0, 20);
}
},
};
</script>
上图左上角能看到咱们很简单就把水印加上了,当然,这还达不到产品司理的要求,咱们需要把水印平铺开来,防止他人轻易经过截图就把水印清除了。
制作平铺水印
而这个平铺进程也很简单,只需循环去改动 ctx.fillText(text, x, y);
的 x
和 y
就行了,且来看看小编是如何来做的:
<script>
export default {
methods: {
upload(e) { ... },
addWaterMark(img) {
const canvas = document.getElementById('canvas');
const imgWidth = img.width;
const imgHeight = img.height;
canvas.width = imgWidth;
canvas.height = imgHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
// 画笔款式
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.font = '12px Microsoft Yahei';
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
// ctx.fillText('橙或人', 0, 0);
// ctx.fillText('2022年11月22日 09:22:30', 0, 20);
// 平铺水印
const name = '橙或人';
const date = '2022年11月22日 09:22:30';
const height = 120;
const width = 200;
let i = 0;
let j = 0;
const waterMarkerWidth = ctx.measureText(name).width;
ctx.rotate(-20 * Math.PI / 180);
for (i = 0; i <= imgWidth / (waterMarkerWidth) + 100; i++) {
for (j = 0; j <= imgHeight / (height - 20) + 100; j++) {
const x = i * (waterMarkerWidth + width) - 100;
if (j === 0) {
ctx.fillText(name, x, -height, imgWidth);
ctx.fillText(date, x, -height + 20, imgWidth);
}
ctx.fillText(name, x, j * height, imgWidth);
ctx.fillText(date, x, j * height + 20, imgWidth);
}
}
}
},
};
</script>
咱们先不细看代码详细细节进程,上面小编放了两张图片,图中能够看出,水印是平铺开来了,可是作用可能有点差强人意。由于这里在制作进程中需要考虑的要素比较多,比如图片巨细、水印文字巨细、长短、距离、旋转视点等等。
特别是 ctx.rotate(deg);
旋转视点是比较麻烦的,它是将整个画布进行(canvas
)旋转的,咱们要旋转水印,也只能经过该 API
来完成。可是由所以整个画布的旋转,这会形成 ctx.fillText(text, x, y);
的 x
和 y
的变化,很难到达咱们想要的作用。
尽管也能经过复杂的计算来得到正确的坐标方位,可是会比较麻烦,小编是个怕麻烦的人,不想一个小需求写太多复杂的东西,这不符合我”程序和人一个能跑就行”的理念。(✪✪)
可是,秉着有头有尾的原则,仍是在网上寻找了很久,仍是想看看有没有相关比较完善的算法逻辑进程,可惜无果。
(如果你有比较好的做法,欢迎你在谈论给小编共享一下,感谢非常感谢)
那么,这就完毕了吗?
当然还没有-.-。
运用 ctx.createPattern 制作平铺水印
经小编摸鱼得知,canvas 还有一个 ctx.createPattern API
能够用于制作重复的内容,就和背景图片的 background-repeat: 'repeat'
特点作用一样。
那么这不就简单多了,还瞎整啥,且继续来看代码:
<script>
export default {
methods: {
upload(e) { ... },
addWaterMark(img) {
const canvas = document.getElementById('canvas');
const imgWidth = img.width;
const imgHeight = img.height;
canvas.width = imgWidth;
canvas.height = imgHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
// 平铺水印
const canvasWater = document.createElement('canvas');
const waterMarkSize = 200; // 水印巨细
canvasWater.width = waterMarkSize;
canvasWater.height = waterMarkSize;
const ctxWater = canvasWater.getContext('2d');
ctxWater.textAlign = 'left';
ctxWater.textBaseline = 'top';
ctxWater.font = '12px Microsoft Yahei';
ctxWater.fillStyle = 'rgba(255, 255, 255, 0.3)';
ctxWater.rotate(-20 * Math.PI/180);
ctxWater.fillText('橙或人', 60, 80);
ctxWater.fillText('2022年11月22日 09:22:30', 10, 100);
ctx.fillStyle = ctx.createPattern(canvasWater, 'repeat'); // 制作重复的水印
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
},
};
上面咱们经过创建一个新的 canvas
用来专门制作水印,然后把它交给本来 canvas
的 ctx.createPattern()
办法,该办法能够接纳七种类型的参数,然后重复制作出来。
运用这种办法来平铺水印,相比上一种办法,就比较简单一些了,是人能看得懂的代码了,也能满意产品需求了。
输出文件
最后,咱们把 canvas
再转换 file
目标就功德圆满了。
<script>
export default {
methods: {
upload(e) {
const file = e.target.files[0];
if (!file) return;
const filePath = window.URL.createObjectURL(file);
const img = new Image();
img.src = filePath;
img.onload = () => {
const newFile = this.addWaterMark(img, file.name);
console.log(newFile);
}
},
addWaterMark(img, fileName) {
const canvas = document.getElementById('canvas');
const imgWidth = img.width;
const imgHeight = img.height;
canvas.width = imgWidth;
canvas.height = imgHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
// 平铺水印
const canvasWater = document.createElement('canvas');
const waterMarkSize = 200; // 水印巨细
canvasWater.width = waterMarkSize;
canvasWater.height = waterMarkSize;
const ctxWater = canvasWater.getContext('2d');
ctxWater.textAlign = 'left';
ctxWater.textBaseline = 'top';
ctxWater.font = '12px Microsoft Yahei';
ctxWater.fillStyle = 'rgba(255, 255, 255, 0.3)';
ctxWater.rotate(-20 * Math.PI/180);
ctxWater.fillText('橙或人', 60, 80);
ctxWater.fillText('2022年11月22日 09:22:30', 10, 100);
ctx.fillStyle = ctx.createPattern(canvasWater, 'repeat');
ctx.fillRect(0, 0, canvas.width, canvas.height);
const base64 = canvas.toDataURL('image/jpeg', 0.8)
return this.dataURLtoBlob(base64, fileName)
}
},
// base64转文件目标
dataURLtoBlob(dataurl, name) {
const arr = dataurl.split(',')
const mime = arr[0].match(/:(.*?);/)[1]
const bstr = atob(arr[1])
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], name, {
type: mime
})
}
};
至此,本篇文章就写完啦,撒花撒花。
期望本文对你有所帮助,如有任何疑问,期待你的留言哦。
老样子,点赞+谈论=你会了,保藏=你通晓了。