uniapp-实现安卓app水印相机

669 阅读4分钟

写在前面的话:最近要配合项目输出带水印的图片,之前的实现的方式是调uniapp封装好的相机,然后在图片输出的时候用canvas,把水印绘制上去,但是老感觉没有水印相机看着舒服.改成了现在的这种方式。

1.相机实现

水印相机实现有两种方式,在小程序端可以用camera来实现,但在安卓端不支持camera,使用uniapp的live-pusher来实现相机。

而live-pusher推荐使用nvue来做,好处是

  1. nvue也可一套代码编译多端。
  2. nvue的cover-view比vue的cover-view更强大,在视频上绘制元素更容易。如果只考虑App端的话,不用cover-view,任意组件都可以覆盖组件,因为nvue没有层级问题
  3. 若需要视频内嵌在swiper里上下滑动(类抖音、映客首页模式),App端只有nvue才能实现 当然nvue相比vue的坏处是css写法受限,如果只开发微信小程序,不考虑App,那么使用vue页面也是一样的。
  • App平台:使用 <live-pusher/> 组件,打包 App 时必须勾选 manifest.json->App 模块权限配置->LivePusher(直播推流) 模块。

上代码!

<template>
	<view class="live-camera" :style="{ width: windowWidth, height: windowHeight }">
		<view class="preview" :style="{ width: windowWidth, height: windowHeight }">
			<live-pusher
				id="livePusher"
				ref="livePusher"
				class="livePusher"
				mode="FHD"
				beauty="0"
				whiteness="0"
				:aspect="aspect"
				min-bitrate="1000"
				audio-quality="16KHz"
				device-position="back"
				:auto-focus="true"
				:muted="true"
				:enable-camera="true"
				:enable-mic="false"
				:zoom="false"
				@statechange="statechange"
				:style="{ width: windowWidth, height: windowHeight }"
			></live-pusher>
			<!--这里修改水印的样式-->
			<cover-view class="remind">
				<text class="remind-text" style="">{{ message }}</text>
				<text class="remind-text" style="">经度:1002.32</text>
				<text class="remind-text" style="">纬度:1002.32</text>
			</cover-view>
		</view>
		<view class="menu">
			<!--底部菜单区域背景-->
			<cover-image class="menu-mask" src="/static/live-camera/bar.png"></cover-image>

			<!--返回键-->
			<cover-image class="menu-back" @tap="back" src="/static/live-camera/back.png"></cover-image>

			<!--快门键-->
			<cover-image class="menu-snapshot" @tap="snapshot" src="/static/live-camera/shutter.png"></cover-image>

			<!--反转键-->
			<cover-image class="menu-flip" @tap="flip" src="/static/live-camera/flip.png"></cover-image>
		</view>
	</view>
</template>

<script>
let _this = null;
export default {
	data() {
		return {
			dotype: 'watermark',
			message: '水印相机', //水印内容
			poenCarmeInterval: null, //打开相机的轮询
			aspect: '2:3', //比例
			windowWidth: '', //屏幕可用宽度
			windowHeight: '', //屏幕可用高度
			camerastate: false, //相机准备好了
			livePusher: null, //流视频对象
			snapshotsrc: null //快照
		};
	},
	onLoad(e) {
		_this = this;
		if (e.dotype != undefined) this.dotype = e.dotype;
		this.initCamera();
	},
	onReady() {
		this.livePusher = uni.createLivePusherContext('livePusher', this);
		this.startPreview(); //开启预览并设置摄像头
		this.poenCarme();
	},
	methods: {
		//轮询打开
		poenCarme() {
			//#ifdef APP-PLUS
			if (plus.os.name == 'Android') {
				this.poenCarmeInterval = setInterval(function () {
					console.log(_this.camerastate);
					if (!_this.camerastate) _this.startPreview();
				}, 2500);
			}
			//#endif
		},
		//初始化相机
		initCamera() {
			uni.getSystemInfo({
				success: function (res) {
					_this.windowWidth = res.windowWidth;
					_this.windowHeight = res.windowHeight;
					let zcs = _this.aliquot(_this.windowWidth, _this.windowHeight);
					_this.aspect = _this.windowWidth / zcs + ':' + _this.windowHeight / zcs;
					console.log('画面比例:' + _this.aspect);
				}
			});
		},

		//整除数计算
		aliquot(x, y) {
			if (x % y == 0) return y;
			return this.aliquot(y, x % y);
		},

		//开始预览
		startPreview() {
			this.livePusher.startPreview({
				success: (a) => {
					console.log(a);
				}
			});
		},

		//停止预览
		stopPreview() {
			this.livePusher.stopPreview({
				success: (a) => {
					_this.camerastate = false; //标记相机未启动
				}
			});
		},

		//状态
		statechange(e) {
			//状态改变
			console.log(e);
			if (e.detail.code == 1007) {
				_this.camerastate = true;
			} else if (e.detail.code == -1301) {
				_this.camerastate = false;
			}
		},

		//返回
		back() {
			uni.navigateBack();
		},

		//抓拍
		snapshot() {
			this.livePusher.snapshot({
				success: (e) => {
					_this.snapshotsrc = e.message.tempImagePath;
					_this.stopPreview();
					_this.setImage();
					uni.navigateBack();
				}
			});
		},

		//反转
		flip() {
			this.livePusher.switchCamera();
		},

		//设置
		setImage() {
			let pages = getCurrentPages();
			let prevPage = pages[pages.length - 2]; //上一个页面

			//直接调用上一个页面的setImage()方法,把数据存到上一个页面中去
			prevPage.$vm.setImage({ path: _this.snapshotsrc, dotype: this.dotype });
		}
	}
};
</script>

<style lang="scss">
.live-camera {
	justify-content: center;
	align-items: center;
	.preview {
		justify-content: center;
		align-items: center;
		.remind {
			position: absolute;
			bottom: 180rpx;
			left: 20rpx;
			width: 130px;
			z-index: 100;
			.remind-text {
				color: #dddddd;
				font-size: 40rpx;
				text-shadow: #fff 1px 0 0, #fff 0 1px 0, #fff -1px 0 0, #fff 0 -1px 0;
			}
		}
	}
	.menu {
		position: absolute;
		left: 0;
		bottom: 0;
		width: 750rpx;
		height: 180rpx;
		z-index: 98;
		align-items: center;
		justify-content: center;
		.menu-mask {
			position: absolute;
			left: 0;
			bottom: 0;
			width: 750rpx;
			height: 180rpx;
			z-index: 98;
		}
		.menu-back {
			position: absolute;
			left: 30rpx;
			bottom: 50rpx;
			width: 80rpx;
			height: 80rpx;
			z-index: 99;
			align-items: center;
			justify-content: center;
		}
		.menu-snapshot {
			width: 130rpx;
			height: 130rpx;
			z-index: 99;
		}
		.menu-flip {
			position: absolute;
			right: 30rpx;
			bottom: 50rpx;
			width: 80rpx;
			height: 80rpx;
			z-index: 99;
			align-items: center;
			justify-content: center;
		}
	}
}
</style>

2.水印图片绘制

图片水印返回上一页用<canvas>添加水印
<template>
	<view class="page">
		<view style="height: 80rpx;"></view>
		<navigator class="buttons" url="../camera/watermark/watermark"><button type="primary">打开定制水印相机</button></navigator>
		<view style="height: 80rpx;"></view>
		
		<view>拍摄结果预览图,见下方</view>
		<image  class="preview" :src="imagesrc" mode="aspectFit" style="width:710rpx:height:710rpx;margin: 20rpx;"></image>
		
		<canvas id="canvas-clipper" canvas-id="canvas-clipper" type="2d" :style="{width: canvasSiz.width+'px',height: canvasSiz.height+'px',position: 'absolute',left:'-500000px',top: '-500000px'}" />
	</view>
</template>

<script>
	var _this;
export default {
	data() {
		return {
			windowWidth:'',
			windowHeight:'',
			imagesrc: null,
			canvasSiz:{
				width:188,
				height:273
			}
		};
	},
	onLoad() {
		_this= this;
		this.init();
	},
	methods: {
		
		//设置图片
		setImage(e) {
			console.log(e);
			//显示在页面
			//this.imagesrc = e.path;
			if(e.dotype =='idphoto'){
				_this.zjzClipper(e.path);
			}else if(e.dotype =='watermark'){
				_this.watermark(e.path);
			}else{
				_this.savePhoto(e.path);
			}
		},
		
		
		//添加照片水印
		watermark(path){
			uni.getImageInfo({
				src: path,
				success: function(image) {
					console.log(image);
					
					_this.canvasSiz.width =image.width;
					_this.canvasSiz.height =image.height;
					
					//担心尺寸重置后还没生效,故做延迟
					setTimeout(()=>{
						let ctx = uni.createCanvasContext('canvas-clipper', _this);
						
						ctx.drawImage(
							path,
							0,
							0,
							image.width,
							image.height
						);
						
						//具体位置如需和相机页面上一致还需另外做计算,此处仅做大致演示
						ctx.setFillStyle('white');
						ctx.setFontSize(40);
						ctx.fillText('live-camera', 20, 100);
						
						
						//再来加个时间水印
						var now = new Date();
						var time= now.getFullYear()+'-'+now.getMonth()+'-'+now.getDate()+' '+now.getHours()+':'+now.getMinutes()+':'+now.getMinutes();
						ctx.setFontSize(30);
						ctx.fillText(time, 20, 140);

						ctx.draw(false, () => {
							uni.canvasToTempFilePath(
								{
									destWidth: image.width,
									destHeight: image.height,
									canvasId: 'canvas-clipper',
									fileType: 'jpg',
									success: function(res) {
										_this.savePhoto(res.tempFilePath);
									}
								},
								_this
							);
						});
					},500)
					
					
				}
			});
		},
		
		//保存图片到相册,方便核查
		savePhoto(path){
			this.imagesrc = path;
			//保存到相册
			uni.saveImageToPhotosAlbum({
				filePath: path,
				success: () => {
					uni.showToast({
						title: '已保存至相册',
						duration: 2000
					});
				}
			});
		},
		
		//初始化
		init(){
			let _this = this;
			uni.getSystemInfo({
				success: function(res) {
					_this.windowWidth = res.windowWidth;
					_this.windowHeight = res.windowHeight;
				}
			});
		}
		
	}
};
</script>

<style lang="scss">
.page {
	width: 750rpx; 
	justify-content: center;
	align-items: center;
	flex-direction:column;
	display: flex;
	.buttons {
		width: 600rpx;
	}
}


</style>

8dbee86b262efefc549933df666fbc7.jpg