'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); exports.default = aframeFrustumLockComponent; var _lodash = require('lodash.throttle'); var _lodash2 = _interopRequireDefault(_lodash); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var COMPONENT_NAME = 'frustum-lock'; /** * @param aframe {Object} The Aframe instance to register with * @param componentName {String} The component name to use. Default: * 'frustum-lock' */ function aframeFrustumLockComponent(aframe) { var componentName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : COMPONENT_NAME; // From https://github.com/ngokevin/aframe-animation-component/blob/master/index.js#L176 function getPropertyType(el, property) { var _property$split = property.split('.'); var _property$split2 = _slicedToArray(_property$split, 2); var compName = _property$split2[0]; var propName = _property$split2[1]; var component = el.components[compName] || aframe.components[compName]; // Raw attributes if (!component) { return null; } if (propName) { return component.schema[propName].type; } return component.schema.type; } function isValidTypeOrThrow(element, propertyName, type) { var widthPropType = getPropertyType(element, propertyName); // null is a valid value (for raw attributes) if (widthPropType && widthPropType !== type) { throw new Error('unable to update property ' + propertyName + '; not a ' + type); } } function calculateFrustrumSize(cameraEl, depth) { var threeCamera = cameraEl.getObject3D('camera'); var height = 2 * Math.tan(threeCamera.fov * aframe.THREE.Math.DEG2RAD / 2) * depth; var width = height * threeCamera.aspect; return { width: width, height: height }; } /** * Frustum Lock component for A-Frame. */ aframe.registerComponent(componentName, { schema: { /** * @param {string} [widthProperty=width] - Once frustum width is * calculated, this property on the element will be given the value. */ widthProperty: { default: 'width' }, /** * @param {string} [heightProperty=width] - Once frustum height is * calculated, this property on the element will be given the value. */ heightProperty: { default: 'height' }, /** * @param {number} [depth=10] - Distance along the z-index to position the * entity once frustum size calculated. */ depth: { default: 10 }, /** * @param {number} [throttleTimeout=100] - Frustum calculations are * performed on resize and enter/exit vr. This throttles the calculations * to every throttleTimeout milliseconds. */ throttleTimeout: { default: 100 } }, /** * Set if component needs multiple instancing. */ multiple: false, /** * Called once when component is attached. Generally for initial setup. */ init: function init() { var _this = this; this._attachedEventListener = false; this._waitingForCameraInit = false; this._doFrustumCalcs = function (camera) { var _calculateFrustrumSiz = calculateFrustrumSize(camera, _this.data.depth); var width = _calculateFrustrumSiz.width; var height = _calculateFrustrumSiz.height; aframe.utils.entity.setComponentProperty(_this.el, _this.data.widthProperty, width); aframe.utils.entity.setComponentProperty(_this.el, _this.data.heightProperty, height); _this.el.setAttribute('position', '0 0 -' + _this.data.depth); }; this._activeCameraListener = function (event) { _this._waitingForCameraInit = false; _this.el.sceneEl.removeEventListener('camera-set-active', _this._activeCameraListener); _this._doFrustumCalcs(event.detail.cameraEl); }; this._attachEventListeners = function () { if (_this._attachedEventListener) { return; } _this._attachedEventListener = (0, _lodash2.default)(_this._resizeEventListener, _this.data.throttleTimeout); window.addEventListener('resize', _this._attachedEventListener); _this.el.sceneEl.addEventListener('enter-vr', _this._attachedEventListener); _this.el.sceneEl.addEventListener('exit-vr', _this._attachedEventListener); }; this._removeEventListeners = function () { if (!_this._attachedEventListener) { return; } window.removeEventListener('resize', _this._attachedEventListener); _this.el.sceneEl.removeEventListener('enter-vr', _this._attachedEventListener); _this.el.sceneEl.removeEventListener('exit-vr', _this._attachedEventListener); _this._attachedEventListener.cancel(); _this._attachedEventListener = undefined; }; this._resizeEventListener = function () { if (_this._waitingForCameraInit) { return; } var cameraEl = _this.el.sceneEl.systems.camera.activeCameraEl; // no active camera available. Let the update() function handle doing the // calculations in the future. if (!cameraEl) { return; } _this._doFrustumCalcs(cameraEl); }; }, /** * Called when component is attached and when component data changes. * Generally modifies the entity based on the data. */ update: function update(oldData) { if (oldData.widthProperty !== this.data.widthProperty) { isValidTypeOrThrow(this.el, this.data.widthProperty, 'number'); } if (oldData.heightProperty !== this.data.heightProperty) { isValidTypeOrThrow(this.el, this.data.heightProperty, 'number'); } // Check the waiting flag so we don't accidentally do the calculations // over and over again if (!aframe.utils.deepEqual(oldData, this.data) && !this._waitingForCameraInit) { if (this.el.sceneEl.systems.camera.activeCameraEl) { // Active camera available, go straight to the calculations this._doFrustumCalcs(this.el.sceneEl.systems.camera.activeCameraEl); } else { // no active camera, so let's wait this._waitingForCameraInit = true; this.el.sceneEl.addEventListener('camera-set-active', this._activeCameraListener); } } }, play: function play() { this._attachEventListeners(); }, stop: function stop() { this._removeEventListeners(); } }); }