AR尝试(1)---js调用摄像头为底

最近在做一个用网页端的人体姿态识别的demo,遇到一些坑,记录一下。
首先遇到的问题是用js调用摄像头为底。这里我参考了EASY-AR的demo
分为2个文件 main.js 和 ClassAR.js

main.js:

function firstOpenCamera(){
    classAR.listCamera(videoDevice)
        .then(msg => {
            classAR.openCamera(JSON.parse(videoDevice[0].value))
                .then(msg => {
                    console.info(msg);
                }).catch(err => {
                console.info(err);
            });
        })
        .catch(err => {
            // 没有找到摄像头
            console.info(err);
        });
}

document.querySelector('#btn_changeCamera').addEventListener('click', function () {
    // 打开摄像头
    // 打开后置摄像头参数: {audio: false, video: {facingMode: {exact: 'environment'}}}
    if(videoDevice.length == 0 || videoDevice.length == 1)
        return;
    nowVideo = nowVideo == 0? 1 : 0;    //切换当前摄像头
    classAR.openCamera(JSON.parse(videoDevice[nowVideo].value))
        .then(msg => {
            console.info(msg);
        }).catch(err => {
        console.info(err);
    });
});

// 开启识别
document.querySelector('#btn_check').addEventListener('click', () => {
    classAR.startRecognize(classAR,(msg) => {
        console.info(msg);
    });
}, false);

classAR:

export default class ClassAR {
     constructor(interval,nowBodyModel) {
         this.isRecognizing = false;
         // 前/后置摄像头
         this.cameras = ["user", "environment"];
         this.interval = interval;
         this.videoOffWidth = 0;
         this.videoOffHeight = 0;
     }

     listCamera(videoDevice) {
         return new Promise((resolve, reject) => {
             navigator.mediaDevices.enumerateDevices()
                 .then((devices) => {
                     let index = 0;
                     devices.find((device) => {
                         if (device.kind === 'videoinput') {
                             const option = document.createElement('option');
                             // 在iOS12.2上deviceId为空
                             if (device.deviceId == '') {
                                 option.text = device.label || 'camera ' + this.cameras[index];
                                 option.value = JSON.stringify({
                                     audio: false,
                                     video: {facingMode: {exact: this.cameras[index]}}
                                 });
                                 index++;
                             } else {
                                 option.text = device.label || 'camera ' + (videoDevice.length + 1).toString();
                                 option.value = JSON.stringify({
                                     audio: false,
                                     video: {deviceId: {exact: device.deviceId}}
                                 });
                             }
                             // 将摄像头信息存储在select元素中,方便切换前、后置摄像头
                             videoDevice.push(option);
                         }
                         return false;
                     });
                     if (videoDevice.length === 0) {
                         reject('没有可使用的视频设备');
                     } else {
                         this.initVideo();
                         //this.initCanvas();
                         resolve(true);
                     }
                 }).catch(err => {
                 reject(err);
             });
         });
     }

     /**
      * 打开摄像头
      * 摄像头设置参数请查看: https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints
      * @param videoDeviceIndex
      * @returns {Promise<T>}
      */


     openCamera(constraints) {
         // 如果是切换摄像头,则需要先关闭。
         if (this.videoElement && this.videoElement.srcObject) {
             this.videoElement.srcObject.getTracks().forEach(track => {
                 track.stop();
             });
         }
         return new Promise((resolve, reject) => {
             navigator.mediaDevices.getUserMedia(constraints)
                 .then(stream => {
                     this.videoElement.srcObject = stream;
                     this.videoElement.style.display = 'block';
                     this.videoElement.play();
                     this.videoElement.onloadedmetadata = () => {
                         const cameraSize = {
                             width: this.videoElement.offsetWidth,
                             height: this.videoElement.offsetHeight
                         };
                         console.info(JSON.stringify(cameraSize));
                         if (window.innerWidth < window.innerHeight) {
                             // 竖屏
                             if (cameraSize.height < window.innerHeight) {
                                 this.videoElement.setAttribute('height', window.innerHeight.toString() + 'px');
                                 this.videoOffWidth = this.videoElement.offsetWidth - window.innerWidth;
                                 this.videoElement.style.marginLeft = -this.videoOffWidth/2 + "px";
                                 //this.canvasElement.style.marginLeft =  -(cameraSize.width - window.innerWidth) + "px";
                             }
                         } else {
                             // 横屏
                             if (cameraSize.width < window.innerWidth) {
                                 this.videoElement.setAttribute('width', window.innerWidth.toString() + 'px');
                                 this.videoOffHeight = this.videoElement.offsetHeight - window.innerHeight + "px";
                                 this.videoElement.style.marginTop = -this.videoOffHeight + "px";
                                 //this.canvasElement.style.marginTop =  -(cameraSize.width - window.innerWidth) + "px";
                             }
                         }
                         resolve(true);

                          this.initCanvas();
                     };
                 })
                 .catch(err => {
                     reject(err);
                 });
         });
     }

     /**
      * 截取摄像头图片
      * @returns {HTMLImageElement}
      */
     captureVideo() {
         //this.canvasContext.drawImage(this.videoElement, this.videoOffWidth / 2, 0 , window.innerWidth,620 , 0 , 0 ,window.innerWidth,window.innerHeight);
         this.canvasContext.drawImage(this.videoElement,0,0,window.innerWidth,window.innerHeight);

         //this.canvasElement.style.marginLeft = - this.videoOffWidth/2 + 'px';
         //this.canvasContext2.drawImage(this.canvasElement,0,0,300,window.innerHeight,0,0,300,window.innerHeight);
         //return this.canvasElement.toDataURL('image/jpeg', 0.5).split('base64,')[1];
         let image_64 =  this.canvasElement.toDataURL('image/jpeg');
         let image = new Image();
         image.src = image_64;
         this.canvasElement.style.display = "none";

         let newImagePromise = this.cutImage(image);
         return newImagePromise;
     }

     /**
      * 创建视频详情元素,播放摄像头视频流
      */
     initVideo() {
         /*this.videoElement = document.createElement('video');
         this.videoElement.setAttribute('playsinline', 'playsinline');
         document.body.appendChild(this.videoElement);*/

         // this.videoElement = document.createElement('video');
         this.videoElement = document.getElementById('video');
         this.videoElement.setAttribute('playsinline', 'playsinline');
         //this.videoElement.setAttribute('width', window.innerWidth.toString() + 'px');
         //this.videoElement.setAttribute('height', window.innerHeight.toString() + 'px');
         document.body.appendChild(this.videoElement);
     }

     /**
      * 创建canvas,截取摄像头图片时使用
      */

     initCanvas() {

         // this.canvasElement = document.createElement('canvas');
         this.canvasElement = document.getElementById('canvas');
         this.canvasElement.setAttribute('width', window.innerWidth.toString() + 'px');
         this.canvasElement.setAttribute('height', window.innerHeight.toString() + 'px');
         this.canvasContext = this.canvasElement.getContext('2d');

         this.canvasElement2 = document.getElementById('canvas2');
         this.canvasElement2.setAttribute('width', window.innerWidth + 'px');
         this.canvasElement2.setAttribute('height', window.innerHeight+ 'px');
         this.canvasContext2 = this.canvasElement2.getContext('2d');
         // document.body.appendChild(this.canvasElement);
     }

然后这里遇到了一个问题,就是当手机访问时,安卓大部分机型是可以的,但是我测试的ios机型都出现了,实际摄像头所摄区域要大于html,导致网页可以左右移动。

查看源码,并未想到解决方案,于是我用了自己的方法来解:
首先计算出如果只显示中间时,左右2端应有多少width。

this.videoOffWidth = this.videoElement.offsetWidth - window.innerWidth;

然后通过设置marginleft,来“裁”去左边的。

this.videoElement.style.marginLeft = -this.videoOffWidth/2 + "px";

再通过给html设置不能左右滑动,来“裁”去右边的(当然这只是治标不治本的方法,但是对于我这次的项目来说,这种方法够用了。)

body {
    margin: 0;
    padding: 0;
    position:fixed;
    overflow: hidden;
}

这样就能实现屏幕用摄像头为底了。

发布了57 篇原创文章 · 获赞 3 · 访问量 6217

猜你喜欢

转载自blog.csdn.net/qq_39830579/article/details/97259195
今日推荐