Skip to content

Commit 667c838

Browse files
committed
refactor scene code to components (canvas, fullscreen metatags, stats, vr mode, vr mode ui)
1 parent bd91813 commit 667c838

File tree

21 files changed

+858
-594
lines changed

21 files changed

+858
-594
lines changed

‎src/components/index.js‎

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
1-
require('../components/camera');
2-
require('../components/cursor');
3-
require('../components/fog');
4-
require('../components/geometry');
5-
require('../components/light');
6-
require('../components/loader');
7-
require('../components/look-at');
8-
require('../components/look-controls');
9-
require('../components/material');
10-
require('../components/position');
11-
require('../components/raycaster');
12-
require('../components/rotation');
13-
require('../components/scale');
14-
require('../components/sound');
15-
require('../components/visible');
16-
require('../components/wasd-controls');
1+
require('./camera');
2+
require('./cursor');
3+
require('./geometry');
4+
require('./light');
5+
require('./loader');
6+
require('./look-at');
7+
require('./look-controls');
8+
require('./material');
9+
require('./position');
10+
require('./raycaster');
11+
require('./rotation');
12+
require('./scale');
13+
require('./sound');
14+
require('./visible');
15+
require('./wasd-controls');
16+
17+
require('./scene/canvas');
18+
require('./scene/fog');
19+
require('./scene/fullscreen');
20+
require('./scene/meta-tags');
21+
require('./scene/stats');
22+
require('./scene/vr-mode');
23+
require('./scene/vr-mode-ui');
24+
require('./scene/wakelock');

‎src/components/look-controls.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ module.exports.Component = registerComponent('look-controls', {
6969
},
7070

7171
addEventListeners: function () {
72-
var canvasEl = document.querySelector('a-scene').canvas;
72+
var canvasEl = this.el.sceneEl.canvas;
7373

7474
// Mouse Events
7575
canvasEl.addEventListener('mousedown', this.onMouseDown, false);

‎src/components/scene/canvas.js‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
var register = require('../../core/component').registerComponent;
2+
3+
module.exports.Component = register('canvas', {
4+
schema: {
5+
canvas: {
6+
type: 'selector',
7+
default: undefined
8+
},
9+
height: {
10+
default: 100
11+
},
12+
width: {
13+
default: 100
14+
}
15+
},
16+
17+
update: function () {
18+
var data = this.data;
19+
var canvas = data.canvas;
20+
var scene = this.el;
21+
22+
// No updating canvas.
23+
if (scene.canvas) { return; }
24+
25+
// Inject canvas if one not specified with height and width.
26+
if (!canvas) {
27+
canvas = document.createElement('canvas');
28+
canvas.classList.add('a-canvas');
29+
canvas.style.height = data.height + '%';
30+
canvas.style.width = data.width + '%';
31+
scene.appendChild(canvas);
32+
}
33+
34+
// Prevent overscroll on mobile.
35+
canvas.addEventListener('touchmove', function (event) {
36+
event.preventDefault();
37+
});
38+
39+
// Set canvas on scene.
40+
scene.canvas = canvas;
41+
scene.emit('render-target-loaded', {
42+
target: canvas
43+
});
44+
}
45+
});

‎src/components/scene/fog.js‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
var register = require('../../core/component').registerComponent;
2+
var THREE = require('../../lib/three');
3+
var debug = require('../../utils/debug');
4+
5+
var warn = debug('components:fog:warn');
6+
7+
/**
8+
* Fog component.
9+
* Applies only to the scene entity.
10+
*/
11+
module.exports.Component = register('fog', {
12+
schema: {
13+
color: { default: '#000' },
14+
density: { default: 0.00025 },
15+
far: { default: 1000, min: 0 },
16+
near: { default: 1, min: 0 },
17+
type: { default: 'linear', oneOf: ['linear', 'exponential'] }
18+
},
19+
20+
update: function () {
21+
var data = this.data;
22+
var el = this.el;
23+
var fog = this.el.object3D.fog;
24+
25+
if (!el.isScene) {
26+
warn('Fog component can only be applied to <a-scene>');
27+
return;
28+
}
29+
30+
// (Re)create fog if fog doesn't exist or fog type changed.
31+
if (!fog || data.type !== fog.name) {
32+
el.object3D.fog = getFog(data);
33+
el.updateMaterials();
34+
return;
35+
}
36+
37+
// Fog data changed. Update fog.
38+
Object.keys(this.schema).forEach(function (key) {
39+
var value = data[key];
40+
if (key === 'color') { value = new THREE.Color(value); }
41+
fog[key] = value;
42+
});
43+
},
44+
45+
/**
46+
* Remove fog on remove (callback).
47+
*/
48+
remove: function () {
49+
var fog = this.el.object3D.fog;
50+
if (fog) {
51+
fog.density = 0;
52+
fog.far = 0;
53+
fog.near = 0;
54+
}
55+
}
56+
});
57+
58+
/**
59+
* Creates a fog object. Sets fog.name to be able to detect fog type changes.
60+
*
61+
* @param {object} data - Fog data.
62+
* @returns {object} fog
63+
*/
64+
function getFog (data) {
65+
var fog;
66+
if (data.type === 'exponential') {
67+
fog = new THREE.FogExp2(data.color, data.density);
68+
} else {
69+
fog = new THREE.Fog(data.color, data.near, data.far);
70+
}
71+
fog.name = data.type;
72+
return fog;
73+
}

‎src/components/scene/fullscreen.js‎

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
var registerComponent = require('../../core/component').registerComponent;
2+
var isIframed = require('../../utils/').isIframed;
3+
4+
module.exports.Component = registerComponent('fullscreen', {
5+
dependencies: [ 'canvas' ],
6+
7+
init: function () {
8+
var scene = this.el;
9+
this.isMobile = scene.isMobile;
10+
this.handler = this.fullscreenChangeHandler.bind(this);
11+
document.addEventListener('mozfullscreenchange', this.handler);
12+
document.addEventListener('webkitfullscreenchange', this.handler);
13+
14+
// Handles fullscreen behavior when inside an iframe.
15+
if (isIframed()) {
16+
window.addEventListener('message', this.iframedFullscreenChangeHandler.bind(this));
17+
}
18+
19+
scene.addEventListener('vr-mode-enter', function () {
20+
setFullscreen(scene.canvas);
21+
});
22+
},
23+
24+
fullscreenChangeHandler: function (event) {
25+
var fullscreenElement = document.fullscreenElement ||
26+
document.mozFullScreenElement ||
27+
document.webkitFullscreenElement;
28+
29+
// Lock to landscape orientation on mobile.
30+
if (window.screen.orientation) {
31+
if (fullscreenElement && this.isMobile) {
32+
window.screen.orientation.lock('landscape');
33+
} else {
34+
window.screen.orientation.unlock();
35+
}
36+
}
37+
38+
if (fullscreenElement) {
39+
this.enterFullscreenHandler();
40+
} else {
41+
this.exitFullscreenHandler();
42+
}
43+
},
44+
45+
iframedFullscreenChangeHandler: function (event) {
46+
if (!event.data) { return; }
47+
switch (event.data.type) {
48+
case 'fullscreen': {
49+
switch (event.data.data) {
50+
case 'enter':
51+
this.enterFullscreenHandler();
52+
break;
53+
case 'exit':
54+
this.exitFullscreenHandler();
55+
break;
56+
}
57+
}
58+
}
59+
},
60+
61+
enterFullscreenHandler: function () {
62+
var scene = this.el;
63+
scene.addState('fullscreen');
64+
scene.emit('fullscreen-enter', { target: scene });
65+
},
66+
67+
exitFullscreenHandler: function () {
68+
var scene = this.el;
69+
scene.removeState('fullscreen');
70+
scene.emit('fullscreen-exit', { target: scene });
71+
}
72+
});
73+
74+
/**
75+
* Manually handles fullscreen for non-VR mobile where the renderer' VR
76+
* display is not polyfilled.
77+
*
78+
* Desktop just works so use the renderer.setFullScreen in that case.
79+
*/
80+
function setFullscreen (canvas) {
81+
if (canvas.requestFullscreen) {
82+
canvas.requestFullscreen();
83+
} else if (canvas.mozRequestFullScreen) {
84+
canvas.mozRequestFullScreen();
85+
} else if (canvas.webkitRequestFullscreen) {
86+
canvas.webkitRequestFullscreen();
87+
}
88+
}

‎src/components/scene/meta-tags.js‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
var register = require('../../core/component').registerComponent;
2+
3+
module.exports.Component = register('meta-tags', {
4+
init: function () {
5+
if (!this.el.isMobile) { return; }
6+
this.metaTags = injectMetaTags();
7+
},
8+
9+
remove: function () {
10+
var metaTags = this.metaTags;
11+
if (!metaTags) { return; }
12+
metaTags.forEach(function (metaTag) {
13+
metaTag.parentNode.removeChild(metaTag);
14+
});
15+
}
16+
});
17+
18+
/**
19+
* Injects the necessary metatags in the document for mobile support to:
20+
* 1. Prevent the user to zoom in the document
21+
* 2. Ensure that window.innerWidth and window.innerHeight have the correct
22+
* values and the canvas is properly scaled
23+
* 3. To allow fullscreen mode when pinning a web app on the home screen on
24+
* iOS.
25+
* Adapted from: https://www.reddit.com/r/web_design/comments/3la04p/
26+
*
27+
* @type {Object}
28+
*/
29+
function injectMetaTags () {
30+
var headEl;
31+
var meta = document.querySelector('meta[name="viewport"]');
32+
var metaTags = [];
33+
34+
if (meta) { return; }
35+
36+
headEl = document.getElementsByTagName('head')[0];
37+
meta = document.createElement('meta');
38+
meta.name = 'viewport';
39+
meta.content =
40+
'width=device-width,initial-scale=1,shrink-to-fit=no,user-scalable=no';
41+
headEl.appendChild(meta);
42+
metaTags.push(meta);
43+
44+
// iOS-specific meta tags for fullscreen when pinning to homescreen.
45+
meta = document.createElement('meta');
46+
meta.name = 'apple-mobile-web-app-capable';
47+
meta.content = 'yes';
48+
headEl.appendChild(meta);
49+
metaTags.push(meta);
50+
51+
meta = document.createElement('meta');
52+
meta.name = 'apple-mobile-web-app-status-bar-style';
53+
meta.content = 'black';
54+
headEl.appendChild(meta);
55+
metaTags.push(meta);
56+
57+
return metaTags;
58+
}

‎src/components/scene/stats.js‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
var registerComponent = require('../../core/component').registerComponent;
2+
var RStats = require('../../../vendor/rStats');
3+
4+
var HIDDEN_CLASS = 'a-hidden';
5+
6+
/**
7+
* Stats appended to document.body by RStats.
8+
*/
9+
module.exports.Component = registerComponent('stats', {
10+
init: function () {
11+
var scene = this.el;
12+
13+
this.stats = createStats();
14+
this.statsEl = document.querySelector('.rs-base');
15+
16+
this.hideBound = this.hide.bind(this);
17+
this.showBound = this.show.bind(this);
18+
19+
scene.addEventListener('vrmode-enter', this.hideBound);
20+
scene.addEventListener('vrmode-exit', this.showBound);
21+
},
22+
23+
remove: function () {
24+
this.el.removeEventListener('vrmode-enter', this.hideBound);
25+
this.el.removeEventListener('vrmode-exit', this.showBound);
26+
this.statsEl.parentNode.removeChild(this.statsEl);
27+
},
28+
29+
tick: function () {
30+
var stats = this.stats;
31+
stats('rAF').tick();
32+
stats('FPS').frame();
33+
stats().update();
34+
},
35+
36+
hide: function () {
37+
this.statsEl.classList.add(HIDDEN_CLASS);
38+
},
39+
40+
show: function () {
41+
this.statsEl.classList.remove(HIDDEN_CLASS);
42+
}
43+
});
44+
45+
function createStats () {
46+
return new RStats({
47+
CSSPath: '../../style/',
48+
values: {
49+
fps: { caption: 'fps', below: 30 }
50+
},
51+
groups: [
52+
{ caption: 'Framerate', values: [ 'fps', 'raf' ] }
53+
]
54+
});
55+
}

0 commit comments

Comments
 (0)