uniapp 图片添加水印代码封装(优化版、图片上传压缩、生成文字根据页面自适应比例、增加文字背景色
多张照片上传封装
<template>
<view class="image-picker">
<uni-file-picker v-model="imageValue" :auto-upload="false" :title="title" :limit="limit"
:image-styles="imageStyles" :file-mediatype="fileMediatype" :mode="mode" @select="select">
<view v-if="loading" class="form-item-column-center">
<u-loading-icon text="上传中" textSize="12" :vertical="true"></u-loading-icon>
</view>
<view v-else class="form-item-column-center">
<u-icon name="camera" size="28"></u-icon>
<view :style="{ marginTop: '5px'}">上传照片</view>
</view>
</uni-file-picker>
<view class="watermark-canvas">
<canvas id="watermark-canvas" :style="{ width: canvasWidth, height: canvasHeight }"
canvas-id="watermark-canvas" v-if="flag" />
</view>
</view>
</template>
<script>
import {
imageUpload,
} from '@/api/system/applet.js'
import {
imageChoose,
} from '@/utils/public.js'
export default {
name: 'ImageWatermarkPicker',
props: {
limit: {
type: [Number, String],
default: 1,
},
title: {
type: String,
default: null,
},
mode: {
type: String,
default: 'grid',
},
fileMediatype: {
type: String,
default: 'image',
},
imageStyles: {
type: Object,
default: null,
},
watermark: {
type: Boolean,
default: true,
},
modelValue: {
type: Array,
default () {
return []
},
},
value: {
type: Array,
default () {
return []
},
},
},
emits: ['input', 'update:modelValue'],
data() {
return {
flag: false,
imageValue: [],
canvasWidth: '1080px',
canvasHeight: '2160px',
longitude: '',
latitude: '',
addressName: '',
loading: false,
oldImageslength: null,
}
},
watch: {
imageValue(newVal) {
this.$emit('update:modelValue', newVal)
this.$emit('input', newVal)
this.$emit('change', newVal)
},
value: {
handler(newVal) {
this.imageValue = newVal
},
immediate: true,
},
modelValue: {
handler(newVal) {
this.imageValue = newVal
},
immediate: true,
},
},
methods: {
checkImage(url) {
const checkNum = 5
let currentCheckNum = 1
return new Promise((resolve, reject) => {
process()
function process() {
uni.getImageInfo({
src: url,
success: function(image) {
resolve(image)
},
fail: function(err) {
if (checkNum <= currentCheckNum) {
uni.showToast({
title: '图片上传失败',
icon: 'none'
})
reject(err)
} else {
currentCheckNum++
const timer = setTimeout(() => {
clearTimeout(timer)
process()
}, 300)
}
},
})
}
})
},
async select(e) {
this.oldImageslength = e.tempFiles.length
for (let tempFile of e.tempFiles) {
await this.watermarkProcess(tempFile)
}
},
async watermarkProcess(tempFile) {
const {
name,
size,
extname,
uuid,
path
} = tempFile
let url = null
let photo = null
if (this.watermark) {
url = await this.addWatermark(path)
}
url = await this.uploadFile(url)
await this.checkImage(url)
this.imageValue = [
...this.imageValue,
{
name,
extname,
url,
photo,
size,
uuid,
},
]
},
getHeightOffset(height, index, fontSize) {
return index == 0 ? (height - fontSize) : (height - (index * fontSize) - fontSize) - (index * (fontSize /
3))
},
async addWatermark(tempFilePath) {
this.addressName = '测试位置'
this.latitude = 119.651
this.longitude = 80.654
this.flag = true
if (this.loading == true) {
uni.showLoading({
title: "上传图片",
mask: true,
})
}
return new Promise((resolve, reject) => {
uni.getImageInfo({
src: tempFilePath,
success: async (res) => {
this.canvasWidth = `${res.width}px`
this.canvasHeight = `${res.height}px`
await this.sleep(200)
const ctx = uni.createCanvasContext('watermark-canvas', this)
ctx.clearRect(0, 0, res.width, res.height)
ctx.beginPath()
ctx.drawImage(tempFilePath, 0, 0)
let size
ctx.fillStyle = 'rgba(0,0,0,0.1)';
if (res.width / res.height > 1) {
size = Math.floor(res.height / 26)
ctx.fillRect(0, res.height - (size*6), res.width, size*6);
} else {
size = Math.floor(res.width / 26)
ctx.fillRect(0, res.height - (size*7), res.width, size*7);
}
let fontSize = size
ctx.setFontSize(fontSize)
ctx.shadowColor = "rgba(0,0,0,1.0)";
ctx.shadowOffsetX = 5
ctx.shadowOffsetY = 5
let max = (res.width - fontSize) / fontSize
let marks = []
let address = "地址:" + this.addressName
let location = "坐标:" + this.latitude + ',' + this.longitude
let fillTexts = [address, location, time]
fillTexts.forEach((mark, index) => {
if (mark.length <= max) {
marks.push({
mark: mark,
start: fontSize / 2
})
} else {
marks.push({
mark: mark.substring(max),
start: fontSize / 2 + fontSize *
3
})
marks.push({
mark: mark.substring(0, max),
start: fontSize / 2
})
}
})
marks.forEach((mark, index) => {
ctx.setFillStyle("rgba(250, 250, 250,1.0)")
ctx.fillText(mark.mark, mark.start, this.getHeightOffset(
res.height, index, fontSize))
});
ctx.draw(false, async () => {
await this.sleep(500)
uni.canvasToTempFilePath({
canvasId: 'watermark-canvas',
destWidth: res.width,
destHeight: res.height,
fileType: 'jpg',
quality: 0.8,
success: (fileRes) => {
this.flag = false
resolve(fileRes.tempFilePath)
},
fail: (err) => {
console.log('[Error draw]', err)
uni.showToast({
title: err.errMsg,
icon: 'none'
})
reject()
},
},
this,
)
})
},
fail: (err) => {
console.log('[Error getImageInfo]', err)
uni.showToast({
title: err.errMsg,
icon: 'none'
})
reject()
},
})
})
},
async uploadFile(path) {
let image = imageChoose(path)
const res = await imageUpload(image).then(
response => {
this.oldImageslength--
this.loading = this.oldImageslength == 0 ? false : true
this.oldImageslength == 0 ? uni.hideLoading() : ''
return response.data.url
})
return res
},
sleep(millisecond) {
return new Promise((resolve) => {
setTimeout(resolve, millisecond)
})
},
},
}
</script>
<style lang="scss" scoped>
canvas {
position: absolute;
left: 2000upx;
}
.image-picker {
position: relative;
.form-item-column-center {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
flex-direction: column;
}
.watermark-canvas {
position: absolute;
top: 5px;
left: 5px;
width: 1px;
height: 1px;
overflow: hidden;
}
}
</style>
应用实例
照片上传实例
<template>
<view>
<photoList v-model="baseFormData.faceImgsFirst" :limit="1"/>
</view>
</template>
<script>
import photoList from '@/pages/public/photoOne/photoOne.vue'
export default{
components: {
photoList
},
data(){
return{
baseFormData:{}
}
},
methods:{
}
}
</script>
动态表单照片添加水印(直接使用)
<template>
<view>
<uni-forms>
<uni-forms-item label="照片" required :rules="[{required: true,errorMessage: '最少一张照片'}]":name="['inspectionCustodyWorkLogDetailBoList',index,'imagelist']" label-width="100rpx">
<view class="form-item">
<photoList v-model="baseFormData.faceImgsFirst" :limit="1"/>
</view>
</uni-forms-item>
</uni-forms>
</view>
</template>
<script>
import photoList from '@/pages/public/photoOne/photoOne.vue'
export default{
components: {
photoList
},
data(){
baseFormData: {
inspectionCustodyWorkLogDetailBoList: [],
},
},
methods:{
}
}
</script>