当前位置:   article > 正文

uniapp 图片添加水印代码封装(优化版、图片上传压缩、生成文字根据页面自适应比例、增加文字背景色

uniapp 图片添加水印代码封装(优化版、图片上传压缩、生成文字根据页面自适应比例、增加文字背景色

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,
			},
			// #ifdef VUE3
			modelValue: {
				type: Array,
				default () {
					return []
				},
			},
			// #endif

			// #ifndef VUE3
			value: {
				type: Array,
				default () {
					return []
				},
			},
			// #endif
		},
		emits: ['input', 'update:modelValue'],
		data() {
			return {
				flag: false, //绘制显示
				imageValue: [],
				canvasWidth: '1080px',
				canvasHeight: '2160px',
				longitude: '', //坐标
				latitude: '', //y坐标
				addressName: '', //传入公司地址
				loading: false,
				oldImageslength: null, //上传时照片个数
			}
		},
		watch: {
			imageValue(newVal) {
				// #ifdef VUE3
				this.$emit('update:modelValue', newVal)
				// #endif
				// #ifndef VUE3
				this.$emit('input', newVal)
				// #endif
				this.$emit('change', newVal)
			},
			// #ifndef VUE3
			value: {
				handler(newVal) {
					this.imageValue = newVal
				},
				immediate: true,
			},
			// #endif
			// #ifdef VUE3
			modelValue: {
				handler(newVal) {
					this.imageValue = newVal
				},
				immediate: true,
			},
			// #endif
		},
		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) {
				// #ifdef MP-WEIXIN
				this.addressName = '测试位置'
				this.latitude = 119.651
				this.longitude = 80.654
				// #endif
				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) // 某些平台 canvas 渲染慢,需要等待
							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); // 填充整个 Canvas 区域	
							} else {
								size = Math.floor(res.width / 26)
								ctx.fillRect(0, res.height - (size*7), res.width, size*7); // 填充整个 Canvas 区域	
							}
							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) => {
								//测量出最长文字的宽
								// console.log('文字宽:'+ctx.measureText(mark).width)
								if (mark.length <= max) {
									marks.push({
										mark: mark, //水印文字
										start: fontSize / 2 //第一个字的起点位置
									})
								} else {
									marks.push({
										mark: mark.substring(max),
										start: fontSize / 2 + fontSize *
											3 //第一个字的起点位置,+fontSize*3是因为此水印为当前mark的换行,因此缩进3字符
									})
									marks.push({
										mark: mark.substring(0, max),
										start: fontSize / 2 //第一个字的起点位置
									})
								}
							})
							// 绘制水印背景另外写法,实测不太好用
							// ctx.fillStyle = 'rgba(0,0,0,0.1)'; // 设置背景色
							// ctx.fillRect(0, this.getHeightOffset(res.height, 4, fontSize), res.width,this.getHeightOffset(res.height, 4, fontSize)); // 填充整个 Canvas 区域	
							//绘制水印文字
							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) // 某些平台 canvas 渲染慢,需要等待
								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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346

应用实例

照片上传实例
<template>
  <view>
    <photoList   v-model="baseFormData.faceImgsFirst"  :limit="1"/>
  </view>
</template>
<script>
import photoOne from '@/pages/public/photoOne/photoOne.vue'
export default{
       components: {
			photoOne
		},
		data(){
		   return{
		       baseFormData:{}
		   }
		},
		methods:{
		  
		}
}
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

动态表单照片添加水印(直接使用)

注意imagelists必填,避免出现删除不一致现象,发送为父级数据


<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 photoOne from '@/pages/public/photoOne/photoOne.vue'
export default{
       components: {
			photoOne
		},
		data(){
		    // 基础表单数据
			baseFormData: {
				inspectionCustodyWorkLogDetailBoList: [], 
			},
		},
		methods:{
		 	  
		}
}
</script>


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/560423
推荐阅读
相关标签
  

闽ICP备14008679号