threex.domevent.js | |
---|---|
This THREEx helper makes it easy to handle the mouse events in your 3D scene Lets get startedFirst you include it in your page
use the object oriented apiYou bind an event like this
To unbind an event, just do
As an alternative, there is another naming closer DOM events. Pick the one you like, they are doing the same thing
Supported EventsAlways in a effort to stay close to usual pratices, the events name are the same as in DOM. The semantic is the same too. Currently, the available events are click, dblclick, mouseup, mousedown, mouseover and mouse out. use the standalone apiThe object-oriented api modifies THREE.Object3D class.
It is a global class, so it may be legitimatly considered unclean by some people.
If this bother you, simply do First, you instanciate the object
Then you bind an event like this
To unbind an event, just do
Code | |
/** @namespace */
var THREEx = THREEx || {}; | |
Constructor | THREEx.DomEvent = function(domElement)
{
this._domElement= domElement || document;
this._projector = new THREE.Projector();
this._selected = null; |
Bind dom event for mouse and touch | var _this = this;
this._$onClick = function(){ _this._onClick.apply(_this, arguments); };
this._$onDblClick = function(){ _this._onDblClick.apply(_this, arguments); };
this._$onMouseMove = function(){ _this._onMouseMove.apply(_this, arguments); };
this._$onMouseDown = function(){ _this._onMouseDown.apply(_this, arguments); };
this._$onMouseUp = function(){ _this._onMouseUp.apply(_this, arguments); };
this._$onTouchMove = function(){ _this._onTouchMove.apply(_this, arguments); };
this._$onTouchStart = function(){ _this._onTouchStart.apply(_this, arguments); };
this._$onTouchEnd = function(){ _this._onTouchEnd.apply(_this, arguments); };
this._domElement.addEventListener( 'click' , this._$onClick , false );
this._domElement.addEventListener( 'dblclick' , this._$onDblClick , false );
this._domElement.addEventListener( 'mousemove' , this._$onMouseMove , false );
this._domElement.addEventListener( 'mousedown' , this._$onMouseDown , false );
this._domElement.addEventListener( 'mouseup' , this._$onMouseUp , false );
this._domElement.addEventListener( 'touchmove' , this._$onTouchMove , false );
this._domElement.addEventListener( 'touchstart' , this._$onTouchStart , false );
this._domElement.addEventListener( 'touchend' , this._$onTouchEnd , false );
} |
Destructor | THREEx.DomEvent.prototype.destroy = function()
{ |
unBind dom event for mouse and touch | this._domElement.removeEventListener( 'click' , this._$onClick , false );
this._domElement.removeEventListener( 'dblclick' , this._$onDblClick , false );
this._domElement.removeEventListener( 'mousemove' , this._$onMouseMove , false );
this._domElement.removeEventListener( 'mousedown' , this._$onMouseDown , false );
this._domElement.removeEventListener( 'mouseup' , this._$onMouseUp , false );
this._domElement.removeEventListener( 'touchmove' , this._$onTouchMove , false );
this._domElement.removeEventListener( 'touchstart' , this._$onTouchStart , false );
this._domElement.removeEventListener( 'touchend' , this._$onTouchEnd , false );
}
THREEx.DomEvent.eventNames = [
"click",
"dblclick",
"mouseover",
"mouseout",
"mousedown",
"mouseup"
];
/********************************************************************************/
/* domevent context */
/********************************************************************************/ |
handle domevent context in object3d instance | THREEx.DomEvent.prototype._objectCtxInit = function(object3d){
object3d._3xMouseEvent = {};
}
THREEx.DomEvent.prototype._objectCtxDeinit = function(object3d){
delete object3d._3xMouseEvent;
}
THREEx.DomEvent.prototype._objectCtxIsInit = function(object3d){
return object3d._3xMouseEvent ? true : false;
}
THREEx.DomEvent.prototype._objectCtxGet = function(object3d){
return object3d._3xMouseEvent;
}
/********************************************************************************/
/* */
/********************************************************************************/
THREEx.DomEvent.prototype.bind = function(object3d, eventName, callback)
{
console.assert( THREEx.DomEvent.eventNames.indexOf(eventName) !== -1, "not available events:"+eventName );
if( !this._objectCtxIsInit(object3d) ) this._objectCtxInit(object3d);
var objectCtx = this._objectCtxGet(object3d);
if( !objectCtx[eventName+'Handlers'] ) objectCtx[eventName+'Handlers'] = [];
objectCtx[eventName+'Handlers'].push({
callback : callback
});
}
THREEx.DomEvent.prototype._bound = function(eventName, object3d)
{
var objectCtx = this._objectCtxGet(object3d);
if( !objectCtx ) return false;
return objectCtx[eventName+'Handlers'] ? true : false;
}
/********************************************************************************/
/* onMove */
/********************************************************************************/ |
handle mousemove kind of events | THREEx.DomEvent.prototype._onMove = function(mouseX, mouseY)
{
var vector = new THREE.Vector3( mouseX, mouseY, 1 );
this._projector.unprojectVector( vector, camera );
var ray = new THREE.Ray( camera.position, vector.subSelf( camera.position ).normalize() );
var intersects = ray.intersectScene( scene );
var oldSelected = this._selected;
if( intersects.length > 0 ){
var intersect = intersects[ 0 ];
var newSelected = intersect.object;
this._selected = newSelected;
var overHandlers, outHandlers;
if( oldSelected != newSelected ){ |
if newSelected bound mouseenter, notify it | if( this._bound('mouseover', newSelected) ){
overHandlers = this._objectCtxGet(newSelected).mouseoverHandlers.slice(0);
} |
if there is a oldSelect and oldSelected bound mouseleave, notify it | if( oldSelected && this._bound('mouseout', oldSelected) ){
outHandlers = this._objectCtxGet(oldSelected).mouseoutHandlers.slice(0);
}
}
}else{ |
if there is a oldSelect and oldSelected bound mouseleave, notify it | if( oldSelected && this._bound('mouseout', oldSelected) ){
outHandlers = this._objectCtxGet(oldSelected).mouseoutHandlers.slice(0);
}
this._selected = null;
} |
notify mouseEnter - done at the end with a copy of the list to allow callback to remove handlers | overHandlers && overHandlers.forEach(function(handler){
handler.callback(newSelected);
}) |
notify mouseLeave - done at the end with a copy of the list to allow callback to remove handlers | outHandlers && outHandlers.forEach(function(handler){
handler.callback(oldSelected);
})
}
/********************************************************************************/
/* onEvent */
/********************************************************************************/ |
handle click kind of events | THREEx.DomEvent.prototype._onEvent = function(eventName, mouseX, mouseY)
{
var vector = new THREE.Vector3( mouseX, mouseY, 1 );
this._projector.unprojectVector( vector, camera );
var ray = new THREE.Ray( camera.position, vector.subSelf( camera.position ).normalize() );
var intersects = ray.intersectScene( scene ); |
FIXME to test only the bound objects is likely faster to run - handle the list var intersects = ray.intersectObjects( [mesh] ); | for(var i = 0; i < intersects.length; i++){
var intersect = intersects[i];
var object3d = intersect.object;
var objectCtx = this._objectCtxGet(object3d);
if( !objectCtx ) continue;
var handlers = objectCtx[eventName+'Handlers'];
handlers && handlers.length && handlers.forEach(function(handler){
handler.callback(object3d, intersect);
})
}
}
/********************************************************************************/
/* handle mouse events */
/********************************************************************************/ |
handle mouse events | THREEx.DomEvent.prototype._onMouseDown = function(event){ return this._onMouseEvent('mousedown', event); }
THREEx.DomEvent.prototype._onMouseUp = function(event){ return this._onMouseEvent('mouseup' , event); }
THREEx.DomEvent.prototype._onMouseEvent = function(eventName, domEvent)
{
var mouseX = +(domEvent.clientX / window.innerWidth ) * 2 - 1;
var mouseY = -(domEvent.clientY / window.innerHeight) * 2 + 1;
return this._onEvent(eventName, mouseX, mouseY);
}
THREEx.DomEvent.prototype._onMouseMove = function(event)
{
var mouseX = +(event.clientX / window.innerWidth ) * 2 - 1;
var mouseY = -(event.clientY / window.innerHeight) * 2 + 1;
return this._onMove(mouseX, mouseY);
}
THREEx.DomEvent.prototype._onClick = function(event)
{ |
TODO handle touch ? | return this._onMouseEvent('click' , event);
}
THREEx.DomEvent.prototype._onDblClick = function(event)
{ |
TODO handle touch ? | return this._onMouseEvent('dblclick' , event);
}
/********************************************************************************/
/* handle touch events */
/********************************************************************************/ |
handle touch events | THREEx.DomEvent.prototype._onTouchStart = function(event){ return this._onTouchEvent('mousedown', event); }
THREEx.DomEvent.prototype._onTouchEnd = function(event){ return this._onTouchEvent('mouseup' , event); }
THREEx.DomEvent.prototype._onTouchMove = function(event)
{
if( event.touches.length != 1 ) return undefined;
event.preventDefault();
var mouseX = +(event.touches[ 0 ].pageX / window.innerWidth ) * 2 - 1;
var mouseY = -(event.touches[ 0 ].pageY / window.innerHeight) * 2 + 1;
return this._onMove('mousemove', mouseX, mouseY);
}
THREEx.DomEvent.prototype._onTouchEvent = function(eventName, domEvent)
{
if( event.touches.length != 1 ) return undefined;
event.preventDefault();
var mouseX = +(event.touches[ 0 ].pageX / window.innerWidth ) * 2 - 1;
var mouseY = -(event.touches[ 0 ].pageY / window.innerHeight) * 2 + 1;
return this._onEvent(eventName, mouseX, mouseY);
}
/********************************************************************************/ |
Patch THREE.Object3D | /********************************************************************************/ |
handle noConflit. | THREEx.DomEvent.noConflict = function(){
THREEx.DomEvent.noConflict.symbols.forEach(function(symbol){
THREE.Object3D.prototype[symbol] = THREEx.DomEvent.noConflict.previous[symbol]
})
} |
Backup previous values to restore them later if needed. | THREEx.DomEvent.noConflict.symbols = ['on', 'off', 'addEventListener', 'removeEventListener'];
THREEx.DomEvent.noConflict.previous = {};
THREEx.DomEvent.noConflict.symbols.forEach(function(symbol){
THREEx.DomEvent.noConflict.previous[symbol] = THREE.Object3D.prototype[symbol]
}) |
begin the actual patching of THREE.Object3D | |
create the global instance of THREEx.DomEvent | THREE.Object3D._threexDomEvent = new THREEx.DomEvent(); |
wrap mouseevents.bind() | THREE.Object3D.prototype.on =
THREE.Object3D.prototype.addEventListener = function(eventName, callback){
THREE.Object3D._threexDomEvent.bind(this, eventName, callback);
return this;
} |
wrap mouseevents.unbind() | THREE.Object3D.prototype.off =
THREE.Object3D.prototype.removeEventListener = function(eventName, callback){ |
TODO to code | return this;
}
|