( function () {
/**
* Information spot attached to panorama
* @constructor
* @param {number} [scale=300] - Infospot scale
* @param {imageSrc} [imageSrc=PANOLENS.DataImage.Info] - Image overlay info
* @param {boolean} [animated=true] - Enable default hover animation
*/
PANOLENS.Infospot = function ( scale, imageSrc, animated ) {
var scope = this, ratio, startScale, endScale, duration;
scale = scale || 300;
imageSrc = imageSrc || PANOLENS.DataImage.Info;
duration = 500;
THREE.Sprite.call( this );
this.type = 'infospot';
this.animated = animated !== undefined ? animated : true;
this.isHovering = false;
this.visible = false;
this.element;
this.toPanorama;
this.cursorStyle;
this.mode = PANOLENS.Modes.UNKNOWN;
this.scale.set( scale, scale, 1 );
this.rotation.y = Math.PI;
this.scaleFactor = 1.3;
this.container;
// Event Handler
this.HANDLER_FOCUS;
PANOLENS.Utils.TextureLoader.load( imageSrc, postLoad );
function postLoad ( texture ) {
texture.wrapS = THREE.RepeatWrapping;
texture.repeat.x = - 1;
texture.image.width = texture.image.naturalWidth || 64;
texture.image.height = texture.image.naturalHeight || 64;
ratio = texture.image.width / texture.image.height;
scope.scale.set( ratio * scale, scale, 1 );
startScale = scope.scale.clone();
scope.scaleUpAnimation = new TWEEN.Tween( scope.scale )
.to( { x: startScale.x * scope.scaleFactor, y: startScale.y * scope.scaleFactor }, duration )
.easing( TWEEN.Easing.Elastic.Out );
scope.scaleDownAnimation = new TWEEN.Tween( scope.scale )
.to( { x: startScale.x, y: startScale.y }, duration )
.easing( TWEEN.Easing.Elastic.Out );
scope.material.side = THREE.DoubleSide;
scope.material.map = texture;
scope.material.depthTest = false;
scope.material.needsUpdate = true;
}
function show () {
this.visible = true;
}
function hide () {
this.visible = false;
}
// Add show and hide animations
this.showAnimation = new TWEEN.Tween( this.material )
.to( { opacity: 1 }, duration )
.onStart( show.bind( this ) )
.easing( TWEEN.Easing.Quartic.Out );
this.hideAnimation = new TWEEN.Tween( this.material )
.to( { opacity: 0 }, duration )
.onComplete( hide.bind( this ) )
.easing( TWEEN.Easing.Quartic.Out );
// Attach event listeners
this.addEventListener( 'click', this.onClick );
this.addEventListener( 'hover', this.onHover );
this.addEventListener( 'hoverenter', this.onHoverStart );
this.addEventListener( 'hoverleave', this.onHoverEnd );
this.addEventListener( 'panolens-dual-eye-effect', this.onDualEyeEffect );
this.addEventListener( 'panolens-container', this.setContainer.bind( this ) );
this.addEventListener( 'dismiss', this.onDismiss );
this.addEventListener( 'panolens-infospot-focus', this.setFocusMethod );
};
PANOLENS.Infospot.prototype = Object.create( THREE.Sprite.prototype );
/**
* Set infospot container
* @param {HTMLElement|object} data - Data with container information
*/
PANOLENS.Infospot.prototype.setContainer = function ( data ) {
var container;
if ( data instanceof HTMLElement ) {
container = data;
} else if ( data && data.container ) {
container = data.container;
}
// Append element if exists
if ( container && this.element ) {
container.appendChild( this.element );
}
this.container = container;
};
/**
* Get container
* @return {HTMLElement} - The container of this infospot
*/
PANOLENS.Infospot.prototype.getContainer = function () {
return this.container;
};
/**
* This will be called by a click event
* Translate and lock the hovering element if any
* @param {object} event - Event containing mouseEvent with clientX and clientY
*/
PANOLENS.Infospot.prototype.onClick = function ( event ) {
if ( this.element && this.getContainer() ) {
this.onHoverStart( event );
// Lock element
this.lockHoverElement();
}
};
/**
* Dismiss current element if any
* @param {object} event - Dismiss event
*/
PANOLENS.Infospot.prototype.onDismiss = function ( event ) {
if ( this.element ) {
this.unlockHoverElement();
this.onHoverEnd();
}
};
/**
* This will be called by a mouse hover event
* Translate the hovering element if any
* @param {object} event - Event containing mouseEvent with clientX and clientY
*/
PANOLENS.Infospot.prototype.onHover = function ( event ) {
};
/**
* This will be called on a mouse hover start
* Sets cursor style to 'pointer', display the element and scale up the infospot
*/
PANOLENS.Infospot.prototype.onHoverStart = function ( event ) {
if ( !this.getContainer() ) { return; }
var cursorStyle = this.cursorStyle || ( this.mode === PANOLENS.Modes.NORMAL ? 'pointer' : 'default' );
this.isHovering = true;
this.container.style.cursor = cursorStyle;
if ( this.animated ) {
this.scaleDownAnimation && this.scaleDownAnimation.stop();
this.scaleUpAnimation && this.scaleUpAnimation.start();
}
if ( this.element && event.mouseEvent.clientX >= 0 && event.mouseEvent.clientY >= 0 ) {
if ( this.mode === PANOLENS.Modes.CARDBOARD || this.mode === PANOLENS.Modes.STEREO ) {
this.element.style.display = 'none';
this.element.left && ( this.element.left.style.display = 'block' );
this.element.right && ( this.element.right.style.display = 'block' );
// Store element width for reference
this.element._width = this.element.left.clientWidth;
this.element._height = this.element.left.clientHeight;
} else {
this.element.style.display = 'block';
this.element.left && ( this.element.left.style.display = 'none' );
this.element.right && ( this.element.right.style.display = 'none' );
// Store element width for reference
this.element._width = this.element.clientWidth;
this.element._height = this.element.clientHeight;
}
}
};
/**
* This will be called on a mouse hover end
* Sets cursor style to 'default', hide the element and scale down the infospot
*/
PANOLENS.Infospot.prototype.onHoverEnd = function () {
if ( !this.getContainer() ) { return; }
this.isHovering = false;
this.container.style.cursor = 'default';
if ( this.animated ) {
this.scaleUpAnimation && this.scaleUpAnimation.stop();
this.scaleDownAnimation && this.scaleDownAnimation.start();
}
if ( this.element && !this.element.locked ) {
this.element.style.display = 'none';
this.element.left && ( this.element.left.style.display = 'none' );
this.element.right && ( this.element.right.style.display = 'none' );
this.unlockHoverElement();
}
};
/**
* On dual eye effect handler
* Creates duplicate left and right element
* @param {object} event - panolens-dual-eye-effect event
*/
PANOLENS.Infospot.prototype.onDualEyeEffect = function ( event ) {
if ( !this.getContainer() ) { return; }
var element, halfWidth, halfHeight;
this.mode = event.mode;
element = this.element;
halfWidth = this.container.clientWidth / 2;
halfHeight = this.container.clientHeight / 2;
if ( !element ) {
return;
}
if ( !element.left || !element.right ) {
element.left = element.cloneNode( true );
element.right = element.cloneNode( true );
}
if ( this.mode === PANOLENS.Modes.CARDBOARD || this.mode === PANOLENS.Modes.STEREO ) {
element.left.style.display = element.style.display;
element.right.style.display = element.style.display;
element.style.display = 'none';
} else {
element.style.display = element.left.style.display;
element.left.style.display = 'none';
element.right.style.display = 'none';
}
// Update elements translation
this.translateElement( halfWidth, halfHeight );
this.container.appendChild( element.left );
this.container.appendChild( element.right );
};
/**
* Translate the hovering element by css transform
* @param {number} x - X position on the window screen
* @param {number} y - Y position on the window screen
*/
PANOLENS.Infospot.prototype.translateElement = function ( x, y ) {
if ( !this.element._width || !this.element._height || !this.getContainer() ) {
return;
}
var left, top, element, width, height, delta, container;
container = this.container;
element = this.element;
width = element._width / 2;
height = element._height / 2;
delta = element.verticalDelta !== undefined ? element.verticalDelta : 40;
left = x - width;
top = y - height - delta;
if ( ( this.mode === PANOLENS.Modes.CARDBOARD || this.mode === PANOLENS.Modes.STEREO )
&& element.left && element.right
&& !( x === container.clientWidth / 2 && y === container.clientHeight / 2 ) ) {
left = container.clientWidth / 4 - width + ( x - container.clientWidth / 2 );
top = container.clientHeight / 2 - height - delta + ( y - container.clientHeight / 2 );
this.setElementStyle( 'transform', element.left, 'translate(' + left + 'px, ' + top + 'px)' );
left += container.clientWidth / 2;
this.setElementStyle( 'transform', element.right, 'translate(' + left + 'px, ' + top + 'px)' );
} else {
this.setElementStyle( 'transform', element, 'translate(' + left + 'px, ' + top + 'px)' );
}
};
/**
* Set vendor specific css
* @param {string} type - CSS style name
* @param {HTMLElement} element - The element to be modified
* @param {string} value - Style value
*/
PANOLENS.Infospot.prototype.setElementStyle = function ( type, element, value ) {
var style = element.style;
if ( type === 'transform' ) {
style.webkitTransform = style.msTransform = style.transform = value;
}
};
/**
* Set hovering text content
* @param {string} text - Text to be displayed
*/
PANOLENS.Infospot.prototype.setText = function ( text ) {
if ( this.element ) {
this.element.textContent = text;
}
};
/**
* Set cursor css style on hover
*/
PANOLENS.Infospot.prototype.setCursorHoverStyle = function ( style ) {
this.cursorStyle = style;
};
/**
* Add hovering text element
* @param {string} text - Text to be displayed
* @param {number} [delta=40] - Vertical delta to the infospot
*/
PANOLENS.Infospot.prototype.addHoverText = function ( text, delta ) {
if ( !this.element ) {
this.element = document.createElement( 'div' );
this.element.style.display = 'none';
this.element.style.color = '#fff';
this.element.style.top = 0;
this.element.style.maxWidth = '50%';
this.element.style.maxHeight = '50%';
this.element.style.textShadow = '0 0 3px #000000';
this.element.style.fontFamily = '"Trebuchet MS", Helvetica, sans-serif';
this.element.style.position = 'absolute';
this.element.classList.add( 'panolens-infospot' );
this.element.verticalDelta = delta !== undefined ? delta : 40;
}
this.setText( text );
};
/**
* Add hovering element by cloning an element
* @param {HTMLDOMElement} el - Element to be cloned and displayed
* @param {number} [delta=40] - Vertical delta to the infospot
*/
PANOLENS.Infospot.prototype.addHoverElement = function ( el, delta ) {
if ( !this.element ) {
this.element = el.cloneNode( true );
this.element.style.display = 'none';
this.element.style.top = 0;
this.element.style.position = 'absolute';
this.element.classList.add( 'panolens-infospot' );
this.element.verticalDelta = delta !== undefined ? delta : 40;
}
};
/**
* Remove hovering element
*/
PANOLENS.Infospot.prototype.removeHoverElement = function () {
if ( this.element ) {
if ( this.element.left ) {
this.container.removeChild( this.element.left );
this.element.left = null;
}
if ( this.element.right ) {
this.container.removeChild( this.element.right );
this.element.right = null;
}
this.container.removeChild( this.element );
this.element = null;
}
};
/**
* Lock hovering element
*/
PANOLENS.Infospot.prototype.lockHoverElement = function () {
if ( this.element ) {
this.element.locked = true;
}
};
/**
* Unlock hovering element
*/
PANOLENS.Infospot.prototype.unlockHoverElement = function () {
if ( this.element ) {
this.element.locked = false;
}
};
/**
* Show infospot
* @param {number} [delay=0] - Delay time to show
*/
PANOLENS.Infospot.prototype.show = function ( delay ) {
delay = delay || 0;
if ( this.animated ) {
this.hideAnimation && this.hideAnimation.stop();
this.showAnimation && this.showAnimation.delay( delay ).start();
}
};
/**
* Hide infospot
* @param {number} [delay=0] - Delay time to hide
*/
PANOLENS.Infospot.prototype.hide = function ( delay ) {
delay = delay || 0;
if ( this.animated ) {
this.showAnimation && this.showAnimation.stop();
this.hideAnimation && this.hideAnimation.delay( delay ).start();
}
};
/**
* Set focus event handler
*/
PANOLENS.Infospot.prototype.setFocusMethod = function ( event ) {
if ( event ) {
this.HANDLER_FOCUS = event.method;
}
};
/**
* Focus camera center to this infospot
* @param {number} [duration=1000] - Duration to tween
* @param {function} [easing=TWEEN.Easing.Exponential.Out] - Easing function
*/
PANOLENS.Infospot.prototype.focus = function ( duration, easing ) {
if ( this.HANDLER_FOCUS ) {
this.HANDLER_FOCUS( this.position, duration, easing );
this.onDismiss();
}
};
/**
* Dispose infospot
*/
PANOLENS.Infospot.prototype.dispose = function () {
this.removeHoverElement();
this.material.dispose();
if ( this.parent ) {
this.parent.remove( this );
}
};
} )();