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;
}

這樣就能實現屏幕用攝像頭爲底了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章