网页水印实现
说明文档
1 水印位置
-
从sight-info.html点击公告信息中WaterMark访问,或者直接打开/html/watermark.html。调用的js代码均在/js/watermark.js中。
-
网页截图:
2 可见水印实现
-
思路:
- 图片路径转成canvas
- canvas添加水印
- canvas转成img
-
步骤说明和关键代码说明:
async function run_visible() { // 1.图片路径转成canvas const tempCanvas = await imgToCanvas(imgUrl); // 2.canvas添加水印 const canvas = addWatermark(tempCanvas, "这是一个水印"); // 3.canvas转成img const url = convasToImg(canvas); // 查看效果 document.getElementById("watermark_visible").src = url; }
其中imgToCanvas方法:
// 创建canvas DOM元素,并设置其宽高和图片一样 const canvas = document.createElement("canvas"); canvas.width = img.width; canvas.height = img.height; // 坐标(0,0) 表示从此处开始绘制,相当于偏移。 canvas.getContext("2d").drawImage(img, 0, 0);
其中addWaterMark在tempCanvas基础上添加水印文字,并设定样式:
const angle = -30; const ctx = canvas.getContext("2d"); ctx.fillStyle = "blue"; ctx.textBaseline = "middle"; ctx.font = `100px serif`; ctx.globalAlpha = 0.6; ctx.rotate(Math.PI / 180 * angle); ctx.fillText(text, -160, 500);
其中convasToImg方法:
// canvas.toDataURL 返回的是一串Base64编码的URL,指定格式 PNG image.src = canvas.toDataURL("image/png");
3 不可见数字水印实现
-
思路:
- 图片都是有一个个像素点构成的,每个像素点都是由 RGB 三种元素构成。
- RGB 分量值的小量变动,是肉眼无法分辨的,不影响对图片的识别。
- 因此,当我们把其中的一个分量修改,即使是设计师也很难分辨察觉。
-
步骤说明和关键代码说明:
先获取要加入到图片中的文字的像素信息,这里用 canvas 在画布上打印文字,获取像素信息。
var ctx = document.getElementById('canvas_invi').getContext('2d'); ctx.font = `40px serif`; ctx.fillStyle = "blue"; ctx.textBaseline = "middle"; ctx.fillText(text, 10, 130); var textData; textData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height).data;
然后提取加密信息在待加密的图片上进行处理。也就是,接受要隐藏的数据以及隐藏的颜色通道,然后对原图进行操作,修改图片该通道分量的最低位,如果有文字信息,则最低位置为 1,否则为 0:
var mergeData = function(ctx, newData, color, originalData) { var oData = originalData.data; var bit, offset; // offset的作用是找到alpha通道值 switch (color) { case 'R': bit = 0; offset = 3; break; case 'G': bit = 1; offset = 2; break; case 'B': bit = 2; offset = 1; break; } for (var i = 0; i < oData.length; i++) { if (i % 4 == bit) { // 只处理目标通道 if (newData[i + offset] === 0 && (oData[i] % 2 === 1)) { // 没有信息的像素,该通道最低位置0,但不要越界 if (oData[i] === 255) { oData[i]--; } else { oData[i]++; } } else if (newData[i + offset] !== 0 && (oData[i] % 2 === 0)) { //有信息的像素,该通道最低位置1 oData[i]++; } } } ctx.putImageData(originalData, 0, 0); }
最后在 img.onload 调用 processData(ctx, originalData):
img.onload = function() { // 获取指定区域的canvas像素信息 ctx.drawImage(img, 0, 0, 256, 256); originalData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); mergeData(ctx, textData, 'R', originalData) };