uniapp+h5微信浏览器,调用摄像头生成九宫格辅助线

Uniapp+H5微信浏览器调用摄像头生成九宫格辅助线

 安卓和ios均适用

<template>
	<view>
		<view class="" style="width: 100vw;height: 100vh;background-color: #000;" v-if="!imageUrl">
			<video style="width: 100vw;height:calc(100vh - 240rpx);" object-fit="fill" id="video" playsinline autoplay
				muted></video>
			<view class=""
				style="width: 100vw;height: 100vh;background-color: transparent;opacity: 1;position: absolute;top: 0; left: 0;z-index: 1;">
				<!-- <view class="flex justify-center align-center"
					style="width: 100vw;height: 100vh;position: absolute;top: 0; left: 0;z-index: 2;">
					<image :src="qjkImgSrc" mode="widthFix" style="width: 600rpx;margin-top: -200rpx;"></image>
				</view> -->
				<view class="flex justify-center align-center"
					style="width: 100%;height:calc(100vh - 240rpx);position: absolute;top: 0; left: 0;z-index: 3;">
					<!-- <image :src="qjtxkImgSrc" mode="widthFix" style="width: 500rpx;margin-top: -200rpx;"></image> -->
					<image :src="qjtxkImgSrc" style="width: 100%;height: 100%;"></image>
				</view>

				<view class="flex justify-center"
					style="width: 100vw;height: 100vh;position: absolute;top: 0; left: 0;z-index: 5;">
					<!-- <view class=""
						style="position: absolute;bottom: 88rpx;left: 68rpx; color: #fff;font-weight: bold;background: #fff;border-radius: 16rpx;">
						<uni-icons type="close" :size="32" @click="handlePhotographCloseClick">
						</uni-icons>
					</view> -->
					<view class="outer-ring" style="position: absolute;bottom: 40rpx;left: 50%;margin-left: -50px;"
						@click="handlePhotographClick">
						<view class="middle-ring">
							<view class="inner-ring"></view>
						</view>
					</view>
					<!-- <view class=""
						style="position: absolute;bottom: 88rpx;right: 68rpx; color: #fff;font-weight: bold;background: #fff;border-radius: 16rpx;">
						<uni-icons type="folder-add" :size="32" @click="handleAddPhotographClick">
						</uni-icons>
					</view> -->
				</view>


			</view>
		</view>

		<view v-else>
			<image style="width: 300px;" :src="imageUrl"></image>
		</view>
	</view>

</template>

<script>
	export default {
		data() {
			return {
				// 背景透明的取景框图片
				// qjkImgSrc: "/static/images/qjk.png",
				// 背景透明的头像框图片
				qjtxkImgSrc: "/static/images/biaochi.png",
				imageUrl: "",
				// 媒体流,用于关闭摄像头
				mediaStreamTrack: null,
			};
		},
		onLoad() {
			this.invokingCamera();
		},
		onUnload() {
			this.handlePhotographCloseClick();
		},
		methods: {
			invokingCamera() {
				const self = this;
				// 注意本例需要在HTTPS协议网站中运行,新版本Chrome中getUserMedia接口在http下不再支持。

				// 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象
				if (navigator.mediaDevices === undefined) {
					navigator.mediaDevices = {};
				}

				// 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia
				// 因为这样可能会覆盖已有的属性。这里我们只会在没有getUserMedia属性的时候添加它。
				if (navigator.mediaDevices.getUserMedia === undefined) {
					navigator.mediaDevices.getUserMedia = function(constraints) {
						// 首先,如果有getUserMedia的话,就获得它
						const getUserMedia =
							navigator.getUserMedia ||
							navigator.webkitGetUserMedia ||
							navigator.mozGetUserMedia;

						// 一些浏览器根本没实现它 - 那么就返回一个error到promise的reject来保持一个统一的接口
						if (!getUserMedia) {
							return Promise.reject(
								new Error("getUserMedia is not implemented in this browser")
							);
						}

						// 否则,为老的navigator.getUserMedia方法包裹一个Promise
						return new Promise(function(resolve, reject) {
							getUserMedia.call(navigator, constraints, resolve, reject);
						});
					};
				}

				uni.getSystemInfo({
					success: function(res) {
						const constraints = {
							audio: false,
							video: {
								// 前置摄像头
								// facingMode: "user",
								// 后置摄像头
								facingMode: { exact: "environment" },
								// 手机端相当于高
								width: Math.max(res.windowWidth, res.windowHeight) - 120,
								// 手机端相当于宽
								height: Math.min(res.windowWidth, res.windowHeight),
							},
						};

						navigator.mediaDevices
							.getUserMedia(constraints)
							.then(function(stream) {
								self.mediaStreamTrack = stream;

								const video = document.querySelector("video");
								// // let video = document.getElementById("video");


								video.setAttribute('autoplay', '');
								video.setAttribute('muted', '');
								video.setAttribute('playsinline', '');
								// console.log('video对象:',video);
								// // 旧的浏览器可能没有srcObject
								if ("srcObject" in video) {
									// console.log('"srcObject" in video');
									video.srcObject = stream;
								} else {
									// console.log('"srcObject" not in video');
									// 防止在新的浏览器里使用它,应为它已经不再支持了
									video.src = window.URL.createObjectURL(stream);
								}
								// console.log('video对象2:',video);
								// video.onloadedmetadata = function(e) {
								// console.log('video.onloadedmetadata() success');
								video.play();
								// console.log('video.play() success');
								// };
							})
							.catch(function(err) {
								console.log('错误1:', err.name + ": " + err.message);
							});
					},
				});
			},
			handlePhotographCloseClick() {
				if (this.mediaStreamTrack) {
					// 关闭摄像头
					this.mediaStreamTrack.getTracks().forEach(function(track) {
						track.stop();
					});
					this.mediaStreamTrack = null;
				}
			},
			handlePhotographClick() {
				const self = this;
				const canvas = document.createElement("canvas");
				const ctx = canvas.getContext("2d");
				const video = document.querySelector("video");
				canvas.width = Math.min(video.videoWidth, video.videoHeight);
				canvas.height = Math.max(video.videoWidth, video.videoHeight);
				ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

				// ****** 镜像处理 ******
				// function getPixel(imageData, row, column) {
				// 	const uint8ClampedArray = imageData.data;
				// 	const width = imageData.width;
				// 	const height = imageData.height;
				// 	const pixel = [];
				// 	for (let i = 0; i < 4; i++) {
				// 		pixel.push(uint8ClampedArray[row * width * 4 + column * 4 + i]);
				// 	}
				// 	return pixel;
				// }

				// function setPixel(imageData, row, column, pixel) {
				// 	const uint8ClampedArray = imageData.data;
				// 	const width = imageData.width;
				// 	const height = imageData.height;
				// 	for (let i = 0; i < 4; i++) {
				// 		uint8ClampedArray[row * width * 4 + column * 4 + i] = pixel[i];
				// 	}
				// }

				// const mirrorImageData = ctx.createImageData(canvas.width, canvas.height);
				// const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
				// for (let h = 0; h < canvas.height; h++) {
				// 	for (let w = 0; w < canvas.width; w++) {
				// 		const pixel = getPixel(imageData, h, canvas.width - w - 1);
				// 		setPixel(mirrorImageData, h, w, pixel);
				// 	}
				// }
				// ctx.putImageData(mirrorImageData, 0, 0);
				// ****** 镜像处理 ******

				self.$nextTick(() => {
					const base64 = canvas.toDataURL("image/jpeg");
					self.imageUrl = base64;
					console.log('图片地址:', base64);
					self.handlePhotographCloseClick();
				});
			},
			handleAddPhotographClick() {
				this.uploadImage();
			},
			uploadImage: function() {
				const self = this;
				uni.chooseImage({
					count: 1,
					sizeType: ["compressed"],
					success: function(res) {
						self.handlePhotographCloseClick();
						const file = res.tempFiles[0];

						const reader = new FileReader();
						reader.readAsDataURL(file);
						reader.onload = function(e) {
							self.imageUrl = e.target.result;
						};
					},
				});
			},
		},
	};
</script>

<style scoped>
	video {
		/* transform: rotateY(180deg); */
		/* -webkit-transform: rotateY(180deg); */
		/* Safari 和 Chrome */
		/* -moz-transform: rotateY(180deg); */
	}

	/deep/ .uni-video-bar {
		display: none;
	}

	/deep/ .uni-video-cover {
		display: none;
	}

	.outer-ring {
		width: 160rpx;
		height: 160rpx;
		border-radius: 50%;
		background-color: #fff;
		display: flex;
		justify-content: center;
		align-items: center;
	}

	.middle-ring {
		width: 150rpx;
		height: 150rpx;
		border-radius: 50%;
		background-color: #000000;
		display: flex;
		justify-content: center;
		align-items: center;
	}

	.inner-ring {
		width: 140rpx;
		height: 140rpx;
		border-radius: 50%;
		background-color: #fff;
	}
</style>

示例:

接入项目

takephoto.vue

<template>
	<view>
		<view class="" style="width: 100vw;height: 100vh;background-color: #000;">
			<video style="width: 100vw;height:calc(100vh - 240rpx);" object-fit="fill" id="video" playsinline autoplay
				muted></video>
			<view class=""
				style="width: 100vw;height: 100vh;background-color: transparent;opacity: 1;position: absolute;top: 0; left: 0;z-index: 1;">
				<!-- <view class="flex justify-center align-center"
					style="width: 100vw;height: 100vh;position: absolute;top: 0; left: 0;z-index: 2;">
					<image :src="qjkImgSrc" mode="widthFix" style="width: 600rpx;margin-top: -200rpx;"></image>
				</view> -->
				<view class="flex justify-center align-center"
					style="width: 100%;height:calc(100vh - 240rpx);position: absolute;top: 0; left: 0;z-index: 3;">
					<!-- <image :src="qjtxkImgSrc" mode="widthFix" style="width: 500rpx;margin-top: -200rpx;"></image> -->
					<image :src="qjtxkImgSrc" style="width: 100%;height: 100%;"></image>
				</view>

				<view class="flex justify-center"
					style="width: 100vw;height: 100vh;position: absolute;top: 0; left: 0;z-index: 5;">
					<!-- <view class=""
						style="position: absolute;bottom: 88rpx;left: 68rpx; color: #fff;font-weight: bold;background: #fff;border-radius: 16rpx;">
						<uni-icons type="close" :size="32" @click="handlePhotographCloseClick">
						</uni-icons>
					</view> -->
					<view class="outer-ring" style="position: absolute;bottom: 40rpx;left: 50%;margin-left: -50px;"
						@click="handlePhotographClick">
						<view class="middle-ring">
							<view class="inner-ring"></view>
						</view>
					</view>
					<!-- <view class=""
						style="position: absolute;bottom: 88rpx;right: 68rpx; color: #fff;font-weight: bold;background: #fff;border-radius: 16rpx;">
						<uni-icons type="folder-add" :size="32" @click="handleAddPhotographClick">
						</uni-icons>
					</view> -->
				</view>


			</view>
		</view>

		<!-- <view v-else>
			<image style="width: 300px;" :src="imageUrl"></image>
		</view> -->
	</view>

</template>

<script>
	import { EventBus } from '@/utils/event-bus.js';
	import $ from '@/utils/js/jquery-3.3.1.min.js';
	
	export default {
		data() {
			return {
				// 背景透明的取景框图片
				// qjkImgSrc: "/static/images/qjk.png",
				// 背景透明的头像框图片
				qjtxkImgSrc: "/static/images/biaochi.png",
				imageUrl: "",
				// 媒体流,用于关闭摄像头
				mediaStreamTrack: null,
				photoName: ''
			};
		},
		onLoad(option) {
			this.photoName = option.photoName
			this.invokingCamera();
		},
		onUnload() {
			this.handlePhotographCloseClick();
		},
		methods: {
			invokingCamera() {
				const self = this;
				// 注意本例需要在HTTPS协议网站中运行,新版本Chrome中getUserMedia接口在http下不再支持。

				// 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象
				if (navigator.mediaDevices === undefined) {
					navigator.mediaDevices = {};
				}

				// 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia
				// 因为这样可能会覆盖已有的属性。这里我们只会在没有getUserMedia属性的时候添加它。
				if (navigator.mediaDevices.getUserMedia === undefined) {
					navigator.mediaDevices.getUserMedia = function(constraints) {
						// 首先,如果有getUserMedia的话,就获得它
						const getUserMedia =
							navigator.getUserMedia ||
							navigator.webkitGetUserMedia ||
							navigator.mozGetUserMedia;

						// 一些浏览器根本没实现它 - 那么就返回一个error到promise的reject来保持一个统一的接口
						if (!getUserMedia) {
							return Promise.reject(
								new Error("getUserMedia is not implemented in this browser")
							);
						}

						// 否则,为老的navigator.getUserMedia方法包裹一个Promise
						return new Promise(function(resolve, reject) {
							getUserMedia.call(navigator, constraints, resolve, reject);
						});
					};
				}

				uni.getSystemInfo({
					success: function(res) {
						const constraints = {
							audio: false,
							video: {
								// 前置摄像头
								// facingMode: "user",
								// 后置摄像头
								facingMode: { exact: "environment" },
								// 手机端相当于高
								width: Math.max(res.windowWidth, res.windowHeight) - 120,
								// 手机端相当于宽
								height: Math.min(res.windowWidth, res.windowHeight),
							},
						};

						navigator.mediaDevices
							.getUserMedia(constraints)
							.then(function(stream) {
								self.mediaStreamTrack = stream;

								const video = document.querySelector("video");
								// // let video = document.getElementById("video");


								video.setAttribute('autoplay', '');
								video.setAttribute('muted', '');
								video.setAttribute('playsinline', '');
								// console.log('video对象:',video);
								// // 旧的浏览器可能没有srcObject
								if ("srcObject" in video) {
									// console.log('"srcObject" in video');
									video.srcObject = stream;
								} else {
									// console.log('"srcObject" not in video');
									// 防止在新的浏览器里使用它,应为它已经不再支持了
									video.src = window.URL.createObjectURL(stream);
								}
								// console.log('video对象2:',video);
								// video.onloadedmetadata = function(e) {
								// console.log('video.onloadedmetadata() success');
								video.play();
								// console.log('video.play() success');
								// };
							})
							.catch(function(err) {
								console.log('错误1:', err.name + ": " + err.message);
							});
					},
				});
			},
			handlePhotographCloseClick() {
				if (this.mediaStreamTrack) {
					// 关闭摄像头
					this.mediaStreamTrack.getTracks().forEach(function(track) {
						track.stop();
					});
					this.mediaStreamTrack = null;
				}
			},
			handlePhotographClick() {
				const self = this;
				const canvas = document.createElement("canvas");
				const ctx = canvas.getContext("2d");
				const video = document.querySelector("video");
				canvas.width = Math.min(video.videoWidth, video.videoHeight);
				canvas.height = Math.max(video.videoWidth, video.videoHeight);
				ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

				// ****** 镜像处理 ******
				// function getPixel(imageData, row, column) {
				// 	const uint8ClampedArray = imageData.data;
				// 	const width = imageData.width;
				// 	const height = imageData.height;
				// 	const pixel = [];
				// 	for (let i = 0; i < 4; i++) {
				// 		pixel.push(uint8ClampedArray[row * width * 4 + column * 4 + i]);
				// 	}
				// 	return pixel;
				// }

				// function setPixel(imageData, row, column, pixel) {
				// 	const uint8ClampedArray = imageData.data;
				// 	const width = imageData.width;
				// 	const height = imageData.height;
				// 	for (let i = 0; i < 4; i++) {
				// 		uint8ClampedArray[row * width * 4 + column * 4 + i] = pixel[i];
				// 	}
				// }

				// const mirrorImageData = ctx.createImageData(canvas.width, canvas.height);
				// const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
				// for (let h = 0; h < canvas.height; h++) {
				// 	for (let w = 0; w < canvas.width; w++) {
				// 		const pixel = getPixel(imageData, h, canvas.width - w - 1);
				// 		setPixel(mirrorImageData, h, w, pixel);
				// 	}
				// }
				// ctx.putImageData(mirrorImageData, 0, 0);
				// ****** 镜像处理 ******

				self.$nextTick(() => {
					const base64 = canvas.toDataURL("image/jpeg");
					// self.imageUrl = base64;
					self.handlePhotographCloseClick();
					// console.log('图片地址:', base64);
					EventBus.$emit('takephoto-channel', base64)
					
					uni.navigateBack({
						delta: 1
					});
					
				});
			},
			
			
			dataURLtoFile(dataurl, filename) {
				var arr = dataurl.split(","),
					mime = arr[0].match(/:(.*?);/)[1],
					bstr = atob(arr[1]),
					n = bstr.length,
					u8arr = new Uint8Array(n);
				while (n--) {
					u8arr[n] = bstr.charCodeAt(n);
				}
				return new File([u8arr], filename, { type: mime });
			},
			handleAddPhotographClick() {
				this.uploadImage();
			},
			uploadImage: function() {
				const self = this;
				uni.chooseImage({
					count: 1,
					sizeType: ["compressed"],
					success: function(res) {
						self.handlePhotographCloseClick();
						const file = res.tempFiles[0];

						const reader = new FileReader();
						reader.readAsDataURL(file);
						reader.onload = function(e) {
							self.imageUrl = e.target.result;
						};
					},
				});
			},
		},
	};
</script>

<style scoped>
	video {
		/* transform: rotateY(180deg); */
		/* -webkit-transform: rotateY(180deg); */
		/* Safari 和 Chrome */
		/* -moz-transform: rotateY(180deg); */
	}

	/deep/ .uni-video-bar {
		display: none;
	}

	/deep/ .uni-video-cover {
		display: none;
	}

	.outer-ring {
		width: 160rpx;
		height: 160rpx;
		border-radius: 50%;
		background-color: #fff;
		display: flex;
		justify-content: center;
		align-items: center;
	}

	.middle-ring {
		width: 150rpx;
		height: 150rpx;
		border-radius: 50%;
		background-color: #000000;
		display: flex;
		justify-content: center;
		align-items: center;
	}

	.inner-ring {
		width: 140rpx;
		height: 140rpx;
		border-radius: 50%;
		background-color: #fff;
	}
</style>

onshelf.vue

	    mounted() {
			EventBus.$on('takephoto-channel', res => {
				this.img = res;
			})
		},
		destroyed() {
			EventBus.$off('takephoto-channel', {})
		},
        methods: {
            submit() {
                let img64 = that.img;
		        var file = that.dataURLtoFile(img64, that.generateRandomString(10) + '.png');
			    let formData = new FormData();
			    formData.append('file', file);
			    // 引入ajax,将文件上传到服务器
			    let promise = $.ajax({
				    url: 'https://xxx.com/upload',
				    data: formData,
				    processData: false,
				    contentType: false,
				    cache: false, //上传文件不需要缓存
				    type: 'post',
				    success: result => {
				    	console.log(result)
				    }
			    })
            },
            dataURLtoFile(dataurl, filename) {
				var arr = dataurl.split(","),
					mime = arr[0].match(/:(.*?);/)[1],
					bstr = atob(arr[1]),
					n = bstr.length,
					u8arr = new Uint8Array(n);
				while (n--) {
					u8arr[n] = bstr.charCodeAt(n);
				}
				return new File([u8arr], filename, { type: mime });
			},
            generateRandomString(length) {
				let result = '';
				const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
				for (let i = 0; i < length; i++) {
					result += characters.charAt(Math.floor(Math.random() * characters.length));
				}
				return result;
			},

        }

### 解决 UniApp H5 模式在微信浏览器中的下拉刷新 为了使 UniApp 应用程序能够在微信浏览器H5 模式中支持下拉刷新功能,可以采用多种方式来实现这一目标。 #### 使用 `@better-scroll/pull-down` 插件 安装配置 `@better-scroll/pull-down` 是一种有效的方法。通过此插件可以在不干扰其他交互的情况下轻松集成自定义的下拉刷新逻辑[^2]: ```bash npm install @better-scroll/pull-down --save ``` 接着,在项目中引入该库,按照官方文档说明完成初始化设置。 #### 修改页面配置文件 对于某些特定场景,可能只需要简单调整页面配置就能解决问题。确保页面 JSON 配置中有如下字段被正确启用: ```json { "enablePullDownRefresh": true, } ``` 这会告知框架允许当前页面响应用户的下拉操作[^3]。 #### 处理特殊设备兼容性问题 针对 iPhone 设备上可能出现的问题,可以通过检测用户代理字符串(User-Agent),对这些平台应用额外样式或脚本来优化体验效果。例如,当识别到 iOS 环境时,可尝试修改视口元标签(`<meta name="viewport">`) 的属性值以改善滚动性能[^4]。 #### 实现示例代码 下面是一个简单的 JavaScript 函数用于处理下拉刷新事件: ```javascript // #ifdef H5 if (typeof WeixinJSBridge === 'undefined') { document.addEventListener('WeixinJSBridgeReady', function () { setupPullToRefresh(); }, false); } else { setupPullToRefresh(); } function setupPullToRefresh() { wx.startPullDownRefresh({ success(res) { console.log('Start pull down refresh'); // 执行数据更新逻辑... setTimeout(() => { wx.stopPullDownRefresh(); }, 1000); // 延迟停止刷新动画模拟加载过程 } }); } // #endif ``` 上述代码片段展示了如何利用微信小程序 API 来启动和结束下拉刷新流程,同时考虑到不同环境之间的差异进行了适配处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值