I'm working on a ReactJS app in which I'm using EaselJS to handle multiple canvases, on same page I have to add and remove different canvases on the base of different conditions to render different views. Even after removing a canvas using following code to dispose a canvas component
createjs.Touch.disable(this.stage);
this.stage.removeAllChildren();
this.stage.removeAllEventListeners();
this.stage.enableDOMEvents(false);
some events are being triggered. After using application for some time it start to use a lot of processing and memory. After having a look at performance tab in developer tools in chrome, I came to know a timer event is being called for every canvas which ever was added. After inspecting code I come to know that
this.stage.enableMouseOver();
is setting an setInterval timer which is not being removed even after calling all above code and I can't find any way to remove it.
Can anyone please help me to get rid of it.
Thanks in advance
The enableMouseOver method is documented to both add and remove the functionality from a Stage. By passing 0 as the frequency, the interval should be cleared.
stage.enableMouseOver(0);
From the documentation:
Enables or disables (by passing a frequency of 0)
and
frequency: Optional param specifying the maximum number of times per second to broadcast mouse over/out events. Set to 0 to disable mouse over events completely.
I did a quick pass on the code, and it definitely removes the interval.
Related
Link 1 - http://horebmultimedia.com/Sam3/
Link 2 - http://horebmultimedia.com/Sam5/
In the above links, i have added a set of numbers added in separate containers in each file and u can find the FPS on the top right. The issue is when i mouse over in this Link 1 and click any numbers, as u see the FPS is getting slower & slower, making the world to rotate slower on the left side.
While on this link, Link 2, I added only one mouse over and 5 mouse over, but there is not much difference in FPS, why it lags so much when i have 37 containers. I can give my code if u need to resolve.
I had a rough look at your code, but digging through an entire project is not a fantastic way to debug an optimization problem.
The first thing to consider is if you have mouseOver enabled on your stage, I would recommend a liberal use of mouseChildren=false on interactive elements, and mouseEnabled=mouseChildren=false on anything not interactive. The rollover could be a big cause, as it requires everything to be drawn 20 times per second (in your usage). Text and vectors can be expensive to redraw.
// Non-interactive elements (block all mouse interactions)
element.mouseEnabled = element.mouseChildren = false;
// Interactive elements (reduce mouse-checking children individually)
element.mouseChildren = false;
If they don't change, you might consider caching text elements, or button graphics. I think I saw some caching in the source - but its generally a good thing to consider.
--
With that said, debugging optimization can be tough.. If removing all the buttons brings your performance up, consider how your buttons are being constructed, and what their cost is.
* Mouse over is expensive
* Vectors and text can be expensive
* Caching can help when used right, but can be expensive if it happens too often.
* Review what is happening on tick(). Sometimes, code is running constantly, which doesn't need to.
--
A few other notes:
This does not do what you think: _oButton.off("mousedown"); -- You need to pass the result of the on() call. If you are just cleaning up, call _oButton.removeAllEventListeners().
You don't need to set the cursor on mouseover. The cursor will only change when it rolls over -- so just set it once, and then get rid of your buttonover stuff.
It might make sense to just extend EventDispatcher for your custom classes, which gives you things like the on() method, which supports a data param. I might recommend this in place of your addEventListener stuff in CTextButton
Note that RAF does not support a framerate property (it just uses the browser's RAF rate, which is usually 60fps). Use createjs.Ticker.timingMode instead of the deprecated useRAF.
Hope that helps a little.
Basically, i have to update multiple features, specifically setGeometry, but everytime an update is applied on single feature, the change event is triggered, causing the layer to be redrawn. So, if there are 500 features updated, the layer will ve redrawn 500 times.
I would like to payse the redraw event, untill all features are updated.
Update:
One of the workaround that i can think of is:
getFeatures()
clear(true)
(Updates the features)
addFeatures(features)
But i'm not sure if there's a cleaner way than this
Short answer:
var feature = layer.getFeatureById("myId");
var geometry = new ol.geom.Point(ol.proj.fromLonLat([lon, lat]));
feature.set(feature.getGeometryName(), geometry, true);
........
source.changed()
Long answer:
After looking at the setGeometry source code (feature.js), basically it will use the set method on geometryName_. So by explicitly setting the same property and set opt_silent to true, it will not fire the change event (object.js).
Once you have completed the changes, just fire the change event on the layer.
Don't skip the change events
The basic premise of the question - that each feature change results in a redraw of the layer - is incorrect.
When a feature changes, the map will request a redraw on the next animation frame. But if multiple features are changed within the same thread of execution, the browser won't redraw anything until the script finishes. You could loop through and change millions of features with only a single redraw.
You can verify when a render is done by listening to the postrender event:
map.on('postrender', function(event){
console.log('did render', new Date())
})
You could set the geometry without triggering any events, as suggested in your answer. But doing so could cause issues such as a corrupt spatial index in the source. Omitting the change events should only be done if you know for sure that no one is interested in that change.
What delays are there then?
With that said, there's still a performance hit to updating a lot of features. I wrote this demo, which replaces all features' geometries on each map click. On my laptop, it takes around 600 milliseconds to replace all 10 000 geometries. During that time, the map and the browser is unresponsive and no updates are shown until the next redraw. The duration of the redraw depends on the zoom level.
When settings the geometry on a feature, it will:
add the geometry to it's properties
remove any listeners to the previous geometry.
add change listeners to the new geometry.
trigger a change event to notify listeners that it has changed.
On of the listeners of the feature will be the source, which will:
update the spatial index, if used.
trigger a change event to notify listeners that it has changed.
The layer and eventually the map will we notified of these changes, and queue a redraw.
Possible optimizations
If you wish to be able to change many features in a more performant way, I'd suggest considering:
Disabling the spatial index
As noted in the docs, setting useSpatialIndex to false may increase performance in some situations. Without the spatial index, the source has less computations to do on each feature change. Other uses of the source might become less performant without the index, though.
Modifying the geometry instead of replacing it
Using setCoordinates on the geometry is faster than using setGeometry on the feature. The whole Geometry instance creation can be skipped, and no listeners has to be modified. This assumes that you can modify the geometry, such as if the feature is the sole 'owner' of the geometry.
Trying to split the feature modifications into smaller chunks
This is contrary to the intent of the question. If it takes a lot of time to modify the features, you probably want to trigger a render more often. That would make the map appear more responsive, as well as allowing the browser to catch up on handling events and other pending tasks.
I simply want to restart a canvas on a website. I do not want to reload the page, so the restart has to be done dynamically. Problem : after few restarts the animation gets slower and the memory increases.
Actually i face a more complex situation : I use a javascript framework to load pages, MeteorJS with IronRouter. Changing the URL only changes the DOM (nodes are removed) but the 'page' is not really left. So i thought that coming back to my page with the canvas can be assimilated to a dynamic canvas restart, since i have no solution to that too.
That is why i describe the problem from two points of view (same problem on both) :
first a simple page where a button asks the canvas to restart
then applying the solution to MeteorJS with IronRouter and try to come back on my page many times.
Note : my canvas content is some WebGL (in ThreeJS).
1. Deleting and re-initializing canvas dynamically.
In this case i face those two problems :
animation not smooth
memory increase. To be precise :
(source: hostingpics.net)
Page loaded, playing with canvas / 2. Restarting the canvas 30-40 times (i did not stop restarting so i do not know why the increase is not linear) / 3. Playing with canvas / 4. Closing the tab (reloading the page only frees a small fraction of what has been mobilized)
Few questions on SO yet asked how to solve those issues, but reproducing the solutions partially solves the problem. I have read two main things :
the canvas-related parts (nodes, variables, functions) have to be cleaned from the DOM (That is something Meteor does. But in this first part it stays relevant and yet is not enough to prevent the issues).
if requestAnimationFrame has been called, it needs to get assigned to a variable so cancelAnimationFrame(variable) can stop it. That indeed seems to prevent the frame rate drop. While memory stays high, at least the movements remain fluent.
With those solutions the memory problem still happens.
Here is an example : http://codepen.io/Astrak/pen/RNYLxd
2. In MeteorJS : i cannot get this partial solution work.
Here is what i wrote in my app, with id=requestAnimationFrame(myAnimation) declared globaly :
Template.myTemplate.destroyed=function(){
cancelAnimationFrame(id);
document.body.removeChild(document.querySelector('canvas'));//useful in Meteor ?
};
Despite those instructions, my animation gets slow much quicker with MeteorJS than in the previous example : it becomes unusable after 3-4 page change. And same memory problem.
Thanks for every comment and answer and happy coding :)
Related questions :
Performance drop on multiple restart of scene from threejs
Performance drops when trying to reset whole scene with Three.js
How do we handle webgl context lost event in Three.js
Pause, resume and restart Canvas animations with JS
How can I restart my function?
JavaScript restart canvas script
For memory problem, I'd recommend Chrome's javascript profiler - you can compare snapshots before and after canvas is reloaded and see which objects are not freed from memory.
Problem usually comes from event handlers and/or using closures. If you, for example, hook a function to window object's onresize event, all objects referenced in this function will stay in memory as long as window exists. So always remove event handlers from global objects if you want to refresh page content without actually refreshing the browser session.
Similar with closures. They will create link between function's scope and referenced variables preventing GC to collect them. Check this https://www.meteor.com/blog/2013/08/13/an-interesting-kind-of-javascript-memory-leak
I have two questions about Crafty (I've also asked in their google group community, but it seems very few people look at that).
I've followed this tutorial http://buildnewgames.com/introduction-to-crafty/ and also took a look at the "isometric" demo in crafty's website of a bunch of blocks (http://craftyjs.com/demos/isometric/). And I've been trying some stuff by combining what I've learned in both.
(Q1) When I use the fourway component (used in the tutorial a lot), if I hold the left arrow key for example and CTRL-TAB out the current tab while holding left, and then go back (not necessarily holding left anymore), then the my character seems to get stuck in moving to the "left" direction. It also happens for the other 3 directions. Is that a known issue? Is there anyway to fix it without changing crafty?
It happens here with firefox 29 and chrome 34. My code is pretty much the one in the final version presented at the tutorial's end (it's not the same, but even when it was the same I already had this issue).
By the way, when this happens, if I CTRL-TAB out and back again holding that left key, things go back to normal (the movement stops).
(Q2) The isometric-ish features interprets Z as being height, and the gravity component uses Y for height. Isn't this a problem? Can I, maybe, for example, tell gravity to use something else, other than y, for height?
Regarding (Q1), the movement is managed by keydown and keyup events. If you change the tab when the start of a movement was triggered, the fourway component never gets any keyup event to stop again. You could use a workaround like the following:
Crafty.settings.modify("autoPause", true);
Enabling autoPause (somewhere in your init function) will pause your game when the browser tab crafty is running in is inactive. You can then react to this event by triggering keyup events or preventing the player component to move like this:
player.bind('Pause', function() {
this.disableControl();
});
player.bind('Unpause', function() {
this.enableControl();
});
You might want to stop animation there too if you handle that in your player component..
I'm having trouble re-initializing a TimelineMax sequence. When the window is resized, I need to revert all the tweens to their default styles and re-initialize them based on the new window size. Is there a simple way to effectively destroy the timeline and start fresh, without manually resetting all the CSS properties?
Depending on what you are trying to achieve, I can suggest two methods.
The first is exactly as you are describing:
myTimeline.pause(0, true); //Go back to the start (true is to suppress events)
myTimeline.remove();
This takes everything back to the way they were at the start of the timeline. You can remove any initialization properties by calling .invalidate() as well.
The second method, I'm adding because it may be worth thinking about...
Rather than completely restarting the tweens/timeline, why not .invalidate() (items stay where they are, just the timeline itself is cleared) and use the tweening method .to() rather than .from(), as you'll get a nicer user experience when everything moves from it's previous position to it's new position rather than completely restarting.
Also, the majority of users don't resize the window. So think about whether or not it's worth it if it becomes a major time consumer.
API/Docs: http://api.greensock.com/js/