一、前言
最近遇到一个需求,微信小程序上传图片添加水印的需求,故此有该文章做总结, 功能涵盖定理地位,百度地址解析,图片四角水印,图片压缩,图片压缩并添加水印,图片剪裁,定位授权,保存图片到相册等
二、效果

三、代码实现核心
3.1)添加水印并压缩 核心实现
javascript
// 添加水印并压缩
export function addWatermarkAndCompress(options, that, isCompress = false) {
return new Promise((resolve, reject) => {
const {
errLog,
config
} = dealWatermarkConfig(options)
that.watermarkCanvasOption.width = 0
that.watermarkCanvasOption.height = 0
if (!errLog.length) {
const {
canvasId,
imagePath,
watermarkList,
quality = 0.6
} = config
uni.getImageInfo({ // 获取图片信息,以便获取图片的真实宽高信息
src: imagePath,
success: (info) => {
const {
width: oWidth,
height: oHeight,
type,
orientation
} = info; // 获取图片的原始宽高
const fileTypeObj = {
'jpeg': 'jpg',
'jpg': 'jpg',
'png': 'png',
}
const fileType = fileTypeObj[type] || 'png'
let width = oWidth
let height = oHeight
if (isCompress) {
const {
cWidth,
cHeight
} = calcRatioHeightAndWight({
oWidth,
oHeight,
quality,
orientation
})
// 按对折比例缩小
width = cWidth
height = cHeight
}
that.watermarkCanvasOption.width = width
that.watermarkCanvasOption.height = height
that.$nextTick(() => {
// 获取canvas绘图上下文
const ctx = uni.createCanvasContext(canvasId, that);
// 绘制原始图片到canvas上
ctx.drawImage(imagePath, 0, 0, width, height);
// 绘制水印项
const drawWMItem = (ctx, options) => {
const {
fontSize,
color,
text: cText,
position,
margin
} = options
// 添加水印
ctx.setFontSize(fontSize); // 设置字体大小
ctx.setFillStyle(color); // 设置字体颜色为红色
if (isNotEmptyArr(cText)) {
const text = cText.filter(Boolean)
if (position.startsWith('bottom')) {
text.reverse()
}
text.forEach((str, ind) => {
const textMetrics = ctx.measureText(str);
const {
calcX,
calcY
} = calcPosition({
height,
width,
position,
margin,
ind,
fontSize,
textMetrics
})
ctx.fillText(str, calcX, calcY, width);
})
} else {
const textMetrics = ctx.measureText(cText);
const {
calcX,
calcY
} = calcPosition({
height,
width,
position,
margin,
ind: 0,
fontSize,
textMetrics
})
// 在图片底部添加水印文字
ctx.fillText(text, calcX, calcY, width);
}
}
watermarkList.forEach(ele => {
drawWMItem(ctx, ele)
})
// 绘制完成后执行的操作,这里不等待绘制完成就继续执行后续操作,因为我们要导出为图片
ctx.draw(false, () => {
// #ifndef MP-ALIPAY
uni.canvasToTempFilePath({ // 将画布内容导出为图片
canvasId,
x: 0,
y: 0,
width,
height,
fileType,
quality, // 图片的质量,目前仅对 jpg 有效。取值范围为 (0, 1],不在范围内时当作 1.0 处理。
destWidth: width,
destHeight: height,
success: (res) => {
console.log('res.tempFilePath', res)
resolve(res.tempFilePath)
},
fail() {
reject(false)
}
}, that);
// #endif
// #ifdef MP-ALIPAY
ctx.toTempFilePath({ // 将画布内容导出为图片
canvasId,
x: 0,
y: 0,
width: width,
height: height,
destWidth: width,
destHeight: height,
quality,
fileType,
success: (res) => {
console.log('res.tempFilePath', res)
resolve(res.tempFilePath)
},
fail() {
reject(false)
}
}, that);
// #endif
});
})
}
});
} else {
const errStr = errLog.join(';')
showMsg(errStr)
reject(errStr)
}
})
}
3.2)剪切图片
javascript
// 剪切图片
export function clipImg(options, that) {
return new Promise((resolve, reject) => {
const {
errLog,
config
} = dealClipImgConfig(options)
that.watermarkCanvasOption.width = 0
that.watermarkCanvasOption.height = 0
if (!errLog.length) {
const {
canvasId,
imagePath,
cWidth,
cHeight,
position
} = config
// 获取图片信息,以便获取图片的真实宽高信息
uni.getImageInfo({
src: imagePath,
success: (info) => {
const {
width,
height
} = info; // 获取图片的原始宽高
// 自定义剪裁范围要在图片内
if (width >= cWidth && height >= cHeight) {
that.watermarkCanvasOption.width = width
that.watermarkCanvasOption.height = height
that.$nextTick(() => {
// 获取canvas绘图上下文
const ctx = uni.createCanvasContext(canvasId, that);
const {
calcSX,
calcSY,
calcEX,
calcEY
} = calcClipPosition({
cWidth,
cHeight,
position,
width,
height
})
// 绘制原始图片到canvas上
ctx.drawImage(imagePath, 0, 0, width, height);
// 绘制完成后执行的操作,这里不等待绘制完成就继续执行后续操作,因为我们要导出为图片
ctx.draw(false, () => {
// #ifndef MP-ALIPAY
uni.canvasToTempFilePath({ // 将画布内容导出为图片
canvasId,
x: calcSX,
y: calcSY,
width: cWidth,
height: cHeight,
destWidth: cWidth,
destHeight: cHeight,
success: (res) => {
console.log('res.tempFilePath',
res)
resolve(res.tempFilePath)
},
fail() {
reject(false)
}
}, that);
// #endif
// #ifdef MP-ALIPAY
ctx.toTempFilePath({ // 将画布内容导出为图片
canvasId,
x: 0,
y: 0,
width: width,
height: height,
destWidth: width,
destHeight: height,
// fileType: 'png',
success: (res) => {
console.log('res.tempFilePath',
res)
resolve(res.tempFilePath)
},
fail() {
reject(false)
}
}, that);
// #endif
});
})
} else {
return imagePath
}
}
})
} else {
const errStr = errLog.join(';')
showMsg(errStr)
reject(errStr)
}
})
}
3.3)canvas画布标签
javascript
<!-- 给图片添加的标签 -->
<canvas v-if="watermarkCanvasOption.width > 0 && watermarkCanvasOption.height > 0"
:style="{ width: watermarkCanvasOption.width + 'px', height: watermarkCanvasOption.height + 'px' }"
canvas-id="watermarkCanvas" id="watermarkCanvas" style="position: absolute; top: -10000000rpx;" />
以上代码具体的实现功能不做一一讲解,详细请看下方源码地址
四、源码地址
github: github.com/ArcherNull/...
五、总结
- 图片的操作,例如压缩/剪裁/加水印都是需要借助canvas标签,也就是说需要有canvas实例通过该api实现这些操作
- 当执行 ctx.drawImage(imagePath, 0, 0, width, height) 后,后续的操作的是对内存中的数据,而不是源文件
完结撒花,如果对您有帮助,请一键三连