How to dispatch event on object method call in Three.js? - three.js

I've tried to dispatch an event on a method call like this:
Group.remove(obj).dispatchEvent({type:'remove', message:'Object removed'});
and listen to it on the canvas, like this:
document.getElementById('threeCanvas').addEventListener('remove', onObjRemove, false);
function onObjRemove(event){ console.log(event)}
but nothing happens...
On the page of Three.js Object3D says that Three.js EventDispatcher methods are available. Sooo what am I doing wrong?
r98

THREE.EventDispatcher has nothing to do with DOM events. It just enables an event-driven API on custom JavaScript objects. The following example from the github repository might help to better understand this:
function Car() {
this.start = function () {
this.dispatchEvent( { type: 'start', message: 'vroom vroom!' } );
};
}
Object.assign( Car.prototype, EventDispatcher.prototype );
// Using events
var car = new Car();
car.addEventListener( 'start', function ( event ) {
alert( event.message );
} );
car.start();
three.js R99

Related

How can you set anisotropy filtering value on A-Frame textures?

I am working on an A-Frame project that has small textures in the distance that are hard to see with the default three.js texture anisotropy setting of 1. How can you set this value on a texture in an A-Frame project?
Here is a revised component incorporating the suggested event listener and maximum anisotropy value.
AFRAME.registerComponent('max-anisotropy', {
dependencies: ['material', 'geometry'],
init: function () {
// wait until this material is ready ...
this.el.addEventListener('materialtextureloaded', () => {
let myMap = this.el.getObject3D('mesh').material.map;
myMap.anisotropy = this.el.sceneEl.renderer.capabilities.getMaxAnisotropy();
myMap.needsUpdate = true;
})
}
})
Here's a very simple A-Frame component that I created to solve this problem:
AFRAME.registerComponent('af', {
dependencies: ['material', 'geometry'],
init: function () {
// wait until this material is ready ...
setTimeout(() => {
this.el.getObject3D('mesh').material.map.anisotropy = 4;
this.el.getObject3D('mesh').material.map.needsUpdate = true;
}, 5000);
},
})
I cheat here by using setTimeout as I see an error without adding a delay to implementing the anisotropy setting change.
UPDATE: It might be possible to listen for the "materialtextureloaded" event per this example from the A-Frame docs: https://aframe.io/docs/1.0.0/components/visible.html#hiding-entity-until-texture-loaded

How to clear editor.scene before loading a new file?

Working in editor, loading a new geometry, I am trying to query the scene and remove existing geometries. The editor.scene.children and editor.scene.__webglObjects are both undefined. If I console.log( editor.scene ); and expand the object the children and __webglObjects have the elements I am trying to access. My clicker fingers are getting worn out trying to understand this.
Placing a
function deleteOnLoad(){
editor.storage.init( function () {
editor.config.clear();
editor.storage.clear( function () {
} );
});
}
deleteOnLoad();
before the main editor.storage.init seems to be one way to handle this.

How to unbind an external event when a controller/$scope is destroyed?

Supposed I have a controller for a page that is being rendered into an ng-view.
This controller binds to some events of an external source (such as an application-wide message bus) to update its model. This basically works very easy:
function MyController ($scope) {
$scope.bar = '…';
externalSource.on('foo', function (data) {
$scope.$apply(function () {
$scope.bar = data.bar;
});
});
}
The problem is: How do I unbind the controller from the external source once the view it it is associated with is not longer shown?
Is there something such as a dispose event or something similar?
Or is my approach completely wrong, and I should deal with something like that somehow else? If so, how?
To execute event unbind when controller's scope is got destroyed use:
$scope.$on('$destroy', function () { /* Unbind code here */ });
See Scope docs for more info
Use $routeChangeStart or $routeChangeSuccess events:
function MyController ($scope) {
$scope.bar = '…';
externalSource.on('foo', function (data) {
$scope.$apply(function () {
$scope.bar = data.bar;
});
});
$scope.$on('$routeChangeStart', function(next, current){
// unregister listener
// externalSource.off ....
});
}
... or $destroy event:
$scope.$on('$destroy', function(){
// unregister listener
// externalSource.off ....
});

Backbone.js - event trigger not work after rendering other views

There's a addPost function in my router. I don't want to re-create the postAddView every time the function is invoked:
addPost: function () {
var that = this;
if (!this.postAddView) {
this.postAddView = new PostAddView({
model: new Post()
});
this.postAddView.on('back', function () {
that.navigate('#/post/list', { trigger: true });
});
}
this.elms['page-content'].html(this.postAddView.render().el);
}
Here's the PostAddView:
PostAddView = backbone.View.extend({
events: {
'click #post-add-back': 'back'
}
, back: function (e) {
e.preventDefault();
this.trigger('back');
}
});
The first time the postAddView is rendered, the event trigger works well. However, after rendering other views to page-content and render postAddView back, the event trigger won't be trigger anymore. The following version of addPost works well, though.
addPost: function () {
var that = this, view;
view = new PostAddView({
model: new Post()
});
this.elms['page-content'].html(view.render().el);
view.on('back', function () {
delete view;
that.navigate('#/post/list', { trigger: true });
});
}
Somewhere you are calling jQuery's remove and that
In addition to the elements themselves, all bound events and jQuery data associated with the elements are removed.
so the delegate call that Backbone uses to bind events to your postAddView.el will be lost. Then, when you re-add your postAddView.el, there are is no delegate attached anymore and no events are triggered. Note that Backbone.View's standard remove method calls jQuery's remove; a few other things in jQuery, just as empty will do similar things to event handlers. So the actual function call that is killing your delegate could be hidden deep inside something else.
You could try calling delegateEvents manually:
this.elms['page-content'].html(this.postAddView.render().el);
this.postAddView.delegateEvents();
or better, just throw the view away and create a new one every time you need it. Your view objects should be pretty light weight so creating new ones should be cheap and a lot less hassle than trying to keep track of the existing views by hand.
If you really want to reuse the current DOM and View you do not need to set again and again the element as you are doing, everything that you call .html() you are destroying the DOM of the View and generating again and losing events. Also I prefer always to add the "el" in the DOM before render the View. I will have your function in this way:
addPost: function () {
if (!this.postAddView) {
this.postAddView = new PostAddView({
model: new Post()
});
this.postAddView.on('back', this.onBack);
this.elms['page-content'].html(this.postAddView.el);
}
this.postAddView.render();
},
onBack : function () {
this.navigate('#/post/list', { trigger: true });
}
I'm not fan of the use of local variables to refer to "this". If all of your Views uses _.bindAll(this) in the initialize method you could bind your events to your view and could use this(check how I transformed onBack).
With my code there is not a need to manually call this.delegateEvents()

zoom_changed only triggers once in Google Maps API version 3 using MVC

I'm trying to use the MVC objects in Google Maps version 3. What I can't seem to figure out is why my zoom_changed method is only invoked once. When I first load the map the zoom_changed method is invoked. But not when I zoom on the map.
function MarkerWidget (options) {
this.setValues(options);
this.set('zoom', this.map.zoom);
var marker = new google.maps.Marker({
icon : this.icon,
mouseOverIcon : this.mouseOverIcon,
orgIcon : this.orgIcon
});
marker.bindTo('map', this);
marker.bindTo('position', this);
google.maps.event.addListener(marker, 'mouseover', this.onmouseover);
google.maps.event.addListener(marker, 'mouseout', this.onmouseout);
}
MarkerWidget.prototype = new google.maps.MVCObject();
MarkerWidget.prototype.zoom_changed = function () {
$.log(this, new Date());
}
Shouldn't the map object fire the zoom event and notify all objects that has "this.set('zoom', this.map.zoom)"?
Found the solution. Se my comment on the original post.
Referenced comment below:
Found the solution, you need to specify a bindTo not set! ie. this.bindTo('zoom', this.map);

Resources