有我抬联的球迷吗😂?
前言
前些天公司有个需求,需要用电脑来注册人脸🤣,用浏览器来拍照😂。网上查了一下,果然 js 有调用摄像头的 api,自己整理了一下来满足这个需求。
如果你有类似的需求,希望能帮到你😂。
调用摄像头
一共有两个api可以实现。一个是navigator.getUserMedia。(「该特性已经从 Web 标准中删除」,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请尽量不要使用该特性);
第二种是使用 navigator.mediaDevices.getUserMedia
- 第一种方法
navigator.getUserMedia用法详见 mdn ,代码如下:
<video id="myVideo"></video>
(function () {
function userMedia() {
return navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia || null;
}
if (userMedia()) {
let constraints = {
video: true,
audio: false
};
const media = navigator.getUserMedia(constraints, function (stream) {
const myVideo = document.getElementById('myVideo'),
url = window.URL || window.webkitURL;
myVideo.src = url ? url.createObjectURL(stream) : stream;
myVideo.play();
}, function (error) {
console.log("ERROR");
console.log(error);
});
} else {
console.log("不支持");
}
})();
- 第二种方法
navigator.mediaDevices.getUserMedia用法详见mdn, 代码如下:
<video id="myVideo"></video>
(function () {
// 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {};
}
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function (constraints) {
// 首先,如果有getUserMedia的话,就获得它
const getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
// 一些浏览器根本没实现它 - 那么就返回一个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);
});
}
}
const constraints = {
video: true,
audio: false
};
let promise = navigator.mediaDevices.getUserMedia(constraints);
promise.then(stream => {
const myVideo = document.getElementById('myVideo');
// 旧的浏览器可能没有srcObject
if ("srcObject" in v) {
myVideo.srcObject = stream;
} else {
// 防止再新的浏览器里使用它,应为它已经不再支持了
myVideo.src = window.URL.createObjectURL(stream);
}
v.onloadedmetadata = function (e) {
myVideo.play();
};
}).catch(err => {
console.error(err.name + ": " + err.message);
})
})();
拍照
设置一个标志,看看video是否在play。然后听拍照按钮的点击事件,如果videoPlaying 为 true ,使用一个canvas 获取 video 的宽高(默认 canvas 是不显示的),然后使用 canvas 的drawImage,然后使用 canvas 的 toDataURL返回一个 data url,将这个 url,设置在一个 img 标签上即可😀。
直接上代码了
import { Modal } from 'antd';
import styled from 'styled-components';
const AvatarWrapper = styled.div`
float: left;
position:relative;
width:120px;
height:160px;
border: 1px dashed #b2b2b2;
margin: 20px 0px 0px 0px;
`;
const BarRow = styled.i`
position:absolute;
width:20px;
height:2px;
background:#b2b2b2;
left:50%;
top:50%;
margin-left: -10px;
margin-top: -1px;
`;
const BarCol = styled.i`
position:absolute;
width:2px;
height:20px;
background:#b2b2b2;
left:50%;
top:50%;
margin-left: -1px;
margin-top: -10px;
`;
const Avatar = styled.div`
width:120px;
height: 160px;
position: absolute;
background-size: 100% 100%;
`;
const ImageInput = styled.input`
width:120px;
height: 160px;
opacity: 0;
position: absolute;
`;
let mediaStreamTrack = null;
class Demo extends Component {
constructor(props) {
super(props); // this.props = props
this.selectImage = this.selectImage.bind(this);
this.state = {
image: null,
imageUrl: null,
show: false,
}
};
// 选择图片
selectImage = (e) => {
if (e.target.files[0]) {
if (e.target.files[0].size > 1024 * 1024 * 2) return message.error('lessThan2M');
this.setState({
image: e.target.files[0]
});
let fileReader = new FileReader();
fileReader.readAsDataURL(e.target.files[0]);
fileReader.onload = () => {
this.setState({imageUrl: fileReader.result})
}
}
};
// 调用camera,获取图片
useCameraByBrowser = () => {
this.setState({
show: true
}, () => {
setTimeout(() => {
let video = document.querySelector('#test'),
streaming = false,
width = 360,
height = 480;
video.addEventListener('canplay', function (ev) {
if (!streaming) {
height = video.videoHeight / (video.videoWidth / width);
video.setAttribute('width', width);
video.setAttribute('height', height);
streaming = true;
}
}, false);
// 判断浏览器是否支持navigator.mediaDevices 属性
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {}
}
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function (constraints) {
var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
if (!getUserMedia) {
return Promise.reject(new Error('浏览器不支持'))
}
return new Promise((resolve, reject) => {
getUserMedia.call(navigator, constraints, resolve, reject)
})
}
}
navigator.mediaDevices.getUserMedia({video: {width: 120, height: 160}}).then(stream => {
if ("srcObject" in video) {
video.srcObject = stream;
} else {
video.src = window.URL.createObjectURL(stream)
}
mediaStreamTrack = typeof stream.stop === 'function' ? stream : stream.getTracks()[0];
video.onloadedmetadata = function (e) {
video.play()
}
}).catch(err => {
console.log(err)
})
}, 0)
})
};
// 获取摄像头图片
getDraw = (obj) => {
let canvas = document.createElement('canvas');
canvas.height = 480;
canvas.width = 360;
canvas.getContext('2d').drawImage(obj, 0, 0, 360, 480);
canvas.toBlob((obj) => {
this.setState({
image: obj,
imageUrl: canvas.toDataURL("image/png")
})
}, "image/png");
if (mediaStreamTrack) {
mediaStreamTrack.stop()
}
mediaStreamTrack = null
};
render() {
return (
<div>
<Modal visible={ this.state.show } onCancel={ () => {
this.setState({show: false});
// 拍照窗口取消,也需要停止摄像头调用
if (mediaStreamTrack) {
mediaStreamTrack.stop()
}
} } onOk={ () => {
this.getDraw(document.getElementById('test'));
this.setState({show: false})
} }>
<video id="test" src=""></video>
</Modal>
<AvatarWrapper>
<BarRow/>
<BarCol/>
<Avatar style={ {backgroundImage: `url(${ this.state.imageUrl })`} }></Avatar>
<ImageInput type="file" onChange={ (e) => {
this.selectImage(e)
} }></ImageInput>
</AvatarWrapper>
{
window.location.protocol === "https:" ?
<Button style={ {float: 'left', marginLeft: 10, marginTop: 20} }
onClick={ this.useCameraByBrowser }><Icon type="camera"/></Button> : ''
}
</Form.Item>
</div>
)
}
}
总结
使用navigator.mediaDevices.getUserMedia,调用摄像头,将画面通过srcObject或者用window.URL.createObjectURL转化用于src。这一步就可以播放画面了。当点击拍照时,用canvas的drawImage方法
canvas.getContext('2d').drawImage(obj, 0, 0, 360, 480);
canvas.toBlob((obj) => {
this.setState({
image: obj,
imageUrl: canvas.toDataURL("image/png")
})
}, "image/png");
转换成img可以展示的文件即可; 注意:只有HTTPS可以调用。
后记
欢迎大家来指点错误,或者有更好的方法可以分享!