有些app拍照时需要带有时间地址水印,防止随便上传图片拍照作假之类的行为

水印相机用法

首先 组件注册

  • 在需要该操作的页面引入组件并注册
    html
    	<mark-camera ref="camera"></mark-camera>
    
    js
    	// 引入
    	import MarkCamera from "@/components/water-mark-camera/index.vue"
    

其次 定义一个方法跳转到拍照页面去

  • 路由
    js

    	//打开相机
    		openCamera(){
    			uni.navigateTo({
    				url:'/pages/my/settings/water-mark/water-mark',
    			})
    		},
    
  • 定义回调函数,函数名不可更改,用于拍照页面往回传递参数
    js

    	//回调
    	setImage(info){
    		this.$refs.camera.watermark(info)
    	},
    
注意:
  • 拍照页面 直接调用上一个页面的setImage()方法,把数据存到上一个页面中去
    js
    	//返回的参数 用于canvas写入画布中的数据
    	// {
    	// 	path: _this.snapshotsrc, //照片路径
    	// 	info: {
    	// 		username: this.username, //标题名
    	// 		address: this.address, //地址
    	// 		time: this.time //日期时间
    	// 	},
    	// }
    	
    	
    	//给上一页 设置
    	setImage() {
    		let pages = getCurrentPages();
    		let prevPage = pages[pages.length - 2]; //上一个页面
    		//直接调用上一个页面的setImage()方法,把数据存到上一个页面中去
    		prevPage.$vm.setImage({
    			path: _this.snapshotsrc, //照片路径
    			info: {
    				username: this.username, //标题名
    				address: this.address, //地址
    				time: this.time //日期时间
    			},
    		})
    		uni.navigateBack();
    	}
    
另:拍照时的水印样式和保存后的水印样式 需要在两处调整
  • 拍照时的需要在拍照页css处调整
  • 保存后的为canvas后添加的样式需要在js中调整
附组件源码
<template>
	<!-- 水印相机 -->
    <view class="page">
        <view style="height: 80rpx;"></view>
        <view v-if="isShowList">拍摄结果预览图,见下方</view>
        <view class="img-list" v-if="isShowList">
            <view class="img-item" v-for="(item,index) in imgList" :key="index" @click="lookImg(index)">
                <image :src="item"></image>
            </view>
        </view>
        <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>
export default {
	props:{
		//显示拍摄结果预览图
		isShowList:{
			type:Boolean,
			default:true
		}
	},
    data() {
        return {
            windowWidth:'',
            windowHeight:'',
            imagesrc: null,
            imgList:[],
            canvasSiz:{
                width:188,
                height:273
            }
        };
    },
    onLoad() {
        // _this= this;
        // this.init();
    },
    methods: {
        //添加照片水印
        watermark(info){
			let _this = this
			_this.init();
            // console.log("获取到的数据为",info)
            uni.getImageInfo({
                src: info.path,
                success: function(image) {
                    console.log(image,_this.canvasSiz);
                    _this.canvasSiz.width =image.width;
                    _this.canvasSiz.height =image.height;
                    let maxWidth = image.width - 60; 
                    // console.log("获取最大宽度",maxWidth)
                    //担心尺寸重置后还没生效,故做延迟
                    setTimeout(()=>{
                        let ctx = uni.createCanvasContext('canvas-clipper', _this);
                        ctx.drawImage(
                            info.path,
                            0,
                            0,
                            image.width,
                            image.height
                        );
                        
                        //具体位置如需和相机页面上一致还需另外做计算,此处样式调整
                        ctx.setFillStyle('white');
                        ctx.setFontSize(50);
                        
                        ctx.fillText(info.info.username, 20, 150);
                        ctx.setFontSize(50);
                        let previousRowHeight =  _this.textPrewrap(ctx,info.info.address,20,220,70,maxWidth,3);
 
                        //再来加个时间水印
                        ctx.setFontSize(40);
                        ctx.fillText(info.info.time, 20, previousRowHeight+70);
 
                        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)
                    
                    
                }
            });
        },
        /**
        ctx: 画布的上下文环境
        content: 需要绘制的文本内容
        drawX: 绘制文本的x坐标
        drawY: 绘制文本的y坐标
        lineHeight:文本之间的行高
        lineMaxWidth:每行文本的最大宽度
        lineNum:最多绘制的行数
        */
        textPrewrap(ctx, content, drawX, drawY, lineHeight, lineMaxWidth, lineNum) {
			let _this = this
            var drawTxt = ''; // 当前绘制的内容
            var drawLine = 1; // 第几行开始绘制
            var drawIndex = 0; // 当前绘制内容的索引
            // 判断内容是否可以一行绘制完毕
            if (ctx.measureText(content).width <= lineMaxWidth) {
                ctx.fillText(content.substring(drawIndex, i), drawX, drawY);
            } else {
                    for (var i = 0; i < content.length; i++) {
                    drawTxt += content[i];
                    if (ctx.measureText(drawTxt).width >= lineMaxWidth) {
                        if (drawLine >= lineNum) {
                            ctx.fillText(content.substring(drawIndex, i) + '..', drawX, drawY);
                            break;
                        } else {
                            ctx.fillText(content.substring(drawIndex, i + 1), drawX, drawY);
                            drawIndex = i + 1;
                            drawLine += 1;
                            drawY += lineHeight;
                            drawTxt = '';
                        }
                    } else {
                        // 内容绘制完毕,但是剩下的内容宽度不到lineMaxWidth
                        if (i === content.length - 1) {
                            ctx.fillText(content.substring(drawIndex), drawX, drawY);
                            return drawY;
                            console.log("最后高度为",drawY);
                        }
                    }
                }
            }
        },
 
        //保存图片到相册,方便核查
        savePhoto(path){
			let _this = this
            this.imgList.push(path)
            this.imagesrc = path;
            // 保存到相册
            uni.saveImageToPhotosAlbum({
                filePath: path,
                success: () => {
                    uni.showToast({
                        title: '已保存至相册',
                        duration: 2000
                    });
                }
            });
        },
        lookImg(index){
            // 预览图片
            uni.previewImage({
                current:index,
                urls: this.imgList,
            });
        },
        //初始化
        init(){
            let _this = this;
            uni.getSystemInfo({
                success: function(res) {
                    _this.windowWidth = res.windowWidth;
                    _this.windowHeight = res.windowHeight;
                }
            });
        }
        
    }
};
</script>
 
<style lang="less">
.page {
    width: 750rpx; 
    justify-content: center;
    align-items: center;
    flex-direction:column;
    display: flex;
    .buttons {
        width: 600rpx;
    }
}
.img-list{
    padding: 20rpx;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    flex-wrap: wrap;
}
.img-item{
    width: 100rpx;
    height: 100rpx;
    margin-right: 20rpx;
    margin-bottom: 20rpx;
    
}
.img-item image{
    width: 100%;
    height: 100%;
}
 
</style>

拍照页面源码
<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="false"
				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 remind-name" style="">{{ prjName}}:{{username}}</text>
				<text class="remind-text remind-address" style="">{{ address }}</text>
				<text class="remind-text remind-time" style="">{{ time }}</text>
			</cover-view>
		</view>
		<view class="menu">
			<!--底部菜单区域背景-->
			<cover-image class="menu-mask" src="@/static/camera/bar.png"></cover-image>

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

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

			<!--反转键-->
			<cover-image class="menu-flip" @tap="flip" src="@/static/camera/flip.png"></cover-image>
		</view>
		<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>
	let _this = null;
	export default {
		data() {
			return {
				message: 'live-camer', //水印内容
				username: '张三',
				prjName:'项目名称',
				address: '无法获取地址',
				time: '2022-2-14 10:23',
				poenCarmeInterval: null, //打开相机的轮询
				aspect: '2:3', //比例
				windowWidth: '', //屏幕可用宽度
				windowHeight: '', //屏幕可用高度
				camerastate: false, //相机准备好了
				livePusher: null, //流视频对象
				snapshotsrc: null, //快照,
				timer: null, //定时器
				// ----------添加水印
				imagesrc: null,//照片src
				//canvas尺寸
				canvasSiz:{
				    width:188,
				    height:273
				}
			};
		},
		onLoad(e) {
			_this = this;
			this.initCamera();
		},
		onReady() {
			this.getAddress();
			let date = new Date()
			this.time = this.dateFormat("YYYY-mm-dd HH:MM", date);
			this.livePusher = uni.createLivePusherContext('livePusher', this);
			this.startPreview(); //开启预览并设置摄像头
			this.poenCarme();
		},
		onShow() {
			clearInterval(this.timer)
			// 每隔10秒刷新地址和时间
			this.timer = setInterval(() => {
				this.getAddress();
				let date = new Date()
				this.time = this.dateFormat("YYYY-mm-dd HH:MM", date);
			}, 10000);
		},
		onUnload() {
			clearInterval(this.timer)
		},
		methods: {
			getAddress() {
				uni.getLocation({
					type: 'gcj02',
					geocode: true,
					isHighAccuracy: true,
					success: (res) => {
						this.address = res.address.province + res.address.city + res.address.district + res
							.address.street + res.address.streetNum + res.address.poiName;
						console.log('当前位置:', this.address);
						// console.log('当前位置的经度:' + res.longitude);
						// console.log('当前位置的纬度:' + res.latitude);
					}
				});
			},
			//轮询打开
			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; //标记相机未启动
						// clearInterval(_this.poenCarmeInterval)
						console.log('清定轮询,相机未启动-----');
					}
				});
			},

			//状态
			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();
					}
				});
			},

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

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

				//直接调用上一个页面的setImage()方法,把数据存到上一个页面中去
				prevPage.$vm.setImage({
					path: _this.snapshotsrc,
					info: {
						username: this.username,
						address: this.address,
						time: this.time
					},
				})
				uni.navigateBack();
			},
			dateFormat(fmt, date) {
				let ret;
				const opt = {
					"Y+": date.getFullYear().toString(), // 年
					"m+": (date.getMonth() + 1).toString(), // 月
					"d+": date.getDate().toString(), // 日
					"H+": date.getHours().toString(), // 时
					"M+": date.getMinutes().toString(), // 分
					"S+": date.getSeconds().toString() // 秒
					// 有其他格式化字符需求可以继续添加,必须转化成字符串
				};
				for (let k in opt) {
					ret = new RegExp("(" + k + ")").exec(fmt);
					if (ret) {
						fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1]
							.length, "0")))
					};
				};
				return fmt;
			},
			
		}
	};
</script>

<style lang="less">
	.live-camera {
		justify-content: center;
		align-items: center;

	}

	.preview {
		justify-content: center;
		align-items: center;

	}

	.remind {
		position: absolute;
		bottom: 240rpx;
		left: 20rpx;
		z-index: 100;
		background-color: rgba(176, 192, 217, 0.5);
		border-radius: 10rpx;
		overflow: hidden;
	}

	.remind-text {
		box-sizing: border-box;
		padding:0 20rpx;
		color: #dddddd;
		width: 400rpx;
		min-height: 60rpx;
		line-height: 50rpx;
	}

	.remind-name {
		height: 80rpx;
		line-height: 80rpx;
		font-size: 40rpx;
		font-weight: 600;
		color: #fff;
		background-color: rgba(76, 176, 229, 0.4);
	}

	.remind-address {
		font-size: 36rpx;
	}

	.remind-time {
		font-size: 30rpx;
	}


	.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>

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐