odf_new/odf-uniapp/utils/watermark.js
2026-03-26 10:00:23 +08:00

142 lines
4.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 在照片左下角叠加多行水印文字
* @param {string} imagePath - 原始图片路径
* @param {string|string[]} lines - 水印文字,字符串或字符串数组
* @param {object} [canvasCtx] - APP端传入 { canvasId, proxy, setSize }
* @returns {Promise<string>} 带水印的图片路径,失败时返回原图
*/
export function addWatermark(imagePath, lines, canvasCtx) {
if (typeof lines === 'string') {
lines = [lines]
}
return new Promise((resolve) => {
// 超时保护10秒
const timer = setTimeout(() => {
console.warn('[watermark] timeout')
resolve(imagePath)
}, 10000)
_doWatermark(imagePath, lines, canvasCtx)
.then((r) => { clearTimeout(timer); resolve(r || imagePath) })
.catch((e) => { clearTimeout(timer); console.warn('[watermark]', e); resolve(imagePath) })
})
}
function _doWatermark(imagePath, lines, canvasCtx) {
// #ifdef H5
return _h5(imagePath, lines)
// #endif
// #ifdef APP-PLUS
return _app(imagePath, lines, canvasCtx)
// #endif
// eslint-disable-next-line no-unreachable
return Promise.resolve(imagePath)
}
// ========== H5 ==========
// #ifdef H5
function _h5(imagePath, lines) {
return new Promise((resolve, reject) => {
const img = new Image()
img.crossOrigin = 'anonymous'
img.onload = () => {
try {
const w = img.naturalWidth, h = img.naturalHeight
const c = document.createElement('canvas')
c.width = w; c.height = h
const ctx = c.getContext('2d')
ctx.drawImage(img, 0, 0, w, h)
_stamp(ctx, w, h, lines, false)
resolve(c.toDataURL('image/jpeg', 0.9))
} catch (e) { reject(e) }
}
img.onerror = reject
img.src = imagePath
})
}
// #endif
// ========== APP ==========
// #ifdef APP-PLUS
function _app(imagePath, lines, cc) {
if (!cc || !cc.canvasId || !cc.proxy) {
return Promise.reject(new Error('missing canvasCtx'))
}
return new Promise((resolve, reject) => {
uni.getImageInfo({
src: imagePath,
success(info) {
try {
// 限制 canvas 宽度 ≤ 1200避免内存问题
let dw = info.width, dh = info.height
if (dw > 1200) { const r = 1200 / dw; dw = 1200; dh = Math.round(info.height * r) }
// 更新页面 canvas 尺寸
if (typeof cc.setSize === 'function') cc.setSize(dw, dh)
// 等 DOM 刷新200ms 足够 APP webview 更新)
setTimeout(() => {
try {
const ctx = uni.createCanvasContext(cc.canvasId, cc.proxy)
ctx.drawImage(imagePath, 0, 0, dw, dh)
_stamp(ctx, dw, dh, lines, true)
let done = false
const exp = () => {
if (done) return
done = true
uni.canvasToTempFilePath({
canvasId: cc.canvasId,
x: 0, y: 0, width: dw, height: dh,
destWidth: info.width, destHeight: info.height,
fileType: 'jpg', quality: 0.9,
success: (r) => resolve(r.tempFilePath),
fail: reject
}, cc.proxy)
}
ctx.draw(false, () => setTimeout(exp, 200))
// 兜底
setTimeout(exp, 1200)
} catch (e) { reject(e) }
}, 200)
} catch (e) { reject(e) }
},
fail: reject
})
})
}
// #endif
/**
* 在 canvas context 上绘制水印条
* @param {boolean} isUni - true 用 uni canvas APIfalse 用 H5 canvas API
*/
function _stamp(ctx, w, h, lines, isUni) {
const fs = Math.max(Math.floor(w * 0.03), 14)
const pad = Math.floor(fs * 0.8)
const lh = fs + pad
const bgH = lh * lines.length + pad
if (isUni) {
ctx.setFillStyle('rgba(0,0,0,0.4)')
ctx.fillRect(0, h - bgH, w, bgH)
ctx.setFillStyle('#ffffff')
ctx.setFontSize(fs)
lines.forEach((t, i) => {
// uni canvas 的 fillText y 是文字顶部基线
ctx.fillText(t, pad, h - bgH + pad + lh * i)
})
} else {
ctx.fillStyle = 'rgba(0,0,0,0.4)'
ctx.fillRect(0, h - bgH, w, bgH)
ctx.fillStyle = '#ffffff'
ctx.font = `${fs}px sans-serif`
ctx.textBaseline = 'middle'
lines.forEach((t, i) => {
ctx.fillText(t, pad, h - bgH + pad / 2 + lh * i + fs / 2)
})
}
}