1. 文档目录
.
├── app.js
├── css
│ └── index.css
├── index.html
└── scss
└── index.scss
2. 源码
js
var app = new Vue({
el: '#app',
data: {
cameraList: [],
myCanvas: null,
myContext: null
},
methods: {
main () {
this.myCanvas = document.getElementById('canvas')
this.myContext = this.myCanvas.getContext('2d')
this.cameraList.forEach((camera, index) => {
let domId = `video${index}`
let video = document.getElementById(domId)
this.enableCamera(camera.id, video)
})
},
enableCamera (deviceId, video) {
this.getUserMedia(this.setConstraints(deviceId), (stream) => {
video.srcObject = stream;
video.onloadedmetadata = (e) => {
video.play()
}
}, error => {
console.log(`访问用户媒体设备失败${error.name}, ${error.message}`)
})
},
getDevices () {
return new Promise((resolve, reject) => {
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
window.alert("不支持 mediaDevices.enumerateDevices()")
}
navigator.mediaDevices.enumerateDevices().then(devices => {
console.log(devices)
this.cameraList = []
devices.forEach((device, index) => {
if (device.kind && device.kind === 'videoinput') {
this.cameraList.push({
id: device.deviceId,
label: device.label
})
}
})
resolve()
}).catch((err) => {
console.log(err.name + ": " + err.message)
reject()
})
})
},
takePhoto (idx) {
let videoId = `video${idx}`
let videoDom = document.getElementById(videoId)
this.myContext.drawImage(videoDom, 0, 0, 480, 320)
},
getUserMedia (constraints, success, error) {
if (navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error)
} else if (navigator.webkitGetUserMedia) {
navigator.webkitGetUserMedia(constraints, success, error)
} else if (navigator.mozGetUserMedia) {
navigator.mozGetUserMedia(constraints, success, error)
} else if (navigator.getUserMedia) {
navigator.getUserMedia(constraints, success, error)
}
},
setConstraints (deviceId) {
return {
audio: false,
video: {
width: 480,
height: 320,
deviceId: deviceId
}
}
}
},
mounted () {
this.getDevices().then(() => {
this.main()
})
}
})
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="./css/index.css">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<title>摄像头拍照</title>
</head>
<body>
<div id="app">
<div class="video-list">
<div class="video-container" v-for="(item, index) in cameraList" :key="index">
<video :id="'video' + index" width="480" height="320" controls></video>
<div class="btns">
<button @click="takePhoto(index)">拍照</button>
</div>
<div class="name" :id="'name' + index">{{item.label}}</div>
</div>
</div>
</div>
<canvas id="canvas" width="480" height="320"></canvas>
<script src="./app.js"></script>
</body>
</html>
css
.video-list {
.video-container {
position: relative;
z-index: 1;
padding: 4px;
display: inline-block;
vertical-align: top;
background-color: #eee;
.btns {
text-align: center;
}
.name {
position: absolute;
top: 8px;
left: 8px;
z-index: 2;
font-size: 24px;
white-space: nowrap;
color: red;
}
}
}
3. 注意点
- 浏览器兼容性:只能使用现代浏览器
- Safari浏览器:只能检测外接摄像头
- 部署问题:由于浏览器安全限制,只能部署在https 或 localhost 上