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