Newer
Older
reroad-test / 2020-ryusei / aframe-master / src / components / scene / vr-mode-ui.js
@ryusei ryusei on 22 Oct 2020 7 KB パノラマ表示
var registerComponent = require('../../core/component').registerComponent;
var constants = require('../../constants/');
var utils = require('../../utils/');
var bind = utils.bind;

var ENTER_VR_CLASS = 'a-enter-vr';
var ENTER_AR_CLASS = 'a-enter-ar';
var ENTER_VR_BTN_CLASS = 'a-enter-vr-button';
var ENTER_AR_BTN_CLASS = 'a-enter-ar-button';
var HIDDEN_CLASS = 'a-hidden';
var ORIENTATION_MODAL_CLASS = 'a-orientation-modal';

/**
 * UI for entering VR mode.
 */
module.exports.Component = registerComponent('vr-mode-ui', {
  dependencies: ['canvas'],

  schema: {
    enabled: {default: true},
    enterVRButton: {default: ''},
    enterARButton: {default: ''}
  },

  init: function () {
    var self = this;
    var sceneEl = this.el;

    if (utils.getUrlParameter('ui') === 'false') { return; }

    this.insideLoader = false;
    this.enterVREl = null;
    this.enterAREl = null;
    this.orientationModalEl = null;
    this.bindMethods();

    // Hide/show VR UI when entering/exiting VR mode.
    sceneEl.addEventListener('enter-vr', this.updateEnterInterfaces);
    sceneEl.addEventListener('exit-vr', this.updateEnterInterfaces);
    sceneEl.addEventListener('update-vr-devices', this.updateEnterInterfaces);

    window.addEventListener('message', function (event) {
      if (event.data.type === 'loaderReady') {
        self.insideLoader = true;
        self.remove();
      }
    });

    // Modal that tells the user to change orientation if in portrait.
    window.addEventListener('orientationchange', this.toggleOrientationModalIfNeeded);
  },

  bindMethods: function () {
    this.onEnterVRButtonClick = bind(this.onEnterVRButtonClick, this);
    this.onEnterARButtonClick = bind(this.onEnterARButtonClick, this);
    this.onModalClick = bind(this.onModalClick, this);
    this.toggleOrientationModalIfNeeded = bind(this.toggleOrientationModalIfNeeded, this);
    this.updateEnterInterfaces = bind(this.updateEnterInterfaces, this);
  },

  /**
   * Exit VR when modal clicked.
   */
  onModalClick: function () {
    this.el.exitVR();
  },

  /**
   * Enter VR when clicked.
   */
  onEnterVRButtonClick: function () {
    this.el.enterVR();
  },

  /**
   * Enter AR when clicked.
   */
  onEnterARButtonClick: function () {
    this.el.enterAR();
  },

  update: function () {
    var data = this.data;
    var sceneEl = this.el;

    if (!data.enabled || this.insideLoader || utils.getUrlParameter('ui') === 'false') {
      return this.remove();
    }

    if (this.enterVREl || this.enterAREl || this.orientationModalEl) { return; }

    // Add UI if enabled and not already present.
    if (data.enterVRButton) {
      // Custom button.
      this.enterVREl = document.querySelector(data.enterVRButton);
      this.enterVREl.addEventListener('click', this.onEnterVRButtonClick);
    } else {
      this.enterVREl = createEnterVRButton(this.onEnterVRButtonClick);
      sceneEl.appendChild(this.enterVREl);
    }
    if (data.enterARButton) {
      // Custom button.
      this.enterAREl = document.querySelector(data.enterARButton);
      this.enterAREl.addEventListener('click', this.onEnterARButtonClick);
    } else {
      this.enterAREl = createEnterARButton(this.onEnterARButtonClick);
      sceneEl.appendChild(this.enterAREl);
    }

    this.orientationModalEl = createOrientationModal(this.onModalClick);
    sceneEl.appendChild(this.orientationModalEl);

    this.updateEnterInterfaces();
  },

  remove: function () {
    [this.enterVREl, this.enterAREl, this.orientationModalEl].forEach(function (uiElement) {
      if (uiElement && uiElement.parentNode) {
        uiElement.parentNode.removeChild(uiElement);
      }
    });
  },

  updateEnterInterfaces: function () {
    this.toggleEnterVRButtonIfNeeded();
    this.toggleEnterARButtonIfNeeded();
    this.toggleOrientationModalIfNeeded();
  },

  toggleEnterVRButtonIfNeeded: function () {
    var sceneEl = this.el;
    if (!this.enterVREl) { return; }
    if (sceneEl.is('vr-mode')) {
      this.enterVREl.classList.add(HIDDEN_CLASS);
    } else {
      this.enterVREl.classList.remove(HIDDEN_CLASS);
    }
  },

  toggleEnterARButtonIfNeeded: function () {
    var sceneEl = this.el;
    if (!this.enterAREl) { return; }
    // Hide the button while in a session, or if AR is not supported.
    if (sceneEl.is('vr-mode') || !utils.device.checkARSupport()) {
      this.enterAREl.classList.add(HIDDEN_CLASS);
    } else {
      this.enterAREl.classList.remove(HIDDEN_CLASS);
    }
  },

  toggleOrientationModalIfNeeded: function () {
    var sceneEl = this.el;
    var orientationModalEl = this.orientationModalEl;
    if (!orientationModalEl || !sceneEl.isMobile) { return; }
    if (!utils.device.isLandscape() && sceneEl.is('vr-mode')) {
      // Show if in VR mode on portrait.
      orientationModalEl.classList.remove(HIDDEN_CLASS);
    } else {
      orientationModalEl.classList.add(HIDDEN_CLASS);
    }
  }
});

/**
 * Create a button that when clicked will enter into stereo-rendering mode for VR.
 *
 * Structure: <div><button></div>
 *
 * @param {function} onClick - click event handler
 * @returns {Element} Wrapper <div>.
 */
function createEnterVRButton (onClick) {
  var vrButton;
  var wrapper;

  // Create elements.
  wrapper = document.createElement('div');
  wrapper.classList.add(ENTER_VR_CLASS);
  wrapper.setAttribute(constants.AFRAME_INJECTED, '');
  vrButton = document.createElement('button');
  vrButton.className = ENTER_VR_BTN_CLASS;
  vrButton.setAttribute('title',
    'Enter VR mode with a headset or fullscreen mode on a desktop. ' +
    'Visit https://webvr.rocks or https://webvr.info for more information.');
  vrButton.setAttribute(constants.AFRAME_INJECTED, '');
  if (utils.device.isMobile()) { applyStickyHoverFix(vrButton); }
  // Insert elements.
  wrapper.appendChild(vrButton);
  vrButton.addEventListener('click', function (evt) {
    onClick();
    evt.stopPropagation();
  });
  return wrapper;
}

/**
 * Create a button that when clicked will enter into AR mode
 *
 * Structure: <div><button></div>
 *
 * @param {function} onClick - click event handler
 * @returns {Element} Wrapper <div>.
 */
function createEnterARButton (onClick) {
  var arButton;
  var wrapper;

  // Create elements.
  wrapper = document.createElement('div');
  wrapper.classList.add(ENTER_AR_CLASS);
  wrapper.setAttribute(constants.AFRAME_INJECTED, '');
  arButton = document.createElement('button');
  arButton.className = ENTER_AR_BTN_CLASS;
  arButton.setAttribute('title',
    'Enter AR mode with a headset or handheld device. ' +
    'Visit https://webvr.rocks or https://webvr.info for more information.');
  arButton.setAttribute(constants.AFRAME_INJECTED, '');
  if (utils.device.isMobile()) { applyStickyHoverFix(arButton); }
  // Insert elements.
  wrapper.appendChild(arButton);
  arButton.addEventListener('click', function (evt) {
    onClick();
    evt.stopPropagation();
  });
  return wrapper;
}

/**
 * Creates a modal dialog to request the user to switch to landscape orientation.
 *
 * @param {function} onClick - click event handler
 * @returns {Element} Wrapper <div>.
 */
function createOrientationModal (onClick) {
  var modal = document.createElement('div');
  modal.className = ORIENTATION_MODAL_CLASS;
  modal.classList.add(HIDDEN_CLASS);
  modal.setAttribute(constants.AFRAME_INJECTED, '');

  var exit = document.createElement('button');
  exit.setAttribute(constants.AFRAME_INJECTED, '');
  exit.innerHTML = 'Exit VR';

  // Exit VR on close.
  exit.addEventListener('click', onClick);

  modal.appendChild(exit);

  return modal;
}

/**
 * CSS hover state is sticky in iOS (as in 12/18/2019)
 * They are not removed on mouseleave and this function applies a class
 * to resets the style.
 *
 * @param {function} buttonEl - Button element
 */
function applyStickyHoverFix (buttonEl) {
  buttonEl.addEventListener('touchstart', function () {
    buttonEl.classList.remove('resethover');
  });
  buttonEl.addEventListener('touchend', function () {
    buttonEl.classList.add('resethover');
  });
}