Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
var SphereControls = function (camera, domElement, config) {
var self = this;
config = config || {};
domElement = ( domElement !== undefined ) ? domElement : document;
domElement.addEventListener( 'mousedown', onDocumentMouseDown, false );
domElement.addEventListener( 'mousemove', onDocumentMouseMove, false );
domElement.addEventListener( 'mouseup', onDocumentMouseUp, false );
domElement.addEventListener( 'wheel', onDocumentMouseWheel, false );
domElement.addEventListener( 'touchstart', onDocumentTouchStart, false );
domElement.addEventListener( 'touchend', onDocumentTouchEnd, false );
domElement.addEventListener( 'touchmove', onDocumentTouchMove, false );
// [initialTime] determins the how much the speed is initially decreased,
// after the user releases the mouse button - the higher the value,
// the larger initial drop in speed - don't go below 2
var initialTime = 5,
// [lubrication] determins how fast the speed decreases after the user
// releases the mouse button. Larger value, the slower the speed drops
lubrication = 60;
var _isUserInteracting = false,
_dragStartPosition ={
x: null,
y: null,
lat: null,
lon: null
},
_curr = {
position: {
lon: 90,
lat: 0,
phi: Math.PI/2,
theta: 0
},
time: initialTime
},
_prev = {
position: {
phi: _curr.position.phi,
theta: _curr.position.theta
},
time2: 1
},
_target = new THREE.Vector3(),
_speed = {
phi: 0,
theta: 0
},
_autoRotation = {
enabled: !!config.autoRotate,
suspended: !config.autoRotate,
speed: Math.max(0, Math.min(10, config.autoRotateSpeed || 1)) / 1000 * (config.autoRotateDirection===-1 ? -1 : 1),
delay: Math.max(config.autoRotateDelay || 5000, 1000), // after how much time shoud the automatic rotation start
timeout: null
}
function onDocumentMouseDown( event ) {
// attache the original event as payload
self.dispatchEvent({type:'tap', original:event });
event.preventDefault();
_isUserInteracting = true;
autoRotate_Stop();
_curr.position.lon = THREE.Math.radToDeg( _curr.position.theta );
_curr.position.lat = 90 - THREE.Math.radToDeg( _curr.position.phi );
_dragStartPosition.x = event.clientX;
_dragStartPosition.y = event.clientY;
_dragStartPosition.lon = _curr.position.lon;
_dragStartPosition.lat = _curr.position.lat;
}
function onDocumentMouseMove( event ) {
if ( _isUserInteracting === true ) {
_curr.position.lon = ( _dragStartPosition.x - event.clientX ) * 0.1 + _dragStartPosition.lon;
_curr.position.lat = ( event.clientY - _dragStartPosition.y ) * 0.1 + _dragStartPosition.lat;
}
self.dispatchEvent({type:'mouseMove', original:event });
}
function onDocumentMouseUp( event ) {
_isUserInteracting = false;
event.preventDefault()
autoRotate_Start();
}
function onDocumentMouseWheel( event ) {
camera.fov += event.deltaY * 0.05;
camera.updateProjectionMatrix();
}
function onDocumentTouchStart( event ) {
if ( event.touches.length == 1 ) {
_isUserInteracting = true;
autoRotate_Stop();
// attache the original event as payload
self.dispatchEvent({type:'tap', original:event });
event.preventDefault();
event.stopPropagation();
_dragStartPosition.x = event.touches[ 0 ].pageX;
_dragStartPosition.y = event.touches[ 0 ].pageY;
_curr.position.lon = THREE.Math.radToDeg( _curr.position.theta );
_curr.position.lat = 90 - THREE.Math.radToDeg( _curr.position.phi );
_dragStartPosition.lon = _curr.position.lon;
_dragStartPosition.lat = _curr.position.lat;
}
}
function onDocumentTouchMove( event ) {
if ( event.touches.length == 1 ) {
event.preventDefault();
event.stopPropagation();
_curr.position.lon = ( _dragStartPosition.x - event.touches[0].pageX ) * 0.1 + _dragStartPosition.lon;
_curr.position.lat = ( event.touches[0].pageY - _dragStartPosition.y ) * 0.1 + _dragStartPosition.lat;
}
}
function onDocumentTouchEnd( event ) {
_isUserInteracting = false;
autoRotate_Start();
}
function autoRotate_Stop() {
_autoRotation.suspended = true;
if(_autoRotation.timeout) {
window.clearTimeout(_autoRotation.timeout);
}
}
function autoRotate_Start() {
if(_autoRotation.enabled) {
_autoRotation.timeout = window.setTimeout(function() {
_autoRotation.timeout = null;
_autoRotation.suspended = false;
}, _autoRotation.delay);
}
}
this.update = function() {
if(_isUserInteracting) {
_curr.position.lat = Math.max( - 85, Math.min( 85, _curr.position.lat ) );
_curr.position.phi = THREE.Math.degToRad( 90 - _curr.position.lat );
_curr.position.theta = THREE.Math.degToRad( _curr.position.lon );
// calculate the drag speed
_speed = {
phi: _curr.position.phi - _prev.position.phi,
theta: _curr.position.theta - _prev.position.theta
};
_curr.time = initialTime; // reset the time to initial time
_prev.time2 = 1;
} else {
var time2 = _curr.time*_curr.time/lubrication;
_curr.time++;
// calculating the new value of speed
_speed.phi = _speed.phi*_prev.time2 / time2;
_speed.theta = _speed.theta*_prev.time2 / time2;
_prev.time2 = time2;
if(!_autoRotation.suspended) {
_speed.theta = _autoRotation.speed < 0 ? Math.min(_speed.theta, _autoRotation.speed) : Math.max(_speed.theta, _autoRotation.speed);
}
// adjusting the camera angle
_curr.position.phi += _speed.phi;
_curr.position.theta += _speed.theta;
if(_curr.position.phi>Math.PI) {
// IF the camera hits the sphere bottom
// > make it bouce back
_curr.position.phi=Math.PI;
_speed.phi = _speed.phi*-1;
} else if(_curr.position.phi<0) {
// IF the camera hits the sphere top
// > make it bouce back
_curr.position.phi=0;
_speed.phi = _speed.phi*-1;
}
}
_prev.position.phi = _curr.position.phi;
_prev.position.theta = _curr.position.theta;
_target.x = 500 * Math.sin( _curr.position.phi ) * Math.cos( _curr.position.theta );
_target.y = 500 * Math.cos( _curr.position.phi );
_target.z = 500 * Math.sin( _curr.position.phi ) * Math.sin( _curr.position.theta );
camera.lookAt( _target );
}
}; // SphereControls = function (camera) {...}
SphereControls.prototype = Object.create( THREE.EventDispatcher.prototype );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment