微信小程序上传图片使用canvas添加水印

项目近期有一个需求,是在小程序上传图片或者选择多张图片时,页面缩略图和上传服务器的图片都是带水印的,水印文案是当前的时间和当前所处的地点。


<view hidden = "{{ !watermark }}">
    <mp-uploader bindfail="uploadError" bindsuccess="uploadSuccess" binddelete="delete" select="{{selectFile}}" files="{{files}}" upload="{{uplaodFile2}}" max-count="{{maxCount}}" title="{{title}}" tips="{{tip}}">mp-uploader>
    <view style='width:0px;height:0px;overflow:hidden;position:fixed;left:90000000px;z-index:-999;'>
        <canvas wx:for="{{canvasArray}}" wx:key="key" canvas-id="canvasId{{index}}" style="width: {{item.width}}px;height: {{item.height}}px;">canvas>
    view>
view>


<view hidden = "{{ watermark }}">
    <mp-uploader bindfail="uploadError" bindsuccess="uploadSuccess" binddelete="delete" select="{{selectFile}}" files="{{files}}" upload="{{uplaodFile}}" max-count="{{maxCount}}" title="{{title}}" tips="{{tip}}">mp-uploader>
view>


const app = getApp()
const http = require('../../utils/http')
// 引入SDK核心类
var QQMapWX = require('../../utils/qqmap-wx-jssdk')
 
//申请密钥地址
var qqmapsdk = new QQMapWX({ key: 'PBPBZ-HHOW3-4H63F-YZDHC-HAJP3-VJBSK' })

Component({
	options: {
		styleIsolation: 'shared'
	},

	/**
	 * 组件的属性列表
	 */
	properties: {
		files: {
			type: Array,
			value: []
		},
		title: {
			type: String,
			value: '图片上传'
		},
		tip: {
			type: String,
			value: ''
		},
		maxCount: { // 最多上传张数
			type: Number,
			value: 7
		},
		watermark: {
			type: Boolean,
			value: false
		}
	},

	/**
	 * 页面的初始数据
	 */
	data: {
		canvasArray: [], // DOM创建canvas的数组
		pics: [], // 上传到服务器的图片
		promisePics: [], // 上传图片队列
		address: ''
	},
	ready: function () {
		this.setData({
			selectFile: this.selectFile.bind(this),
			uplaodFile: this.uplaodFile.bind(this),
			uplaodFile2: this.uplaodFile2.bind(this)
		})
		http.get('huawei/obs').then(res => {
			this.setData({ huawei: res })
		})

		const that = this
		wx.getLocation({
			isHighAccuracy: true, // 开启地图精准定位
			type: 'gcj02', // 地图类型写这个
			// type: 'wgs84',
			success: function(res) {
        		//用腾讯地图的api,根据经纬度定位当前位置信息
				qqmapsdk.reverseGeocoder({
					location: {
						latitude: res.latitude,  // 回调的纬度
						longitude: res.longitude // 回调的经度
					},
					//回调成功显示位置的详细数据
					success:(res)=> {
						that.setData({ address: res.result.address })
					},
					//回调失败  (调用成功之后这个可以不需要 ,回调失败会有报错信息方便调试)
					fail: function (res) {
           				console.log(res)
					},
					//成功失败都会执行
					complete: function (res) {
						console.log(res)
					}
				})
		   },
		})
		
	},
	
	/**
	 * 组件的方法列表
	 */
	methods: {
		selectFile (files) {
			// 返回false可以阻止某次文件上传
		},
		uplaodFile (files) {
			files.tempFilePaths.map(file => {
				const key = 'miniprogram/' + Math.random().toString(36).substr(2) + '.jpg';
				const formData = {};

				if (this.data.huawei) {
					formData.policy = this.data.huawei.policy;
					formData.signature = this.data.huawei.signature;
					formData.AWSAccessKeyId = this.data.huawei.AccessKeyId;
					formData['Content-Type'] = 'image/jpeg';
				}

				formData.key = key;
				wx.uploadFile({
					url: app.globalData.imgHost,
					header: {},
					filePath: file,
					name: 'file',
					formData: formData,
					success: (res) => {
						// console.log('success', res);
						if (res.errMsg == 'uploadFile:ok') {
							if ( typeof (res.header.Location) == 'string' ) {
								this.setData({ 
									files: [...this.data.files, 
										{ url: app.globalData.imgHost + (res.header.Location).substring(res.header.Location.indexOf('/miniprogram')) }
									]
								})
							} else {
								let location = (res.header.Location)[0]
								this.setData({ 
									files: [...this.data.files, 
										{ url: app.globalData.imgHost + location.substring(location.indexOf('/miniprogram')) }
									]
								})
							}
							this.triggerEvent('change', this.data.files.map(file => file.url))
						}
					},
					complete: (res) => {
						console.log('complete', res);
					}
				})
			})
			// 文件上传的函数,返回一个promise
			return new Promise((resolve, reject) => {
				resolve(this.data.files)
			})
		},
		// 上传图片 // 参数: files
		uplaodFile2 (files) {
			wx.showLoading({ title: "正在加载图片", mask: true })
			const { tempFilePaths } = files
			// 获取动态生成canvas的数组
			let canvasArr = tempFilePaths.map( item => {
				if (typeof item == 'string') { return {'url': item} } else { return item }
			})
        	this.setData({ canvasArray: canvasArr })

			this.uploadFileAll(tempFilePaths).then(res => {
				console.log("所有接口都请求完了", res)
			})
			
			// 图片上传的函数,返回Promise,Promise的callback里面必须resolve({urls})表示成功,否则表示失败
			return new Promise((resolve, reject) => {
				resolve({ urls: this.data.promisePics })
				// 清空上传图片队列
				this.setData({ promisePics: [] })
			})
		},
		uploadFileAll (tempFilePaths) {
			return new Promise( (resolve, reject) => {
				let arr2 = []
				tempFilePaths.map( (item, index) => {
					const promise = new Promise( (res, rej) => {
						this.imgPromise(item, index)
						res()
					})
					arr2.push(promise)
				})
				Promise.all(arr2).then((result) => {
					resolve()  // 所有接口都执行完毕
				})
			} )
		},
		// 返回图片上传promise
		imgPromise (item, index) {
			return new Promise( (resolve, reject) => {
				const that = this
				wx.getImageInfo({
					src: item,
					success(res) {
						// 设置对应的canvas的宽高
						let canvasArray = that.data.canvasArray
						canvasArray[index].width = res.width
						canvasArray[index].height = res.height
						that.setData({ canvasArray: canvasArray })

						// canvas添加水印
						that.getCanvasImg(res, index).then(pImg => {
							resolve(pImg)
						}).catch(err => {
							reject(err)
						})
					}
				})
			})
		},
		// canvas添加水印
		getCanvasImg(imgInfo, index) {
			return new Promise( (resolve, reject) => {
				const that = this
				let { path, width, height } = imgInfo
			
				// 图片添加水印
				// 获取当前时间
				let newDate = new Date()
				let year = newDate.getFullYear() //年
				let month = newDate.getMonth() + 1 //月
				let day = newDate.getDate() //日
				var hour = newDate.getHours()
				var minute = newDate.getMinutes()
				var second = newDate.getSeconds()
				let roleNameInfo = '拍摄时间:' + year + '年' + month + '月' + day + '日 '+ hour+':'+minute +':' + second
				let address = '上海市'

				// 创建canvas
				const ctx = wx.createCanvasContext('canvasId'+index, that)
				ctx.drawImage(path, 0, 0, width, height) // 先画出图片
				// 将声明的时间放入canvas
				let fontSize = 30
				if (width >= 1500) { fontSize = 80 } else if (width >= 1000) { fontSize = 60 }

				ctx.setFontSize(fontSize) //注意:设置文字大小必须放在填充文字之前,否则不生效
				ctx.setFillStyle('rgba(255, 255, 255, 0.5)')
				ctx.shadowOffsetX = -6 //用来设定阴影在 X轴的延伸距
				ctx.shadowOffsetX = -6 //用来设定阴影在 Y轴的延伸距
				ctx.shadowBlur = 3 //设定阴影的模糊程度 默认0
				ctx.shadowColor = "rgba(0, 0, 0, 0.3)" //设定阴影颜色效果
				ctx.fillText(roleNameInfo, 20, height/8)
				ctx.fillText(that.data.address, 20, height/8+fontSize+10)

				ctx.draw(false, function () {
					// 绘画完成回调
					// 生成图片 把当前画布指定区域的内容导出生成指定大小的图片。在 draw() 回调里调用该方法才能保证图片导出成功
					wx.canvasToTempFilePath({
						quality: 1,
						fileType: 'jpg',
						canvasId: 'canvasId'+index,
						// destWidth: width, // 输出的图片的宽度
						// destHeight: height, // 输出的图片的高度
						// width: width,
						// height: height,
						success: function (res) {
							that.handleUpload(res.tempFilePath).then( _=> { // res.tempFilePath 最终图片路径
								resolve()
							})
						},
						fail: function(res) {
							reject(res)
						}
					}, that)
				})
			})

		},
		// 上传图片方法
		handleUpload (img) {
			return new Promise( (resolve, reject) => {
				const that = this
				const key = 'miniprogram/' + Math.random().toString(36).substr(2) + '.jpg'
				const formData = {};
				if (that.data.huawei) {
					formData.policy = that.data.huawei.policy
					formData.signature = that.data.huawei.signature
					formData.AWSAccessKeyId = that.data.huawei.AccessKeyId
					formData['Content-Type'] = 'image/jpeg'
				}
				formData.key = key
				wx.uploadFile({
					url: app.globalData.imgHost,
					filePath: img,
					name: 'file',
					formData: formData,
					success: (res) => {
						wx.hideLoading()
						if(res.errMsg == 'uploadFile:ok') { // 上传成功
							let url = ''
							let { pics, promisePics } = that.data
							promisePics.push( img )
							if ( typeof (res.header.Location) == 'string' ) {
								url = app.globalData.imgHost + res.header.Location.substring(res.header.Location.indexOf('/miniprogram'))
							} else {
								let location = (res.header.Location)[0]
								url = app.globalData.imgHost + location.substring(location.indexOf('/miniprogram'))
							}
							pics.push(url)
							that.setData({ pics, promisePics, files: [...this.data.files, { url: url } ] })
							that.triggerEvent('change', this.data.files.map(file => file.url))
						}
					},
					fail: (error) => {
						wx.hideLoading()
						console.log('error', error)
					}
				})
			})
		},
		delete (e) {
			this.data.files.splice(e.detail.index, 1)
			this.setData({ files: this.data.files })
			this.triggerEvent('change', this.data.files.map(file => file.url))
		},
		change: function () {
			this.triggerEvent('change', this.data)
		},
		uploadError(e) {
			console.log('upload error', e.detail)
		},
		uploadSuccess(e) {
			console.log('upload success', e.detail)
		}
	},
	export () {
		return this.data
	}
})

你可能感兴趣的:(前端,微信小程序,javascript,前端)