webvr js meetup initial commit
This commit is contained in:
92
node_modules/webvr-ui/src/aframe-component.js
generated
vendored
Normal file
92
node_modules/webvr-ui/src/aframe-component.js
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright 2016 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/* global AFRAME */
|
||||
|
||||
import EnterVRButton from './enter-vr-button';
|
||||
import State from './states';
|
||||
|
||||
if (typeof AFRAME !== 'undefined' && AFRAME) {
|
||||
AFRAME.registerComponent('webvr-ui', {
|
||||
dependencies: ['canvas'],
|
||||
|
||||
schema: {
|
||||
enabled: {type: 'boolean', default: true},
|
||||
color: {type: 'string', default: 'white'},
|
||||
background: {type: 'string', default: 'black'},
|
||||
corners: {type: 'string', default: 'square'},
|
||||
disabledOpacity: {type: 'number', default: 0.5},
|
||||
|
||||
textEnterVRTitle: {type: 'string'},
|
||||
textExitVRTitle: {type: 'string'},
|
||||
textVRNotFoundTitle: {type: 'string'},
|
||||
},
|
||||
|
||||
init: function() {
|
||||
},
|
||||
|
||||
update: function() {
|
||||
let scene = document.querySelector('a-scene');
|
||||
scene.setAttribute('vr-mode-ui', {enabled: !this.data.enabled});
|
||||
|
||||
if (this.data.enabled) {
|
||||
if (this.enterVREl) {
|
||||
return;
|
||||
}
|
||||
|
||||
let options = {
|
||||
color: this.data.color,
|
||||
background: this.data.background,
|
||||
corners: this.data.corners,
|
||||
disabledOpacity: this.data.disabledOpacity,
|
||||
textEnterVRTitle: this.data.textEnterVRTitle,
|
||||
textExitVRTitle: this.data.textExitVRTitle,
|
||||
textVRNotFoundTitle: this.data.textVRNotFoundTitle,
|
||||
onRequestStateChange: function(state) {
|
||||
if (state == State.PRESENTING) {
|
||||
scene.enterVR();
|
||||
} else {
|
||||
scene.exitVR();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
let enterVR = this.enterVR = new EnterVRButton(scene.canvas, options);
|
||||
|
||||
this.enterVREl = enterVR.domElement;
|
||||
|
||||
document.body.appendChild(enterVR.domElement);
|
||||
|
||||
enterVR.domElement.style.position = 'absolute';
|
||||
enterVR.domElement.style.bottom = '10px';
|
||||
enterVR.domElement.style.left = '50%';
|
||||
enterVR.domElement.style.transform = 'translate(-50%, -50%)';
|
||||
enterVR.domElement.style.textAlign = 'center';
|
||||
} else {
|
||||
if (this.enterVREl) {
|
||||
this.enterVREl.parentNode.removeChild(this.enterVREl);
|
||||
this.enterVR.remove();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
if (this.enterVREl) {
|
||||
this.enterVREl.parentNode.removeChild(this.enterVREl);
|
||||
this.enterVR.remove();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
230
node_modules/webvr-ui/src/dom.js
generated
vendored
Normal file
230
node_modules/webvr-ui/src/dom.js
generated
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
// Copyright 2016 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
const _LOGO_SCALE = 0.8;
|
||||
let _WEBVR_UI_CSS_INJECTED = {};
|
||||
|
||||
/**
|
||||
* Generate the innerHTML for the button
|
||||
*
|
||||
* @return {string} html of the button as string
|
||||
* @param {string} cssPrefix
|
||||
* @param {Number} height
|
||||
* @private
|
||||
*/
|
||||
const generateInnerHTML = (cssPrefix, height)=> {
|
||||
const logoHeight = height*_LOGO_SCALE;
|
||||
const svgString = generateVRIconString(cssPrefix, logoHeight) + generateNoVRIconString(cssPrefix, logoHeight);
|
||||
|
||||
return `<button class="${cssPrefix}-button">
|
||||
<div class="${cssPrefix}-title"></div>
|
||||
<div class="${cssPrefix}-logo" >${svgString}</div>
|
||||
</button>`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject the CSS string to the head of the document
|
||||
*
|
||||
* @param {string} cssText the css to inject
|
||||
*/
|
||||
export const injectCSS = (cssText)=> {
|
||||
// Create the css
|
||||
const style = document.createElement('style');
|
||||
style.innerHTML = cssText;
|
||||
|
||||
let head = document.getElementsByTagName('head')[0];
|
||||
head.insertBefore(style, head.firstChild);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate DOM element view for button
|
||||
*
|
||||
* @return {HTMLElement}
|
||||
* @param {Object} options
|
||||
*/
|
||||
export const createDefaultView = (options)=> {
|
||||
const fontSize = options.height / 3;
|
||||
if (options.injectCSS) {
|
||||
// Check that css isnt already injected
|
||||
if (!_WEBVR_UI_CSS_INJECTED[options.cssprefix]) {
|
||||
injectCSS(generateCSS(options, fontSize));
|
||||
_WEBVR_UI_CSS_INJECTED[options.cssprefix] = true;
|
||||
}
|
||||
}
|
||||
|
||||
const el = document.createElement('div');
|
||||
el.innerHTML = generateInnerHTML(options.cssprefix, fontSize);
|
||||
return el.firstChild;
|
||||
};
|
||||
|
||||
|
||||
export const createVRIcon = (cssPrefix, height)=>{
|
||||
const el = document.createElement('div');
|
||||
el.innerHTML = generateVRIconString(cssPrefix, height);
|
||||
return el.firstChild;
|
||||
};
|
||||
|
||||
export const createNoVRIcon = (cssPrefix, height)=>{
|
||||
const el = document.createElement('div');
|
||||
el.innerHTML = generateNoVRIconString(cssPrefix, height);
|
||||
return el.firstChild;
|
||||
};
|
||||
|
||||
|
||||
const generateVRIconString = (cssPrefix, height)=> {
|
||||
let aspect = 28 / 18;
|
||||
return `<svg class="${cssPrefix}-svg" version="1.1" x="0px" y="0px"
|
||||
width="${aspect * height}px" height="${height}px" viewBox="0 0 28 18" xml:space="preserve">
|
||||
<path d="M26.8,1.1C26.1,0.4,25.1,0,24.2,0H3.4c-1,0-1.7,0.4-2.4,1.1C0.3,1.7,0,2.7,0,3.6v10.7
|
||||
c0,1,0.3,1.9,0.9,2.6C1.6,17.6,2.4,18,3.4,18h5c0.7,0,1.3-0.2,1.8-0.5c0.6-0.3,1-0.8,1.3-1.4l
|
||||
1.5-2.6C13.2,13.1,13,13,14,13v0h-0.2 h0c0.3,0,0.7,0.1,0.8,0.5l1.4,2.6c0.3,0.6,0.8,1.1,1.3,
|
||||
1.4c0.6,0.3,1.2,0.5,1.8,0.5h5c1,0,2-0.4,2.7-1.1c0.7-0.7,1.2-1.6,1.2-2.6 V3.6C28,2.7,27.5,
|
||||
1.7,26.8,1.1z M7.4,11.8c-1.6,0-2.8-1.3-2.8-2.8c0-1.6,1.3-2.8,2.8-2.8c1.6,0,2.8,1.3,2.8,2.8
|
||||
C10.2,10.5,8.9,11.8,7.4,11.8z M20.1,11.8c-1.6,0-2.8-1.3-2.8-2.8c0-1.6,1.3-2.8,2.8-2.8C21.7
|
||||
,6.2,23,7.4,23,9 C23,10.5,21.7,11.8,20.1,11.8z"/>
|
||||
</svg>`;
|
||||
};
|
||||
|
||||
const generateNoVRIconString = (cssPrefix, height)=>{
|
||||
let aspect = 28 / 18;
|
||||
return `<svg class="${cssPrefix}-svg-error" x="0px" y="0px"
|
||||
width="${aspect * height}px" height="${aspect * height}px" viewBox="0 0 28 28" xml:space="preserve">
|
||||
<path d="M17.6,13.4c0-0.2-0.1-0.4-0.1-0.6c0-1.6,1.3-2.8,2.8-2.8s2.8,1.3,2.8,2.8s-1.3,2.8-2.8,2.8
|
||||
c-0.2,0-0.4,0-0.6-0.1l5.9,5.9c0.5-0.2,0.9-0.4,1.3-0.8
|
||||
c0.7-0.7,1.1-1.6,1.1-2.5V7.4c0-1-0.4-1.9-1.1-2.5c-0.7-0.7-1.6-1-2.5-1
|
||||
H8.1 L17.6,13.4z"/>
|
||||
<path d="M10.1,14.2c-0.5,0.9-1.4,1.4-2.4,1.4c-1.6,0-2.8-1.3-2.8-2.8c0-1.1,0.6-2,1.4-2.5
|
||||
L0.9,5.1 C0.3,5.7,0,6.6,0,7.5v10.7c0,1,0.4,1.8,1.1,2.5c0.7,0.7,1.6,1,2.5,1
|
||||
h5c0.7,0,1.3-0.1,1.8-0.5c0.6-0.3,1-0.8,1.3-1.4l1.3-2.6 L10.1,14.2z"/>
|
||||
<path d="M25.5,27.5l-25-25C-0.1,2-0.1,1,0.5,0.4l0,0C1-0.1,2-0.1,2.6,0.4l25,25c0.6,0.6,0.6,1.5
|
||||
,0,2.1l0,0 C27,28.1,26,28.1,25.5,27.5z"/>
|
||||
</svg>`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate the CSS string to inject
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Number} [fontSize=18]
|
||||
* @return {string}
|
||||
*/
|
||||
export const generateCSS = (options, fontSize=18)=> {
|
||||
const height = options.height;
|
||||
const borderWidth = 2;
|
||||
const borderColor = options.background ? options.background : options.color;
|
||||
const cssPrefix = options.cssprefix;
|
||||
|
||||
let borderRadius;
|
||||
if (options.corners == 'round') {
|
||||
borderRadius = options.height / 2;
|
||||
} else if (options.corners == 'square') {
|
||||
borderRadius = 2;
|
||||
} else {
|
||||
borderRadius = options.corners;
|
||||
}
|
||||
|
||||
return (`
|
||||
@font-face {
|
||||
font-family: 'Karla';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Karla'), local('Karla-Regular'),
|
||||
url(https://fonts.gstatic.com/s/karla/v5/31P4mP32i98D9CEnGyeX9Q.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Karla';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Karla'), local('Karla-Regular'),
|
||||
url(https://fonts.gstatic.com/s/karla/v5/Zi_e6rBgGqv33BWF8WTq8g.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074,
|
||||
U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||
}
|
||||
|
||||
button.${cssPrefix}-button {
|
||||
font-family: 'Karla', sans-serif;
|
||||
|
||||
border: ${borderColor} ${borderWidth}px solid;
|
||||
border-radius: ${borderRadius}px;
|
||||
box-sizing: border-box;
|
||||
background: ${options.background ? options.background : 'none'};
|
||||
|
||||
height: ${height}px;
|
||||
min-width: ${fontSize * 9.6}px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button.${cssPrefix}-button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* Logo
|
||||
*/
|
||||
|
||||
.${cssPrefix}-logo {
|
||||
width: ${height}px;
|
||||
height: ${height}px;
|
||||
position: absolute;
|
||||
top:0px;
|
||||
left:0px;
|
||||
width: ${height - 4}px;
|
||||
height: ${height - 4}px;
|
||||
}
|
||||
.${cssPrefix}-svg {
|
||||
fill: ${options.color};
|
||||
margin-top: ${(height - fontSize * _LOGO_SCALE) / 2 - 2}px;
|
||||
margin-left: ${height / 3 }px;
|
||||
}
|
||||
.${cssPrefix}-svg-error {
|
||||
fill: ${options.color};
|
||||
display:none;
|
||||
margin-top: ${(height - 28 / 18 * fontSize * _LOGO_SCALE) / 2 - 2}px;
|
||||
margin-left: ${height / 3 }px;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Title
|
||||
*/
|
||||
|
||||
.${cssPrefix}-title {
|
||||
color: ${options.color};
|
||||
position: relative;
|
||||
font-size: ${fontSize}px;
|
||||
padding-left: ${height * 1.05}px;
|
||||
padding-right: ${(borderRadius - 10 < 5) ? height / 3 : borderRadius - 10}px;
|
||||
}
|
||||
|
||||
/*
|
||||
* disabled
|
||||
*/
|
||||
|
||||
button.${cssPrefix}-button[disabled=true] {
|
||||
opacity: ${options.disabledOpacity};
|
||||
}
|
||||
|
||||
button.${cssPrefix}-button[disabled=true] > .${cssPrefix}-logo > .${cssPrefix}-svg {
|
||||
display:none;
|
||||
}
|
||||
|
||||
button.${cssPrefix}-button[disabled=true] > .${cssPrefix}-logo > .${cssPrefix}-svg-error {
|
||||
display:initial;
|
||||
}
|
||||
`);
|
||||
};
|
||||
345
node_modules/webvr-ui/src/enter-vr-button.js
generated
vendored
Normal file
345
node_modules/webvr-ui/src/enter-vr-button.js
generated
vendored
Normal file
@@ -0,0 +1,345 @@
|
||||
// Copyright 2016 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import WebVRManager from './webvr-manager';
|
||||
import {createDefaultView} from './dom';
|
||||
import State from './states';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
|
||||
/**
|
||||
* A button to allow easy-entry and messaging around a WebVR experience
|
||||
* @class
|
||||
*/
|
||||
export default class EnterVRButton extends EventEmitter {
|
||||
/**
|
||||
* Construct a new Enter VR Button
|
||||
* @constructor
|
||||
* @param {HTMLCanvasElement} sourceCanvas the canvas that you want to present in WebVR
|
||||
* @param {Object} [options] optional parameters
|
||||
* @param {HTMLElement} [options.domElement] provide your own domElement to bind to
|
||||
* @param {Boolean} [options.injectCSS=true] set to false if you want to write your own styles
|
||||
* @param {Function} [options.beforeEnter] should return a promise, opportunity to intercept request to enter
|
||||
* @param {Function} [options.beforeExit] should return a promise, opportunity to intercept request to exit
|
||||
* @param {Function} [options.onRequestStateChange] set to a function returning false to prevent default state changes
|
||||
* @param {string} [options.textEnterVRTitle] set the text for Enter VR
|
||||
* @param {string} [options.textVRNotFoundTitle] set the text for when a VR display is not found
|
||||
* @param {string} [options.textExitVRTitle] set the text for exiting VR
|
||||
* @param {string} [options.color] text and icon color
|
||||
* @param {string} [options.background] set to false for no brackground or a color
|
||||
* @param {string} [options.corners] set to 'round', 'square' or pixel value representing the corner radius
|
||||
* @param {string} [options.disabledOpacity] set opacity of button dom when disabled
|
||||
* @param {string} [options.cssprefix] set to change the css prefix from default 'webvr-ui'
|
||||
*/
|
||||
constructor(sourceCanvas, options) {
|
||||
super();
|
||||
options = options || {};
|
||||
|
||||
options.color = options.color || 'rgb(80,168,252)';
|
||||
options.background = options.background || false;
|
||||
options.disabledOpacity = options.disabledOpacity || 0.5;
|
||||
options.height = options.height || 55;
|
||||
options.corners = options.corners || 'square';
|
||||
options.cssprefix = options.cssprefix || 'webvr-ui';
|
||||
|
||||
options.textEnterVRTitle = options.textEnterVRTitle || 'ENTER VR';
|
||||
options.textVRNotFoundTitle = options.textVRNotFoundTitle || 'VR NOT FOUND';
|
||||
options.textExitVRTitle = options.textExitVRTitle || 'EXIT VR';
|
||||
|
||||
options.onRequestStateChange = options.onRequestStateChange || (() => true);
|
||||
options.beforeEnter = options.beforeEnter || (()=> new Promise((resolve)=> resolve()));
|
||||
options.beforeExit = options.beforeExit || (()=> new Promise((resolve)=> resolve()));
|
||||
|
||||
options.injectCSS = options.injectCSS !== false;
|
||||
|
||||
this.options = options;
|
||||
|
||||
|
||||
this.sourceCanvas = sourceCanvas;
|
||||
|
||||
// Pass in your own domElement if you really dont want to use ours
|
||||
this.domElement = options.domElement || createDefaultView(options);
|
||||
this.__defaultDisplayStyle = this.domElement.style.display || 'initial';
|
||||
|
||||
// Create WebVR Manager
|
||||
this.manager = new WebVRManager();
|
||||
this.manager.checkDisplays();
|
||||
this.manager.addListener('change', (state)=> this.__onStateChange(state));
|
||||
|
||||
// Bind button click events to __onClick
|
||||
this.domElement.addEventListener('click', ()=> this.__onEnterVRClick());
|
||||
|
||||
this.__forceDisabled = false;
|
||||
this.setTitle(this.options.textEnterVRTitle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the title of the button
|
||||
* @param {string} text
|
||||
* @return {EnterVRButton}
|
||||
*/
|
||||
setTitle(text) {
|
||||
this.domElement.title = text;
|
||||
ifChild(this.domElement, this.options.cssprefix, 'title', (title)=> {
|
||||
if (!text) {
|
||||
title.style.display = 'none';
|
||||
} else {
|
||||
title.innerText = text;
|
||||
title.style.display = 'initial';
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tooltip of the button
|
||||
* @param {string} tooltip
|
||||
* @return {EnterVRButton}
|
||||
*/
|
||||
setTooltip(tooltip) {
|
||||
this.domElement.title = tooltip;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the button
|
||||
* @return {EnterVRButton}
|
||||
*/
|
||||
show() {
|
||||
this.domElement.style.display = this.__defaultDisplayStyle;
|
||||
this.emit('show');
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the button
|
||||
* @return {EnterVRButton}
|
||||
*/
|
||||
hide() {
|
||||
this.domElement.style.display = 'none';
|
||||
this.emit('hide');
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the button
|
||||
* @return {EnterVRButton}
|
||||
*/
|
||||
enable() {
|
||||
this.__setDisabledAttribute(false);
|
||||
this.__forceDisabled = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the button from being clicked
|
||||
* @return {EnterVRButton}
|
||||
*/
|
||||
disable() {
|
||||
this.__setDisabledAttribute(true);
|
||||
this.__forceDisabled = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* clean up object for garbage collection
|
||||
*/
|
||||
remove() {
|
||||
this.manager.remove();
|
||||
|
||||
if (this.domElement.parentElement) {
|
||||
this.domElement.parentElement.removeChild(this.domElement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise getting the VRDisplay used
|
||||
* @return {Promise.<VRDisplay>}
|
||||
*/
|
||||
getVRDisplay() {
|
||||
return WebVRManager.getVRDisplay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the canvas the button is connected to is currently presenting
|
||||
* @return {boolean}
|
||||
*/
|
||||
isPresenting() {
|
||||
return this.state === State.PRESENTING || this.state == State.PRESENTING_FULLSCREEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request entering VR
|
||||
* @return {Promise}
|
||||
*/
|
||||
requestEnterVR() {
|
||||
return new Promise((resolve, reject)=> {
|
||||
if (this.options.onRequestStateChange(State.PRESENTING)) {
|
||||
return this.options.beforeEnter()
|
||||
.then(()=> this.manager.enterVR(this.manager.defaultDisplay, this.sourceCanvas))
|
||||
.then(resolve);
|
||||
} else {
|
||||
reject(new Error(State.ERROR_REQUEST_STATE_CHANGE_REJECTED));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Request exiting presentation mode
|
||||
* @return {Promise}
|
||||
*/
|
||||
requestExit() {
|
||||
const initialState = this.state;
|
||||
|
||||
return new Promise((resolve, reject)=> {
|
||||
if (this.options.onRequestStateChange(State.READY_TO_PRESENT)) {
|
||||
return this.options.beforeExit()
|
||||
.then(()=>
|
||||
// if we were presenting VR, exit VR, if we are
|
||||
// exiting fullscreen, exit fullscreen
|
||||
initialState === State.PRESENTING ?
|
||||
this.manager.exitVR(this.manager.defaultDisplay) :
|
||||
this.manager.exitFullscreen())
|
||||
.then(resolve);
|
||||
} else {
|
||||
reject(new Error(State.ERROR_REQUEST_STATE_CHANGE_REJECTED));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Request entering the site in fullscreen, but not VR
|
||||
* @return {Promise}
|
||||
*/
|
||||
requestEnterFullscreen() {
|
||||
return new Promise((resolve, reject)=> {
|
||||
if (this.options.onRequestStateChange(State.PRESENTING_FULLSCREEN)) {
|
||||
return this.options.beforeEnter()
|
||||
.then(()=>this.manager.enterFullscreen(this.sourceCanvas))
|
||||
.then(resolve);
|
||||
} else {
|
||||
reject(new Error(State.ERROR_REQUEST_STATE_CHANGE_REJECTED));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the disabled attribute
|
||||
* @param {boolean} disabled
|
||||
* @private
|
||||
*/
|
||||
__setDisabledAttribute(disabled) {
|
||||
if (disabled || this.__forceDisabled) {
|
||||
this.domElement.setAttribute('disabled', 'true');
|
||||
} else {
|
||||
this.domElement.removeAttribute('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handling click event from button
|
||||
* @private
|
||||
*/
|
||||
__onEnterVRClick() {
|
||||
if (this.state == State.READY_TO_PRESENT) {
|
||||
this.requestEnterVR();
|
||||
} else if (this.isPresenting()) {
|
||||
this.requestExit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {State} state the state that its transitioning to
|
||||
* @private
|
||||
*/
|
||||
__onStateChange(state) {
|
||||
if (state != this.state) {
|
||||
if (this.state === State.PRESENTING || this.state === State.PRESENTING_FULLSCREEN) {
|
||||
this.emit('exit');
|
||||
}
|
||||
this.state = state;
|
||||
|
||||
switch (state) {
|
||||
case State.READY_TO_PRESENT:
|
||||
this.show();
|
||||
this.setTitle(this.options.textEnterVRTitle);
|
||||
if (this.manager.defaultDisplay) {
|
||||
this.setTooltip('Enter VR using ' + this.manager.defaultDisplay.displayName);
|
||||
}
|
||||
this.__setDisabledAttribute(false);
|
||||
this.emit('ready');
|
||||
break;
|
||||
|
||||
case State.PRESENTING:
|
||||
case State.PRESENTING_FULLSCREEN:
|
||||
if (!this.manager.defaultDisplay ||
|
||||
!this.manager.defaultDisplay.capabilities.hasExternalDisplay ||
|
||||
state == State.PRESENTING_FULLSCREEN) {
|
||||
this.hide();
|
||||
}
|
||||
this.setTitle(this.options.textExitVRTitle);
|
||||
this.__setDisabledAttribute(false);
|
||||
this.emit('enter');
|
||||
break;
|
||||
|
||||
// Error states
|
||||
case State.ERROR_BROWSER_NOT_SUPPORTED:
|
||||
this.show();
|
||||
this.setTitle(this.options.textVRNotFoundTitle);
|
||||
this.setTooltip('Browser not supported');
|
||||
this.__setDisabledAttribute(true);
|
||||
this.emit('error', new Error(state));
|
||||
break;
|
||||
|
||||
case State.ERROR_NO_PRESENTABLE_DISPLAYS:
|
||||
this.show();
|
||||
this.setTitle(this.options.textVRNotFoundTitle);
|
||||
this.setTooltip('No VR headset found.');
|
||||
this.__setDisabledAttribute(true);
|
||||
this.emit('error', new Error(state));
|
||||
break;
|
||||
|
||||
case State.ERROR_REQUEST_TO_PRESENT_REJECTED:
|
||||
this.show();
|
||||
this.setTitle(this.options.textVRNotFoundTitle);
|
||||
this.setTooltip('Something went wrong trying to start presenting to your headset.');
|
||||
this.__setDisabledAttribute(true);
|
||||
this.emit('error', new Error(state));
|
||||
break;
|
||||
|
||||
case State.ERROR_EXIT_PRESENT_REJECTED:
|
||||
default:
|
||||
this.show();
|
||||
this.setTitle(this.options.textVRNotFoundTitle);
|
||||
this.setTooltip('Unknown error.');
|
||||
this.__setDisabledAttribute(true);
|
||||
this.emit('error', new Error(state));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function checking if a specific css class exists as child of element.
|
||||
*
|
||||
* @param {HTMLElement} el element to find child in
|
||||
* @param {string} cssPrefix css prefix of button
|
||||
* @param {string} suffix class name
|
||||
* @param {function} fn function to call if child is found
|
||||
* @private
|
||||
*/
|
||||
const ifChild = (el, cssPrefix, suffix, fn)=> {
|
||||
const c = el.querySelector('.' + cssPrefix + '-' + suffix);
|
||||
c && fn(c);
|
||||
};
|
||||
26
node_modules/webvr-ui/src/index.js
generated
vendored
Normal file
26
node_modules/webvr-ui/src/index.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2016 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import WebVRManager from './webvr-manager';
|
||||
import State from './states';
|
||||
import * as dom from './dom';
|
||||
import EnterVRButton from './enter-vr-button';
|
||||
import './aframe-component';
|
||||
|
||||
export {
|
||||
EnterVRButton,
|
||||
dom,
|
||||
State,
|
||||
WebVRManager,
|
||||
};
|
||||
44
node_modules/webvr-ui/src/states.js
generated
vendored
Normal file
44
node_modules/webvr-ui/src/states.js
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2016 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Not yet presenting, but ready to present
|
||||
const READY_TO_PRESENT = 'ready';
|
||||
|
||||
// In presentation mode
|
||||
const PRESENTING = 'presenting';
|
||||
const PRESENTING_FULLSCREEN = 'presenting-fullscreen';
|
||||
|
||||
// Checking device availability
|
||||
const PREPARING = 'preparing';
|
||||
|
||||
// Errors
|
||||
const ERROR_NO_PRESENTABLE_DISPLAYS = 'error-no-presentable-displays';
|
||||
const ERROR_BROWSER_NOT_SUPPORTED = 'error-browser-not-supported';
|
||||
const ERROR_REQUEST_TO_PRESENT_REJECTED = 'error-request-to-present-rejected';
|
||||
const ERROR_EXIT_PRESENT_REJECTED = 'error-exit-present-rejected';
|
||||
const ERROR_REQUEST_STATE_CHANGE_REJECTED = 'error-request-state-change-rejected';
|
||||
const ERROR_UNKOWN = 'error-unkown';
|
||||
|
||||
export default {
|
||||
READY_TO_PRESENT,
|
||||
PRESENTING,
|
||||
PRESENTING_FULLSCREEN,
|
||||
PREPARING,
|
||||
ERROR_NO_PRESENTABLE_DISPLAYS,
|
||||
ERROR_BROWSER_NOT_SUPPORTED,
|
||||
ERROR_REQUEST_TO_PRESENT_REJECTED,
|
||||
ERROR_EXIT_PRESENT_REJECTED,
|
||||
ERROR_REQUEST_STATE_CHANGE_REJECTED,
|
||||
ERROR_UNKOWN,
|
||||
};
|
||||
224
node_modules/webvr-ui/src/webvr-manager.js
generated
vendored
Normal file
224
node_modules/webvr-ui/src/webvr-manager.js
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
// Copyright 2016 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import State from './states';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import screenfull from 'screenfull';
|
||||
|
||||
/**
|
||||
* WebVR Manager is a utility to handle VR displays
|
||||
*/
|
||||
export default class WebVRManager extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Construct a new WebVRManager
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
this.state = State.PREPARING;
|
||||
|
||||
// Bind vr display present change event to __onVRDisplayPresentChange
|
||||
this.__onVRDisplayPresentChange = this.__onVRDisplayPresentChange.bind(this);
|
||||
window.addEventListener('vrdisplaypresentchange', this.__onVRDisplayPresentChange);
|
||||
|
||||
this.__onChangeFullscreen = this.__onChangeFullscreen.bind(this);
|
||||
if (screenfull.enabled) {
|
||||
document.addEventListener(screenfull.raw.fullscreenchange, this.__onChangeFullscreen);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the browser is compatible with WebVR and has headsets.
|
||||
* @return {Promise<VRDisplay>}
|
||||
*/
|
||||
checkDisplays() {
|
||||
return WebVRManager.getVRDisplay()
|
||||
.then((display) => {
|
||||
this.defaultDisplay = display;
|
||||
this.__setState(State.READY_TO_PRESENT);
|
||||
return display;
|
||||
})
|
||||
.catch((e) => {
|
||||
delete this.defaultDisplay;
|
||||
if (e.name == 'NO_DISPLAYS') {
|
||||
this.__setState(State.ERROR_NO_PRESENTABLE_DISPLAYS);
|
||||
} else if (e.name == 'WEBVR_UNSUPPORTED') {
|
||||
this.__setState(State.ERROR_BROWSER_NOT_SUPPORTED);
|
||||
} else {
|
||||
this.__setState(State.ERROR_UNKOWN);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* clean up object for garbage collection
|
||||
*/
|
||||
remove() {
|
||||
window.removeEventListener('vrdisplaypresentchange', this.__onVRDisplayPresentChange);
|
||||
if (screenfull.enabled) {
|
||||
document.removeEventListener(screenfull.raw.fullscreenchanged, this.__onChangeFullscreen);
|
||||
}
|
||||
|
||||
this.removeAllListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* returns promise returning list of available VR displays.
|
||||
* @return {Promise<VRDisplay>}
|
||||
*/
|
||||
static getVRDisplay() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!navigator || !navigator.getVRDisplays) {
|
||||
let e = new Error('Browser not supporting WebVR');
|
||||
e.name = 'WEBVR_UNSUPPORTED';
|
||||
reject(e);
|
||||
return;
|
||||
}
|
||||
|
||||
const rejectNoDisplay = ()=> {
|
||||
// No displays are found.
|
||||
let e = new Error('No displays found');
|
||||
e.name = 'NO_DISPLAYS';
|
||||
reject(e);
|
||||
};
|
||||
|
||||
navigator.getVRDisplays().then(
|
||||
function(displays) {
|
||||
// Promise succeeds, but check if there are any displays actually.
|
||||
for (let i = 0; i < displays.length; i++) {
|
||||
if (displays[i].capabilities.canPresent) {
|
||||
resolve(displays[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rejectNoDisplay();
|
||||
},
|
||||
rejectNoDisplay);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter presentation mode with your set VR display
|
||||
* @param {VRDisplay} display the display to request present on
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @return {Promise.<TResult>}
|
||||
*/
|
||||
enterVR(display, canvas) {
|
||||
this.presentedSource = canvas;
|
||||
return display.requestPresent([{
|
||||
source: canvas,
|
||||
}])
|
||||
.then(
|
||||
()=> {},
|
||||
// this could fail if:
|
||||
// 1. Display `canPresent` is false
|
||||
// 2. Canvas is invalid
|
||||
// 3. not executed via user interaction
|
||||
()=> this.__setState(State.ERROR_REQUEST_TO_PRESENT_REJECTED)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit presentation mode on display
|
||||
* @param {VRDisplay} display
|
||||
* @return {Promise.<TResult>}
|
||||
*/
|
||||
exitVR(display) {
|
||||
return display.exitPresent()
|
||||
.then(
|
||||
()=> {
|
||||
this.presentedSource = undefined;
|
||||
},
|
||||
// this could fail if:
|
||||
// 1. exit requested while not currently presenting
|
||||
()=> this.__setState(State.ERROR_EXIT_PRESENT_REJECTED)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter fullscreen mode
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @return {boolean}
|
||||
*/
|
||||
enterFullscreen(canvas) {
|
||||
if (screenfull.enabled) {
|
||||
screenfull.request(canvas);
|
||||
} else {
|
||||
// iOS
|
||||
this.__setState(State.PRESENTING_FULLSCREEN);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit fullscreen mode
|
||||
* @return {boolean}
|
||||
*/
|
||||
exitFullscreen() {
|
||||
if (screenfull.enabled && screenfull.isFullscreen) {
|
||||
screenfull.exit();
|
||||
} else if (this.state == State.PRESENTING_FULLSCREEN) {
|
||||
this.checkDisplays();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the state of the manager
|
||||
* @param {State} state
|
||||
* @private
|
||||
*/
|
||||
__setState(state) {
|
||||
if (state != this.state) {
|
||||
this.emit('change', state, this.state);
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered on fullscreen change event
|
||||
* @param {Event} e
|
||||
* @private
|
||||
*/
|
||||
__onChangeFullscreen(e) {
|
||||
if (screenfull.isFullscreen) {
|
||||
this.__setState(State.PRESENTING_FULLSCREEN);
|
||||
} else {
|
||||
this.checkDisplays();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered on vr present change
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
__onVRDisplayPresentChange(event) {
|
||||
try {
|
||||
// Polyfill stores display under detail
|
||||
let display = event.display ? event.display : event.detail.display;
|
||||
if(display.isPresenting && display.getLayers()[0].source !== this.presentedSource) {
|
||||
// this means a different instance of WebVRManager has requested to present
|
||||
return;
|
||||
}
|
||||
|
||||
const isPresenting = this.defaultDisplay && this.defaultDisplay.isPresenting;
|
||||
this.__setState(isPresenting ? State.PRESENTING : State.READY_TO_PRESENT);
|
||||
} catch(err) {
|
||||
// continue regardless of error
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user