赞
踩
<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>
<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>
注意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>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。