出于保密或者版权声明等因素,网页图片会通过设置水印的方式来保密。保密可以通过服务端返回保密后的图片地址,也可以通过前端canvas实现。下面通过canvas实现给图片添加水印。
· 代码
/**
* 画布添加水印
* @param {Object} ctx canvas上下文
* @param {Number} imgWidth 图片宽度
* @param {Number} imgHeight 图片高度
* @param {Object} config config.font:字体;config.textArray: ['张三','2024/11/08 16:44'], 水印文本内容,
允许数组最大长度3 即:3行水印;
config.density 密度 建议取值范围1-5 值越大,水印越多,可能会导致水印重叠等问题,慎重!!!
*/
function drawWatermark(ctx, imgWidth, imgHeight, config = {}) {
try {
if (typeof ctx !== 'object' || typeof imgWidth !== 'number' || typeof imgHeight !== 'number' || typeof config !== 'object') {
throw new Error('Invalid arguments provided.');
}
const defaultConfig = {
font: 'microsoft yahei',
textArray: ['图片版权归原作者所有',new Date().toLocaleDateString()],
density: 3
};
const watermarkConfig = Object.assign(defaultConfig, config);
if (!watermarkConfig.font || typeof watermarkConfig.density !== 'number' || watermarkConfig.density <= 0 || watermarkConfig.density > 5) {
throw new Error('Invalid configuration values.');
}
if (!Array.isArray(watermarkConfig.textArray) || !watermarkConfig.textArray.every(text => typeof text === 'string')) {
throw new Error('textArray must be an array of strings.');
}
const fontSize = imgWidth >= imgHeight ? Math.floor(imgWidth / 40) : Math.floor(imgHeight / 40);
ctx.font = `${fontSize}px ${watermarkConfig.font}`;
ctx.lineWidth = 1;
ctx.fillStyle = 'rgba(200, 200, 200, 0.85)';
ctx.textAlign = 'left';
ctx.textBaseline = 'middle';
const maxDimension = Math.max(imgWidth, imgHeight);
const stepSize = Math.floor(maxDimension / watermarkConfig.density);
const xPositions = [0];
while (xPositions[xPositions.length - 1] < maxDimension / 2) {
xPositions.push(xPositions[xPositions.length - 1] + stepSize);
}
xPositions.push(...xPositions.slice(1, xPositions.length).map((pos) => -pos));
for (let i = 0; i < xPositions.length; i++) {
for (let j = 0; j < xPositions.length; j++) {
ctx.save();
ctx.translate(imgWidth / 2, imgHeight / 2);
ctx.rotate(-Math.PI / 5);
if (watermarkConfig.textArray.length > 3) {
watermarkConfig.textArray = watermarkConfig.textArray.slice(0, 3);
}
watermarkConfig.textArray.forEach((text, index) => {
const verticalOffset = fontSize * index + 2;
ctx.fillText(text, xPositions[i], xPositions[j] + verticalOffset);
});
ctx.restore();
}
}
} catch (error) {
console.error('Draw watermark failed:', error);
throw error;
}
}
/**
* 添加水印并返回新的图片地址
* @param {String} imageUrl 图片地址
* @param {Object} config config.font:字体;config.textArray: ['张三','2024/11/08 16:44'], 水印文本内容,允许数组最大长度3 即:3行水印;config.density 密度 建议取值范围1-5 值越大,水印越多,可能会导致水印重叠等问题,慎重!!!
*/
function getWatermarkImageUrl(imageUrl, config) {
return new Promise((resolve, reject) => {
fetch(imageUrl)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.blob();
})
.then(blob => {
const img = new Image();
img.src = URL.createObjectURL(blob);
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
if (!ctx) {
throw new Error('Canvas context is null');
}
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
drawWatermark(ctx, img.width, img.height, config);
canvas.toBlob(newBlob => {
const resultUrl = URL.createObjectURL(newBlob);
resolve(resultUrl);
URL.revokeObjectURL(img.src);
});
};
img.onerror = () => {
console.error('加载图片失败');
resolve(imageUrl);
};
})
.catch(error => {
console.error('添加水印失败:', error);
reject(error);
});
});
}
· 使用示例
//使用示例
window.onload = async function() {
const img = await getWatermarkImageUrl('images/redtory2.jpg')
console.log('获取的水印图片🐞🐞')
console.log(img)
}
· 效果
原创文章,如需转载,请注明出处。