实现思路
1、 使用MediaDevices.getUserMedia() 方法获取摄像头媒体流,传给标签渲染
2、用标签截取当前摄像头画面,用canvas.getContext(‘2d’).drawImage()方法来绘制
3、canvas.toDataURL() 方法将图像转换为 base64 格式
代码实现
1、<video playsinline webkit-playsinline="true" ref="refVideo" id="video"
preload="auto" muted loop autoplay x5-video-player-type="h5" x5-video-player-fullscreen x5-video-orientation="portraint" x-webkit-airplay x5-playsinline></video>
2、取景框:
<img :src="./frameImg.png" v-if="frame_type" width="100%" height="100%">
3、<canvas id="mycanvas" "></canvas>
使用 MediaDevices.getUserMedia() 方法获取摄像头媒体流,并将其传递给 标签进行渲染。
openCamera() {
// 1. 先展示,因为要从这里获取video标签
this.cameraShow = true
// 2. constraints:指定请求的媒体类型和相对应的参数
// facingMode值:user:前置。environment:后置
var constraints = {
audio: false,
video: {
facingMode: 'environment'
}
}
// 3. 兼容部分:
// 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {}
}
// 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia
// 因为这样可能会覆盖已有的属性。这里我们只会在没有getUserMedia属性的时候添加它。
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function (constraints) {
// 首先,如果有getUserMedia的话,就获得它
var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia
// 一些浏览器根本没实现它 - 那么就返回一个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)
})
}
}
// 4. 获取视频流
let that = this
navigator.mediaDevices.getUserMedia(constraints)
.then(function (stream) {
that.videostream = stream
// 进来这里表示能够兼容
let video = document.querySelector('video')
video.setAttribute('playsinline', true)
if ('srcObject' in that.$refs.refVideo) {
that.$refs.refVideo.srcObject = stream
} else {
that.$refs.refVideo.src = this.URL.createObjectURL(stream)
}
video.srcObject = stream
video.onloadedmetadata = function (e) {
video.play()
that.$refs.refVideo.play()
}
// 进入自定义拍摄模式
that.status = 1
})
.catch(function (err) {
that.cameraShow = false
// 进来这里表示不能兼容
console.log('nonono', err)
})
},
拍照
// 拍照
snapPhoto() {
var canvas = document.querySelector('#mycanvas')
var video = document.querySelector('video')
// 如果有取景框,拍摄的图片需要摆正
if (this.frame_type) {
canvas.width = video.clientHeight
canvas.height = video.clientWidth
this.canvasWidth = video.clientHeight
this.canvasHeight = video.clientWidth
// 图像翻转
let ctx = canvas.getContext('2d')
var xpos = video.clientWidth / 2
var ypos = video.clientHeight / 2
ctx.translate(xpos, ypos)
ctx.rotate((-90 * Math.PI) / 180)
ctx.drawImage(video, -(video.clientWidth - video.clientHeight / 2), -video.clientWidth / 2, video.clientWidth, video.clientHeight)
} else {
canvas.width = video.clientWidth
canvas.height = video.clientHeight
this.canvasWidth = video.clientWidth
this.canvasHeight = video.clientHeight
canvas.getContext('2d').drawImage(video, 0, 0, video.clientWidth, video.clientHeight)
}
// 保存为文件
this.imageFile = { target: { files: [this.canvasToFile(canvas)] } }
// blob转url:用于展示
let p = new Promise((resolve, reject) => {
canvas.toBlob(blob => {
let url = URL.createObjectURL(blob)
resolve(url)
})
})
let that = this
p.then(value => {
that.imageUrl = value
that.status = 2 // 表示拍摄完成
})
},
canvas转文件
canvasToFile(canvas) {
var dataurl = canvas.toDataURL('image/png')
var arr = dataurl.split(',')
var mime = arr[0].match(/:(.*?);/)[1]
var bstr = atob(arr[1])
var n = bstr.length
var u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
var file = new File([u8arr], 'phone.png', { type: mime })
return file
},
兼容性问题
1、ios进入页面黑屏
const video = document.getElementById('video')
video.play()
// 一般情况下,这样就可以自动播放了,但是一些奇葩iPhone机不可以
// 必须在微信Weixin JSAPI的WeixinJSBridgeReady才能生效
document.addEventListener(
'WeixinJSBridgeReady',
function () {
video.play()
},
false
)
let videoStatue = true
// 监听 touchstart 事件进而调用 <audio> 元素提供的 play() 方法播放音频
document.addEventListener(
'touchstart',
function (e) {
if (videoStatue) {
video.play()
videoStatue = false
}
},
false
)
})
// 启动拍摄
this.openCamera()
2、其他兼容性问题
1、需要配置HTTPS协议才可访问
2、支付宝不支持getUserMedia
3、对焦问题:视频无法自动对焦,导致拍摄会糊