PC端、移动端拍照及录像功能

使用vue+elementui中实现拍照录像功能

此示例,为按钮点击,弹框中进行展示,可以实现拍照及录像及对上传后进行回显、删除。话不多说,直接上代码。

template部分

<template>
    <div class="shot_container">
        <el-button size="small" type="primary" icon="el-icon-camera-solid" @click="chooseType">拍摄</el-button>
        <!-- 移动端 -->
        <el-drawer
            title="请选择操作"
            :visible.sync="drawer"
            :direction="'btt'">
            <div class="box">
                <div class="item">
                    <label for="fileImage">图片</label>
                    <!-- 拍照或选择文件 -->
                    <input id="fileImage" type="file" accept="image/*" hidden @change="fileImage">
                </div>
                <div class="item">
                    <label for="fileVideo">视频</label>
                    <!-- 录像或选择文件 -->
                    <input id="fileVideo" type="file" accept="video/*" hidden @change="fileVideo">
                </div>
            </div>
        </el-drawer>
        <!-- PC端 -->
        <el-dialog
            title="拍摄"
            :visible.sync="dialogVisible"
            width="1000px">
            <div class="video_box">
                <!-- 摄像头显示画面 -->
                <video ref="videoRef1" id="video" width="400" height="320" controls></video>

                <div class="img_content">
                    <div class="img_box">
                        <div>图片:</div>
                        <div class="canvas_box">
                            <!-- 拍完照片或视频回显标签 -->
                            <!-- <canvas ref="canvasRef1" width="100" height="80"></canvas> -->
                        </div>
                    </div>
                    <div class="img_box">
                        <div>视频:</div>
                        <div class="video_canvas_box">
                            <!-- 拍完照片或视频回显标签 -->
                            <!-- <canvas ref="canvasRef2" width="100" height="80"></canvas> -->
                        </div>
                    </div>
                </div>
            </div>
            <span slot="footer" class="dialog-footer">
                <el-button @click="recordClick">{{isRecording ? '停 止': '录 制'}}</el-button>
                <el-button @click="shotClick">拍 照</el-button>
                <el-button @click="dialogCancel">取 消</el-button>
                <el-button type="primary" @click="dialogOk">确 定</el-button>
            </span>
        </el-dialog>
    </div>
</template>

script部分

export default {
    data() {
        return {
            drawer: false,
            dialogVisible: false,
            imgFileList: [], // 图片集合
            videoFileList: [], // 视频集合
            isRecording: false, // 是否开始录制
            recordedBlobs: [], // 录制进行中视频流
            recordedBlobsArr: [], // 录制完成后,将视频流进行存储,用于播放
            mediaRecorder: null, // mediaRecorder实例
        }
    },
    mounted() {
        // 注册全局文件删除事件,挂载到window
        window.fileDel = this.fileDel
    },
    methods: {
        // 拍摄判断
        chooseType() {
            if(this.isMobile()){
                this.drawer = true
            }else if(this.isPc()){
                this.dialogVisible = true
                this.$nextTick(()=>{
                    if (navigator.mediaDevices.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator
                        .mozGetUserMedia) {
                        //调用用户媒体设备, 访问摄像头
                        this.getUserMedia({
                            video: {
                                width: 400,
                                height: 320
                            }
                        }, this.mediaSuccess, this.mediaError);
                    } else {
                        alert('不支持访问用户媒体');
                    }
                })
            }
        },
        // 移动端
        fileImage(e) {
            this.$emit('update', e.target.files)
            let file = e.target.files[0]
            console.log('这是图片名称'+file.name)
            this.drawer = false
        },
        fileVideo() {
            this.$emit('update', e.target.files)
            let file = e.target.files[0]
            console.log('这是视频名称'+file.name)
            this.drawer = false
        },
        // PC端
        //打开摄像头
        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);
            }
        },
        mediaSuccess(stream) {
            //兼容webkit核心浏览器
            let CompatibleURL = window.URL || window.webkitURL;
            //将视频流设置为video元素的源
            console.log(stream);
            window.stream = stream;
            if (window.URL) {
                this.$refs.videoRef1.srcObject = stream;
            } else {
                this.$refs.videoRef1.src = stream;
            }

            //video.src = CompatibleURL.createObjectURL(stream);
            this.$refs.videoRef1.srcObject = stream;
            this.$refs.videoRef1.play();
        },
        mediaError(error) {
            console.log(`访问用户媒体设备失败${error.name}, ${error.message}`);
        },
        // 弹框确定
        dialogOk() {
            console.log(this.imgFileList, this.videoFileList)
            this.$emit('update', this.imgFileList, this.videoFileList)
            this.dialogCancel()
        },
        // 弹框取消
        dialogCancel() {
            this.resetPc()
            this.dialogVisible = false
        },
        // 重置状态
        resetPc() {
            // 图片集合
            this.imgFileList = []
            // 图片dom清空
            let cbDom = document.getElementsByClassName('canvas_box')
            if(cbDom.length){
                cbDom[0].innerHTML = ''
            }
            // 视频集合
            this.videoFileList = []
            this.recordedBlobsArr = []
            // 视频dom清空
            let vbDom = document.getElementsByClassName('video_canvas_box')
            if(vbDom.length){
                vbDom[0].innerHTML = ''
            }
        },
        // 拍照
        shotClick() {
            // 生成随机id,用于删除查找
            let id = 'id'+Math.random()
            // 创建canvas
            let canvas = this.creatCanvas(id)
            // 绘画
            canvas.getContext('2d').drawImage(this.$refs.videoRef1, 0, 0, 100, 80);
            // canvas转为图片
            let img = canvas.toDataURL("image/png")
            // 将图片转为可上传的file
            let file = this.dataURLtoFile(img)
            // 将图片file存储
            this.imgFileList.push({key: id, file})
        },
        creatCanvas(id) {
            let canvas_box = document.getElementsByClassName('canvas_box')[0]
            let div = document.createElement('div')
            div.style.marginRight = '10px'
            div.style.position = 'relative'
            let canvas = document.createElement('canvas')
            canvas.height = 80
            canvas.width = 100
            let span = document.createElement('span')
            span.style.cursor = 'pointer'
            span.innerText = '×'
            span.style.fontSize = '30px'
            span.style.position = 'absolute'
            span.style.top = '-5px'
            span.style.right = '5px'
            span.id = id
            span.onclick = fileDel
            div.appendChild(span)
            div.appendChild(canvas)
            canvas_box.appendChild(div)
            return canvas
        },
        // 将base64转换为blob,需要file进行上传
        dataURLtoFile (dataurl, filename = 'file') {
            let arr = dataurl.split(',')
            let mime = arr[0].match(/:(.*?);/)[1]
            let suffix = mime.split('/')[1]
            let bstr = atob(arr[1])
            let n = bstr.length
            let u8arr = new Uint8Array(n)
            while (n--) {
                u8arr[n] = bstr.charCodeAt(n)
            }
            return new File([u8arr], `${filename}.${suffix}`, {type: mime})
        },
        // 录制
        recordClick() {
            if(!this.isRecording){
                // 点击录制
                this.startRecording()
            }else{
                // 点击停止
                this.stopRecording()
            }
            this.isRecording = !this.isRecording
        },
        // 开始录制
        startRecording() {
            this.recordedBlobs = [];
            var options = {
                mimeType: 'video/webm;codecs=vp9'
            };
            if (!MediaRecorder.isTypeSupported(options.mimeType)) {
                console.log(options.mimeType + ' is not Supported');
                options = {
                    mimeType: 'video/webm;codecs=vp8'
                };
                if (!MediaRecorder.isTypeSupported(options.mimeType)) {
                    console.log(options.mimeType + ' is not Supported');
                    options = {
                        mimeType: 'video/webm'
                    };
                    if (!MediaRecorder.isTypeSupported(options.mimeType)) {
                        console.log(options.mimeType + ' is not Supported');
                        options = {
                            mimeType: ''
                        };
                    }
                }
            }
            try {
                this.mediaRecorder = new MediaRecorder(window.stream, options);
            } catch (e) {
                console.error('Exception while creating MediaRecorder: ' + e);
                alert('Exception while creating MediaRecorder: ' +
                    e + '. mimeType: ' + options.mimeType);
                return;
            }
            console.log('Created MediaRecorder', this.mediaRecorder, 'with options', options);
            this.mediaRecorder.onstop = this.handleStop;
            this.mediaRecorder.ondataavailable = this.handleDataAvailable;
            this.mediaRecorder.start(10); // collect 10ms of data
            console.log('MediaRecorder started', this.mediaRecorder);
        },
        // 停止录制回调函数
        handleStop(event) {
            console.log('Recorder stopped: ', event);
        },
        // 录制中
        handleDataAvailable(event) {
            if (event.data && event.data.size > 0) {
                this.recordedBlobs.push(event.data);
            }
        },
        // 点击停止录制按钮
        stopRecording() {
            //影藏录制中提
            this.mediaRecorder.stop();
            // 生成随机id,用于删除查找
            let id = 'id'+Math.random()
            // buffer转dataUrl
            this.bufferToDataUrl(id)
            // 显示
            this.showVideo(id)
            console.log('Recorded Blobs: ', this.recordedBlobs);
            //recordedVideo.controls = true;
        },
        //buffer转dataUrl
        bufferToDataUrl(id) {
            let blob = new Blob(this.recordedBlobs, {
                type: "video/webm"
            });
            this.recordedBlobsArr.push({key: id, Blobs: this.recordedBlobs})
            this.recordedBlobs = []
            let reader = new FileReader();
            let _this = this
            reader.onload = function() {
                let file = _this.dataURLtoFile(reader.result, 'mp4')
                // 将视频流file存储
                _this.videoFileList.push({key: id, file})

                // 将blob转为MP4进行下载
                // var a = document.createElement('a');
                // a.style.display = 'none';
                // a.href = reader.result;
                // //文件名 通过方法传进来 检测是否合法?
                // a.download = 'record.mp4';
                // document.body.appendChild(a);
                // a.click();
                // setTimeout(function() {
                //     document.body.removeChild(a);
                //     window.URL.revokeObjectURL(reader.result);
                // }, 100);
            };
            reader.readAsDataURL(blob);
        },
        showVideo(id) {
            let video = this.creatVideoCanvas(id)
            let b = this.recordedBlobsArr.filter(item=> (item.key == id))
            var superBuffer = new Blob(b[0].Blobs, {
                type: 'video/mp4'
            });
            //recordedVideo.src = window.URL.createObjectURL(superBuffer);
            video.src = window.URL.createObjectURL(superBuffer);
            // video.play()
        },
        creatVideoCanvas(id) {
            let video_canvas_box = document.getElementsByClassName('video_canvas_box')[0]
            let div = document.createElement('div')
            div.style.marginRight = '10px'
            div.style.position = 'relative'
            let video = document.createElement('video')
            video.height = 160
            video.width = 200
            video.controls = true
            let span = document.createElement('span')
            span.style.cursor = 'pointer'
            span.innerText = '×'
            span.style.fontSize = '30px'
            span.style.position = 'absolute'
            span.style.top = '-5px'
            span.style.right = '5px'
            span.style.zIndex = 1000
            span.id = id
            span.onclick = fileDel
            div.appendChild(span)
            div.appendChild(video)
            video_canvas_box.appendChild(div)
            return video
        },
        // 是否是移动端
        isMobile() {
            return /(iPhone|iPad|iPod|iOS|Android|Linux armv8l|Linux armv7l|Linux aarch64)/i.test(navigator.platform);
        },
        // 是否是PC
        isPc() {
            return  /(Win32|Win16|WinCE|Mac68K|MacIntel|MacIntel|MacPPC|Linux mips64)/i.test(navigator.platform);
        },
        // 删除视频或图片
        fileDel(e) {
            let id = e.target.id
            // 删除图片
            for(var i = 0; i < this.imgFileList.length; i++){
                if(this.imgFileList[i].key == id){
                    this.imgFileList.splice(i, 1)
                    e.target.parentNode.remove()
                    break
                }
            }
            // 删除视频
            for(var i = 0; i < this.videoFileList.length; i++){
                if(this.videoFileList[i].key == id){
                    this.videoFileList.splice(i, 1)
                    e.target.parentNode.remove()
                    break
                }
            }
            // 删除视频对应存储的流
            for(var i = 0; i < this.recordedBlobsArr.length; i++){
                if(this.recordedBlobsArr[i].key == id){
                    this.recordedBlobsArr.splice(i, 1)
                    break
                }
            }
        }
    }
}

style部分

.shot_container{
        display: inline-block;
    }
    .box{
        width: 100%;
        height: 100%;
        align-items: center;
        display: flex;
        justify-content: space-around;
    }
    .item{

    }
    .video_box{
        display: flex;
    }
    .img_content{
        padding-left: 20px;
    }
    .video_canvas_box, .canvas_box{
        display: flex;
        flex-wrap: wrap;
    }
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小熊学前端

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值