安卓和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微信浏览器调用摄像头生成九宫格辅助线
9165

被折叠的 条评论
为什么被折叠?



