一、单一水印(较复杂,推荐)
1、样式
2、功能介绍
- 根据图片路径获取图片数据,转成blob类型
- 用FileReader读取图片blob数据为dataURL
- 创建img标签,src属性为dataURL
- 监听img.onload, 创建canvas,将img对象draw在canvas里
- 添加水印
- 使用canvas.toBlob转成最终图像
3、代码
<template>
<div class="pic-item"></div>
</template>
<script setup>
import { onMounted } from "vue"
onMounted(() => {
const url = 'https://cdn.jsdelivr.net/gh/zhangpanfei/static@demo/img/test.jpg'
watermark(url, ['2024-08-17'], (460-24), (580-30-24-10)/2)
})
/**
*
* @param {图片路径} imgUrl
* @param {图片宽度} width
* @param {图片高度} height
* @param {水印文字} watermarkText
*/
async function watermark(imgUrl, textArray, width = 300, height = 300) {
// 水印参数
const wmConfig = {
font: "microsoft yahei", //字体
textArray: textArray,//水印文本内容,允许数组最大长度3 即:3行水印
density: 3 //密度 建议取值范围1-5 值越大,水印越多,可能会导致水印重叠等问题,慎重!!!
}
// 1. 根据图片路径获取图片数据,转成blob类型
const fileBlob = await fetch(imgUrl)
.then((r) => r.blob())
.then((file) => file);
// 2. 用`FileReader`读取图片blob数据为dataURL
const reader = new FileReader();
reader.readAsDataURL(fileBlob);
// 3. 创建img标签,src属性为dataURL
const tempImg = await new Promise((resolve) => {
reader.onload = () => {
const img = document.createElement(`img`);
img.src = reader.result;
resolve(img);
};
});
// 4. 监听`img.onload`, 创建canvas,将img对象`draw`在canvas里
const canvas = await new Promise((resolve) => {
const canvas = document.createElement(`canvas`);
canvas.width = tempImg.width;
canvas.height = tempImg.height;
let resultBase64 = null;
tempImg.onload = () => {
const ctx = canvas.getContext("2d");
ctx.drawImage(tempImg, 0, 0);
// 5. 类文档水印
drawWaterMark(ctx, tempImg.width, tempImg.height, wmConfig);
resultBase64 = canvas.toDataURL("image/png");
if (!resultBase64) {
reject();
} else {
resolve(canvas);
}
};
});
// 6. 使用`canvas.toBlob`转成最终图像
const newImg = await new Promise((resolve) => {
canvas.toBlob((canvasBlob) => {
const newImg = document.createElement(`img`),
url = URL.createObjectURL(canvasBlob);
newImg.onload = function () {
// 图片加载完成后销毁objectUrl
URL.revokeObjectURL(url);
};
newImg.width = width;
newImg.height = height;
newImg.src = url;
resolve(newImg);
});
});
document.getElementsByClassName('pic-item')[0].appendChild(newImg);
// return newImg;
}
// 画布添加水印
const drawWaterMark = (ctx, imgWidth, imgHeight, wmConfig) => {
let fontSize;
if (imgWidth >= 3456) {
fontSize = imgWidth * 38 / 1920;
} else if (imgWidth >= 2700) {
fontSize = imgWidth * 48 / 1920;
} else if (imgWidth >= 2000) {
fontSize = imgWidth * 58 / 1920;
} else if (imgWidth >= 1436) {
fontSize = imgWidth * 68 / 1920;
} else if (imgWidth >= 800) {
fontSize = imgWidth * 78 / 1920;
} else if (imgWidth >= 500) {
fontSize = imgWidth * 88 / 1920;
} else {
fontSize = imgWidth * 98 / 1920;
}
console.log(imgWidth, imgHeight, fontSize);
ctx.font = `${fontSize}px ${wmConfig.font}`;
ctx.fillStyle = "#000000";
ctx.textAlign = "left";
ctx.textBaseline = "middle";
ctx.fillText(wmConfig.textArray[0], imgWidth * 50 / 1920, imgHeight * 100 / 980);
};
</script>
二、单一水印(简单)
1、样式
2、功能介绍
- 图片路径转成canvas
- canvas添加水印
- canvas转成img
3、代码
<template>
<div class="pic-item"></div>
</template>
<script setup>
import { onMounted } from "vue"
onMounted(() => {
const url = 'https://cdn.jsdelivr.net/gh/zhangpanfei/static@demo/img/test.jpg'
run(url, ['2024-08-17'], (460-24), (580-30-24-10)/2)
})
/**
* 图片路径转成canvas
* @param {图片url} url
*/
async function imgToCanvas(url) {
// 创建img元素
const img = document.createElement("img");
img.src = url;
img.setAttribute("crossOrigin", "anonymous"); // 防止跨域引起的 Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
await new Promise((resolve) => (img.onload = resolve));
// 创建canvas DOM元素,并设置其宽高和图片一样
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
// 坐标(0,0) 表示从此处开始绘制,相当于偏移。
canvas.getContext("2d").drawImage(img, 0, 0);
return canvas;
}
/**
* canvas添加水印
* @param {canvas对象} canvas
* @param {水印文字} text
*/
function addWatermark(canvas, text) {
const ctx = canvas.getContext("2d");
ctx.font = "28px microsoft yahei";
ctx.fillStyle = "#ffffff";
ctx.textBaseline = "middle";
ctx.fillText(text, 20, 35);
return canvas;
}
/**
* canvas转成img
* @param {canvas对象} canvas
*/
function convasToImg(canvas, width, height) {
// 新建Image对象,可以理解为DOM
var image = new Image();
// canvas.toDataURL 返回的是一串Base64编码的URL
// 指定格式 PNG
image.width = width;
image.height = height;
image.src = canvas.toDataURL("image/png");
return image;
}
/**
* 运行示例
* @param {图片路径} imgUrl
* @param {图片宽度} width
* @param {图片高度} height
* @param {水印文字} watermarkText
*/
async function run(url, textArray, width = 300, height = 300,) {
const imgUrl = url;
// 1.图片路径转成canvas
const tempCanvas = await imgToCanvas(imgUrl);
// 2.canvas添加水印
const canvas = addWatermark(tempCanvas, textArray[0]);
// 3.canvas转成img
const newImg = convasToImg(canvas, width, height);
// 查看效果
document.getElementsByClassName('pic-item')[0].appendChild(newImg);
}
</script>
三、文档水印
1、样式
2、功能介绍(同一)
仅仅是画布添加的水印方法不一样!
3、代码
这里只写添加水印的方法
// 画布添加水印
const drawWaterMark = (ctx, imgWidth, imgHeight, wmConfig) => {
let fontSize;
if (imgWidth >= 3456) {
fontSize = imgWidth * 38 / 1920;
} else if (imgWidth >= 2700) {
fontSize = imgWidth * 48 / 1920;
} else if (imgWidth >= 2000) {
fontSize = imgWidth * 58 / 1920;
} else if (imgWidth >= 1436) {
fontSize = imgWidth * 68 / 1920;
} else if (imgWidth >= 800) {
fontSize = imgWidth * 78 / 1920;
} else if (imgWidth >= 500) {
fontSize = imgWidth * 88 / 1920;
} else {
fontSize = imgWidth * 98 / 1920;
}
console.log(imgWidth, imgHeight, fontSize);
ctx.fillStyle = "white";
ctx.font = `${fontSize}px ${wmConfig.font}`;
ctx.lineWidth = 1;
ctx.fillStyle = "#ffffff";
ctx.textAlign = "left";
ctx.textBaseline = "middle";
//文字坐标
const maxPx = Math.max(imgWidth, imgHeight);
const stepPx = Math.floor(maxPx / wmConfig.density);
let arrayX = [0];//初始水印位置 canvas坐标 0 0 点
while (arrayX[arrayX.length - 1] < maxPx/2) {
arrayX.push(arrayX[arrayX.length - 1] + stepPx);
}
arrayX.push(...arrayX.slice(1, arrayX.length).map((el) => {
return -el;
}));
for (let i = 0; i < arrayX.length; i++) {
for (let j = 0; j < arrayX.length; j++) {
ctx.save();
ctx.translate(imgWidth / 2, imgHeight / 2); ///画布旋转原点 移到 图片中心
ctx.rotate(-Math.PI / 5);
if (wmConfig.textArray.length > 3) {
wmConfig.textArray = wmConfig.textArray.slice(0, 3);
}
wmConfig.textArray.forEach((el, index) => {
let offsetY = fontSize * index + 2;
ctx.fillText(el, arrayX[i], arrayX[j] + offsetY);
});
ctx.restore();
}
}
}