这一版本优化了很多
This commit is contained in:
94
utils/watermark.js
Normal file
94
utils/watermark.js
Normal file
@@ -0,0 +1,94 @@
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
/**
|
||||
* 前端实时渲染防作弊当前时间戳水印
|
||||
* @param {Object} options
|
||||
* @param {string} options.tempFilePath - 原始图片临时路径
|
||||
* @param {string} options.canvasId - canvas的ID名(默认为 watermarkCanvas)
|
||||
* @param {Object} options.canvasWidthRef - 绑定的 canvasWidth 响应式变量(ref)
|
||||
* @param {Object} options.canvasHeightRef - 绑定的 canvasHeight 响应式变量(ref)
|
||||
* @param {Object} options.instance - 当前Vue组件的实例 (通过 getCurrentInstance() 获取)
|
||||
* @returns {Promise<string>} 渲染添加完水印后的新图片临时路径
|
||||
*/
|
||||
export function addTimestampWatermark({ tempFilePath, canvasId = 'watermarkCanvas', canvasWidthRef, canvasHeightRef, instance }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.getImageInfo({
|
||||
src: tempFilePath,
|
||||
success: (imageInfo) => {
|
||||
const imgWidth = imageInfo.width;
|
||||
const imgHeight = imageInfo.height;
|
||||
|
||||
// 动态配置 Canvas 宽高与图片 1:1 比例,确保高清晰度不失真
|
||||
if (canvasWidthRef && canvasWidthRef.value !== undefined) {
|
||||
canvasWidthRef.value = imgWidth;
|
||||
}
|
||||
if (canvasHeightRef && canvasHeightRef.value !== undefined) {
|
||||
canvasHeightRef.value = imgHeight;
|
||||
}
|
||||
|
||||
// 延迟至 nextTick 等待 UniApp 应用并渲染新的 Canvas 尺寸
|
||||
nextTick(() => {
|
||||
const ctx = uni.createCanvasContext(canvasId, instance);
|
||||
if (!ctx) {
|
||||
reject('创建水印画布上下文失败');
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 绘制原始图片
|
||||
ctx.drawImage(tempFilePath, 0, 0, imgWidth, imgHeight);
|
||||
|
||||
// 2. 构造防作弊当前真实时间戳字符串
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(now.getDate()).padStart(2, '0');
|
||||
const hours = String(now.getHours()).padStart(2, '0');
|
||||
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
const timeStr = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
|
||||
// 3. 动态计算合适的水印字体大小(以标准 375px 屏幕下的 14px 为基准,按图片实际宽度等比缩放,保证大图下不模糊且比例完美)
|
||||
const fontSize = Math.max(14, Math.floor(imgWidth * (14 / 375)));
|
||||
ctx.setFontSize(fontSize);
|
||||
ctx.setFillStyle('#C9CBD4'); // 水印文字颜色 #C9CBD4
|
||||
|
||||
// 设置半透明黑色文字阴影,确保在亮色背景下时间戳也具有极强的辨识度
|
||||
ctx.setShadow(2, 2, 4, 'rgba(0, 0, 0, 0.6)');
|
||||
|
||||
// 4. 计算右下角坐标
|
||||
const padding = fontSize;
|
||||
const textWidth = timeStr.length * (fontSize * 0.55); // 估算英文字符占用宽度
|
||||
const x = imgWidth - textWidth - padding;
|
||||
const y = imgHeight - padding;
|
||||
|
||||
// 5. 渲染防作弊时间戳
|
||||
ctx.fillText(timeStr, x, y);
|
||||
|
||||
// 6. 执行画布绘制并导出文件路径
|
||||
ctx.draw(false, () => {
|
||||
setTimeout(() => {
|
||||
uni.canvasToTempFilePath({
|
||||
canvasId: canvasId,
|
||||
destWidth: imgWidth,
|
||||
destHeight: imgHeight,
|
||||
fileType: 'jpg',
|
||||
quality: 0.9,
|
||||
success: (exportRes) => {
|
||||
resolve(exportRes.tempFilePath);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('导出带水印图片失败:', err);
|
||||
reject(err);
|
||||
}
|
||||
}, instance);
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('获取图片信息失败:', err);
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user