当前位置: 代码迷 >> 综合 >> uniapp APP端水印相机实现
  详细解决方案

uniapp APP端水印相机实现

热度:84   发布时间:2023-09-19 07:41:24.0

  使用插件https://ext.dcloud.net.cn/plugin?id=4892

 在插件功能上增加定位,定时功能,水印相机页面每十秒重新获取一次地址,时间,增加水印生canvas文本多行换行功能

uniapp APP端水印相机实现

 由于相机组件app不支持,所以插件使用 live-pusher 直播推流 组件实现的自定义相机功能。

 拍照页面使用nvue,可以实现应用内拍照以及拍照画面自定义元素等功能。

水印相机页watermarkCamera.nvue

<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="">{
     { 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></view>
</template><script>let _this = null;export default {data() {return {dotype: 'watermark',message: 'live-camer', //水印内容username: uni.getStorageSync('loginUserName'),address: '无法获取地址',time: '2022-2-14 10:23',poenCarmeInterval: null, //打开相机的轮询aspect: '2:3', //比例windowWidth: '', //屏幕可用宽度windowHeight: '', //屏幕可用高度camerastate: false, //相机准备好了livePusher: null, //流视频对象snapshotsrc: null ,//快照,timer:null,//定时器};},onLoad(e) {_this = this;if (e.dotype != undefined) this.dotype = e.dotype;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-PLUSif (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.watermark({path: _this.snapshotsrc,info: {username: this.username, address: this.address,time: this.time}});},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;top: 80rpx;left: 20rpx;z-index: 100;}.remind-text {color: #dddddd;width: 710rpx;}.remind-name{font-size: 40rpx;}.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>

相片展示和水印生成页index.vue

<template><view class="page"><nav-bar :background="'#0042b8'" :is-back="true" title="测试"></nav-bar><view style="height: 80rpx;"></view><navigator class="buttons" url="./watermarkCamera"><button type="primary">打开定制水印相机</button></navigator><view style="height: 80rpx;"></view><view>拍摄结果预览图,见下方</view><view class="img-list"><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>var _this;
export default {data() {return {windowWidth:'',windowHeight:'',imagesrc: null,imgList:[],canvasSiz:{width:188,height:273}};},onLoad() {_this= this;this.init();},methods: {//添加照片水印watermark(info){console.log("获取到的数据为",info)uni.getImageInfo({src: info.path,success: function(image) {console.log(image);_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) {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 {// 内容绘制完毕,但是剩下的内容宽度不到lineMaxWidthif (i === content.length - 1) {ctx.fillText(content.substring(drawIndex), drawX, drawY);return drawY;console.log("最后高度为",drawY);}}}}},//保存图片到相册,方便核查savePhoto(path){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>

uniapp APP端水印相机实现

 canvas文本换行方法:https://segmentfault.com/a/1190000017869922