微信小程序调用相机选择图片生成水印

项目近期有一个需求,是在小程序使用相机拍照时,页面缩略图和上传服务器(或保存本地)的都是带水印的,水印文案是当前的时间和当前所处的地点。

前期使用了微信小程序的wx.chooseImage相机拍照,再使用拍摄后的图片添加水印,上传或者保存本地,这样做发现,每保存一次,相册里面都添加了两张图片,测试后发现,wx.chooseImage相机拍照默认保存到相册了,这样的话就会有问题,后期就改成了自己调用相机上传图片,发现是可以实现的,代码如下:

camera.wxml:

<view class="cameraWrapper" hidden="{{ markPhoto }}">
  <view class='camera'>
    <camera wx:if="{{isAuth}}" device-position="back" flash="off" binderror="error"></camera>
  </view>
  <view class="btn-group">
    <mp-icon class="iconBtn returnBtn" icon="previous" color="#666" size="34" bindtap="returnCarmera"></mp-icon>
    <button class="takePhoto" type="primary" plain="{{true}}" bindtap="camera"><text></text></button>
  </view>
  <!-- 添加水印 -->
	<view class="canvas-cont">
		<canvas canvas-id='firstCanvas' style="width: {{w}}px;height: {{h}}px;"></canvas>
	</view>
</view>

<!-- 拍摄后生成水印图片 -->
<view class="camera-cont" hidden="{{ !markPhoto }}">
	<view class="preview-image-cont">
		<image class="preview-image" src="{{markPhoto}}" mode="aspectFit"></image>
	</view>
	<view class="btn-group btn-group-padding" style="{{ markPhoto=='' ? 'display:none' : ''}}">
		<mp-icon class="iconBtn" icon="previous" color="#666" size="44" bindtap="againBtn"></mp-icon>
		<icon class="icon-box-img" type="success" size="60" bindtap="saveBtn"></icon>
	</view>
</view>

camera.js:

const util = require('../../../utils/util')
const app = getApp()
Page({
	data: {
		isAuth: false,
		markPhoto: null,
		prePage: null // 从哪个页面进入
	},
	onLoad: function (options) {
		this.setData({ prePage: options.page })
		wx.showLoading({ title: "正在加载中...", mask: true })
		app.globalData.pageName = this // 将globalData的页面指向自己
		const that = this
		wx.getSetting({
			success: res => {
				if (res.authSetting['scope.camera']) {
					// 用户已经授权
					wx.hideLoading()
					that.setData({ isAuth: true })
				} else {
					// 用户还没有授权,向用户发起授权请求
					wx.authorize({
						scope: 'scope.camera',
						success() { // 用户同意授权
							wx.hideLoading()
							that.setData({ isAuth: true })
						},
						fail() { // 用户不同意授权
							that.openSetting().then(res => {
								wx.hideLoading()
								that.setData({ isAuth: true })
							})
						}
					})
				}
			},
			fail: res => {
				wx.hideLoading()
				console.log('获取用户授权信息失败')
			}
		})
	},
	// 打开授权设置界面
	openSetting: function () {
		const that = this
		let promise = new Promise((resolve, reject) => {
		wx.showModal({
		  title: '授权',
		  content: '请先授权获取摄像头权限',
		  success(res) {
			if (res.confirm) {
			  wx.openSetting({
				success(res) {
				  if (res.authSetting['scope.camera']) { // 用户打开了授权开关
					resolve(true)
				  } else { // 用户没有打开授权开关, 继续打开设置页面
					that.openSetting().then(res => { resolve(true) })
				  }
				},
				fail(res) {
				  console.log(res)
				}
			  })
			} else if (res.cancel) {
			  that.openSetting().then(res => { resolve(true) })
			}
		  }
	  })
	  })
	  return promise;
	},
  	// 服务站端-水印相机-调用摄像头拍照
  	camera: function () {
		const that = this
		if (app.globalData.address) {
			const ctx = wx.createCameraContext()
			ctx.takePhoto({
				quality: 'normal',
				success: (res) => {
					// console.log('res拍照', res)
					wx.showLoading({ title: "正在加载图片...", mask: true })
					that.addMark(res.tempImagePath)
				},
				fail (error) {
					wx.showToast({ title: error.errMsg, icon: 'none', duration: 2000 })
					setTimeout( () => { wx.navigateBack() }, 2000)
				}
			})
		} else {
			wx.showModal({ 
				title: '提示', content: '请先授权获取当前地理位置',
				success (res) {
					if (res.confirm) {
						app.getLocation('again')
					} else if (res.cancel) {
						wx.navigateBack()
					}
				}
			})
		}
	},
	// 获取图片信息
	addMark: function (file) {
		const that = this
		wx.getImageInfo({
			src: file,
			success(res2) {
				that.getCanvasImg(res2)
			}
		})
	},
  	// 服务站端-相机-canvas添加水印
	getCanvasImg: function (imgInfo) {
		wx.showLoading({ title: "图片努力生成中...", mask: true })
		const that = this
		const today = util.formatTime(new Date())
		const addressTxt = app.globalData.address
		let { path, width, height } = imgInfo
		that.setData({ w: width, h: height }) // 720 1206
		// 创建canvas
		const ctx = wx.createCanvasContext('firstCanvas', that)
		ctx.drawImage(path, 0, 0, width, height) // 先画出图片 地址,在canvas上X轴的位置,在canvas上y轴的位置,图片的宽度,图片的高度
		let fontSize = 30
		let rectY = height - 140
		let rectH = 140
		let imgWidth = 100
		let timeY = height - 80
		let addressY = height - 40
		let logoX = width - 120
		let logoY = height - 120
		let txtMaxWidth = width - imgWidth - 80
		if (addressTxt.length > 20) {
			rectY = height - 180
			rectH = 180
			imgWidth = 140
			timeY = height - 120
			addressY = height - 76
			logoX = width - 160
			logoY = height - 160
			txtMaxWidth = width - imgWidth - 80
		}
		ctx.setFontSize(fontSize) //注意:设置文字大小必须放在填充文字之前,否则不生效
		ctx.setFillStyle('rgba(0, 0, 0, .3)')
		ctx.fillRect(0, rectY, width, rectH)
		ctx.drawImage('/assets/images/logo.png', logoX, logoY, imgWidth, imgWidth)
		ctx.setFillStyle('rgba(255, 255, 255, 1)')
		ctx.fillText(today, 30, timeY)
		if (addressTxt.length < 20) {
			ctx.fillText(addressTxt, 30, addressY)
		} else {
			var chr = addressTxt.split("");//这个方法是将一个字符串分割成字符串数组
			var temp = "";
			var row = [];
			for (var a = 0; a < chr.length; a++) {
				if (ctx.measureText(temp).width < txtMaxWidth) {
					temp += chr[a];
				} else {
					a--; //这里添加了a-- 是为了防止字符丢失,效果图中有对比
					row.push(temp);
					temp = "";
				}
			}
			row.push(temp); 
		
			// 如果数组长度大于2 则截取前两个
			if (row.length > 2) {
				var rowCut = row.slice(0, 2);
				var rowPart = rowCut[1];
				var test = "";
				var empty = [];
				for (var a = 0; a < rowPart.length; a++) {
					if (ctx.measureText(test).width < 460) {
						test += rowPart[a];
					} else {
						break;
					}
				}
				empty.push(test);
				var group = empty[0] + "..." // 这里只显示两行,超出的用...表示
				rowCut.splice(1, 1, group);
				row = rowCut;
			}
			for (var b = 0; b < row.length; b++) {
				ctx.fillText(row[b], 30, addressY + b * 40, txtMaxWidth);
			}
		}
		ctx.draw(false, (() => {
			setTimeout( () => {
				// 生成图片把当前画布指定区域的内容导出生成指定大小的图片。在 draw() 回调里调用该方法才能保证图片导出成功
				wx.canvasToTempFilePath({
					quality: 0.5,
					fileType: 'jpg',
					canvasId: 'firstCanvas',
					success: function (res) {
						wx.hideLoading()
						that.setData({ 'markPhoto': res.tempFilePath })
					},
					fail: function(error) {
						wx.hideLoading()
						wx.showToast({ title: error.errMsg, icon: 'none', duration: 2000 })
					}
				}, that)
			}, 100)
		})())
	},
	// 重拍
	againBtn: function () {
		this.setData({ 'markPhoto': null })
	},
	// 保存图片到相册
	saveBtn: function () {
		const that = this
		if (that.data.prePage == 'complete') {
			let pages = getCurrentPages() // 获取当前页面
			let prePage = pages[pages.length - 2] // 获取上一页面
			prePage.setData({
				'markPhoto': that.data.markPhoto     //给上一页面的变量赋值
			})
			prePage.uploadMark(that.data.markPhoto) // 调用上一页面的方法(加载数据)
			wx.navigateBack({ delta: 1 }) // 返回上一页面
		} else {
			wx.saveImageToPhotosAlbum({ // 保存图片到系统相册
				filePath: that.data.markPhoto,
				success(res) {
					that.setData({ 'markPhoto': null })
				}
			})
		}
	},
	// 相机返回
	returnCarmera: function () {
		wx.navigateBack()
	}
})

camera.json:

{
    "component": true,
    "usingComponents": {
      "mp-icon": "weui-miniprogram/icon/icon"
    },
    "enablePullDownRefresh": true
  }

camera.wxss:


/* 模拟相机 */
.cameraWrapper {
    width: 100vw;
    height: 100vh;
    display: flex;
    flex-direction: column;
}
.camera {
    flex: 1;
    width: 100%;
    position: relative;
}
.camera camera {
    width: 100vw;
    height: calc(100vh - 200rpx);
    box-sizing: border-box;
}
.btn-group {
    position: relative;
    height: 200rpx;
    background-color: #fff;
    display: flex;
    align-items: center;
    justify-content: space-around;
}
.btn-group button.takePhoto:not([size='mini']) {
    position: relative;
    width: 120rpx;
    height: 120rpx;
    border-radius: 50%;
    border-width: 4rpx;
}
.btn-group button.takePhoto:not([size='mini']) text {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 90rpx;
    height: 90rpx;
    border-radius: 50%;
    margin: auto;
    background-color: #07c160;
}
.btn-group .iconBtn {
    border: 4rpx solid #666;
    border-radius: 50%;
}
.btn-group .returnBtn {
    position: absolute;
    bottom: 19%;
    left: 14%;
}
.cameraWrapper .canvas-cont {
    width: 0px;
    height: 0px;
    position: fixed;
    left: 90000000px;
    z-index: -999;
    overflow: hidden;
}

/* 拍摄后生成水印图片 */
.camera-cont {
    width: 100vw;
    height: 100vh;
}
.camera-cont .preview-image-cont {
    height: calc(100% - 200rpx);
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}
.camera-cont .preview-image {
    width: 100%;
    height: 100%;
}
.camera-cont .btn-group-padding {
    padding: 0 12%;
}
.camera-cont .icon-box-img {
    margin-top: -4rpx;
}

let pages = getCurrentPages() // 获取当前页面
let prePage = pages[pages.length - 2] // 获取上一页面
prePage.setData({‘markPhoto’: that.data.markPhoto}) //给上一页面的变量赋值
prePage.uploadMark(that.data.markPhoto) // 调用上一页面的方法(加载数据)
– prePage.uploadMark()是调用上一个页面的方法

// 页面调用子组件的方法
uploadMark: function (markPhoto) {
this.selectComponent(‘#uploadMarkId’).handleUpload(markPhoto)
},
uploadMarkChoose: function (pictureSource) {
this.selectComponent(‘#uploadMarkId’).chooseImage(pictureSource)
}

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值