React + antd调用摄像头控件封装

1. 项目需要调用摄像头,此方式仅适用于谷歌、火狐相同内核的浏览器,至于IE慢慢用flash或者active控件去慢慢折腾吧。里面有基于antd的控件,大家可以快速浏览一下不适用退出 别骂我 害怕  只是提供大家参考

2. 想过如图:

3. 完整源码:

import React from "react";
import {Button,Row, Col, Select, message, Radio, Tag, Icon, Result ,Modal}  from "antd";
import MyModal from "../Model/Model";
const { confirm } = Modal;

const { Option } = Select;
//调用高拍仪使用webapi
export class WebCamWebAPI extends React.Component{

    constructor(props) {
        super(props);
        this.state = {
            defalutSelectedDevice : '',
            mediaDeviceList : [],
            deviceId : "",
            mediaStream : null,
            deviceActive : false,
            fileTypes : [],
            fileTypeSeq : 0,
            addFinish : null,
            tips : ""
        };
    }

    componentDidMount(){
        this.setDeviceList();
        this.requestFileTypes();
    }
    //获取证件类型
    requestFileTypes = () => {
            const {fileTypes} = this.props;
            if(typeof (fileTypes) == 'undefined' || fileTypes.length == 0){
                return;
            }
            console.log(fileTypes);
            //是够已经添加
            let addFinishObj = new Object();
            if(fileTypes.length > 0){
                for (let i in fileTypes){
                    addFinishObj[fileTypes[i].TYPE_CODE] =  0
                }
                console.log(addFinishObj);
            }
            this.setState({
                fileTypes : fileTypes,
                addFinish : addFinishObj
            });
    };
    //连接相机
    connectDevice = (deviceId) => {
        //先关闭当前正在运行摄像头
        if(null != this.state.mediaStream){
            this.onCloseDevice();
        }
        //打开新选择摄像头
        if (navigator.mediaDevices.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia) {
            //调用用户媒体设备, 访问摄像头
            this.getUserMedia({video : {width: 1440, height: 960,deviceId : {exact : deviceId}}}, this.success, this.error);
        } else {
            MyModal.info('不支持访问用户媒体');
        }
    };
    //获取设备列表并设置到设备列表
    setDeviceList =async () => {
        let deviceArray =await navigator.mediaDevices.enumerateDevices();
        if(deviceArray.length > 0){
            let mediaDeviceList = [];
            for (let i in deviceArray){
                if(deviceArray[i].kind == 'videoinput'){
                    let obj ={
                        "value": deviceArray[i].deviceId,
                        "label": deviceArray[i].label
                    };
                    mediaDeviceList.push(obj);
                }
            }
            //判断是否有可用的视频输入设备
            if(mediaDeviceList.length > 0){
                this.setState({
                    mediaDeviceList,
                    defalutSelectedDevice : mediaDeviceList[0].value,
                    deviceId :  mediaDeviceList[0].value
                });
                this.connectDevice();
            }else {
                this.setState({
                    tips : "没有可用照片采集设备或者浏览器不支持此功能,请保证设备可正常使用(此方式不支持IE浏览器)"
                });
            }
        }else {
            MyModal.info("没有可用设备或设备不可用!");
        }
    };
    //访问用户媒体设备的兼容方法
    getUserMedia = (constraints, success, error) => {
        if (navigator.mediaDevices.getUserMedia) {
            //最新的标准API
            navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
        } else if (navigator.webkitGetUserMedia) {
            //webkit核心浏览器
            navigator.webkitGetUserMedia(constraints,success, error)
        } else if (navigator.mozGetUserMedia) {
            //firfox浏览器
            navigator.mozGetUserMedia(constraints, success, error);
        } else if (navigator.getUserMedia) {
            //旧版API
            navigator.getUserMedia(constraints, success, error);
        }
    };
    //成功回调
    success = (stream) => {
        let video = document.getElementById('video');
        let canvas = document.getElementById('canvas');
        //兼容webkit核心浏览器
        let CompatibleURL = window.URL || window.webkitURL;
        //将视频流设置为video元素的源
        this.setState({mediaStream : stream, deviceActive : true});
        //video.src = CompatibleURL.createObjectURL(stream);
        video.srcObject = stream;
        video.play();

    };
    //失败回调
    error = (error) => {
        console.log(`访问用户媒体设备失败${error.name}, ${error.message}`);
    };
    //拍照预览
    takePicturePreView = () => {
        let video = document.getElementById('video');
        let canvas = document.getElementById('canvas');
        let context = canvas.getContext('2d');
        context.drawImage(video, 0, 0, 400, 320);
    };
    //拍照添加到列表
    takePictureToList = () => {
        let video = document.getElementById('video');
        let canvas = document.getElementById('canvas');
        let context = canvas.getContext('2d');
        //let ratio = Utils.getPixelRatio(context);
        context.drawImage(video, 0, 0,  400, 320);
        //获取添加到列表canvas
        let canvasToList = document.getElementById('canvasToList');
        let contextToList = canvasToList.getContext('2d');
        contextToList.drawImage(video, 0, 0,  1440, 960);
        //拍完照直接添加到列表
        this.getPicData();
    };
    //关闭摄像头
    onCloseDevice = () => {
        //关闭
        let stream = this.state.mediaStream;
        if(stream == null){
            return;
        }
        if(stream.active == true){
            let track = stream.getTracks()[0];
            track.stop();
            this.setState({deviceActive : false});
        }
    };
    //得到图片资源
    getPicData = () => {
        let canvas = document.getElementById('canvasToList');
        if(!this.isCanvasBlank(canvas)) {
            let imgData = canvas.toDataURL("image/jpg");
            if(typeof (this.props.addFile) != 'undefined'){
                if(this.state.fileTypeSeq != 0){
                    //设置已添加标记
                    let data = {
                        "imgData" : imgData,
                        "fileType" : this.state.fileTypeSeq
                    };
                    this.props.addFile(data);
                    let addFinish = this.state.addFinish;
                    addFinish[this.state.fileTypeSeq] = addFinish[this.state.fileTypeSeq] + 1;
                    this.setState({addFinish});
                }else {
                    MyModal.info("请选择照片类型");
                }
            }else {
               message.error("无法添加到列表!");
            }
        }else {
            MyModal.info("请先抓取照片!");
        }
    };
    //打开设备
    openDevice = () => {
        if(this.state.deviceId != ""){
            this.connectDevice();
        }else {
            MyModal.info("当前设备不可用,请选择设备!");
        }
    };
    //验证canvas画布是否为空函数
     isCanvasBlank = (canvas) => {
        var blank = document.createElement('canvas');//系统获取一个空canvas对象
        blank.width = canvas.width;
        blank.height = canvas.height;
        return canvas.toDataURL() == blank.toDataURL();//比较值相等则为空
    };

    render() {

        const radioStyle = {
            display: 'block',
            height: '30px',
            lineHeight: '30px',
        };

        return (<div>{this.state.tips == "" ? <div style={{backgroundColor: "#FFF", padding: '20px'}}>
            <Row>
                <Col span={12} key={1}>
                    <h3>实时画面:</h3>
                    <video id="video" width="400" height="320" controls>
                    </video>
                    <Row style={{marginTop: "36px"}}>
                        <div>
                            设备列表 : <Select style={{width: '180px'}}
                                           value={this.state.defalutSelectedDevice}
                                           onChange={(value) => {
                                               this.setState({
                                                   defalutSelectedDevice: value,
                                               });
                                               this.connectDevice(value);
                                           }}
                                           notFoundContent="没有可用的设备"
                        >
                            {this.state.mediaDeviceList.length > 0 ? this.state.mediaDeviceList.map((item, index) => {
                                return (<Option key={index} value={item.value}>{item.label}</Option>);
                            }) : null}
                        </Select>
                            <Button type="primary" disabled={this.state.deviceActive} style={{marginLeft: '20px'}}
                                    onClick={() => {
                                        this.openDevice();
                                    }}>打开设备</Button>
                            <Button type="primary" disabled={!this.state.deviceActive} style={{marginLeft: '20px'}}
                                    onClick={() => {
                                        this.onCloseDevice();
                                    }}>关闭设备</Button>
                        </div>
                    </Row>
                    <Row style={{marginTop: "36px"}}>
                        <Button type="primary" style={{marginLeft: '20px'}} onClick={() => {
                            this.takePicturePreView();
                        }}>拍照预览</Button>
                        <Button type="primary" style={{marginLeft: '20px'}} onClick={() => {
                            this.takePictureToList();
                        }}>拍照并添加到列表</Button>
                    </Row>
                </Col>
                <Col span={12} key={2}>
                    <div>
                        <h3>预览:</h3>
                        <canvas id="canvas" width="480" height="320" ></canvas>
                        <canvas id="canvasToList" width="1440" height="960" style={{display : "none"}} ></canvas>
                    </div>
                    <Row>
                        <h3> 附件类型 : </h3>
                        <Radio.Group value={this.state.fileTypeSeq} onChange={(e) => {
                            this.setState({
                                fileTypeSeq: e.target.value
                            });
                        }}>
                            {this.state.fileTypes.length > 0 ? this.state.fileTypes.map((item, index) => {
                                return (<Radio key={index} style={radioStyle}
                                               value={item.TYPE_CODE}>{this.state.addFinish[item.TYPE_CODE] > 0 ?
                                    <span>{`${item.TYPE_DESC} `}<Tag
                                        color='#52c41a'>{`已添加 ${this.state.addFinish[item.TYPE_CODE]} 张`}</Tag></span> : item.TYPE_DESC}</Radio>);
                            }) : null}
                        </Radio.Group>
                    </Row>
                </Col>
            </Row>
            <Row style={{marginTop: "36px"}}>
                <Button type="danger" style={{float: "right", marginLeft: '36px'}} onClick={() => {
                    //首先关闭设备
                    this.onCloseDevice();
                    //关闭窗口
                    if (typeof (this.props.onClose) != 'undefined') {
                        this.props.onClose();
                    }
                }}>关闭</Button>
                {/*   <Button type="primary" style={{float : "right"}}  onClick={() => {
                    this.getPicData();
                }} >添加到列表</Button>*/}
            </Row>
        </div> : <Result status="warning" title={this.state.tips}
                         extra={<Button type="danger" style={{float: "right", marginLeft: '36px'}} onClick={() => {
                             //关闭窗口
                             if (typeof (this.props.onClose) != 'undefined') {
                                 this.props.onClose();
                             }
                         }}>关闭</Button>}/>}</div>);
    }
}

4. 调用方式:

  <WebCamWebAPI
                    fileTypes={fileTypes}
                    addFile={(imgObj) => {
                        this.handleApplyPhoto(imgObj);
                    }}
                    onClose={() => {
                        this.setState({
                            PhotoUploadVisible: false
                        })
                    }}
                />

传入一个 数组类型的 fileTypes ,一个接受base64的回调函数 我这里是弹出Modal窗口显示的所以提供一个关闭窗口的回调,给大家提供参考,

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值