Question

UPDATED QUESTION I've updated this to be a little more succinct..:

In this fiddle: http://jsfiddle.net/pX2Xb/4/ I have some raphael code that draws 3000 circles to a page. It then attempts to animate all circles at once (changes fill colour) over 10 seconds, which results in a clunky visual animation. Change the number of circles to 20 to see a much smoother animation for comparison.

My questions are (a) is it possible for me to make the update to the 3000 elements smoother and (b) if so, what does the code to do that look like?

Some notes:

  • I'm willing to take a small timing hit if there's some way to optimise around this, but, for example, I would like all circles to at least have updated in 1.5x whatever the animation time is set to. So, if animation is 10 seconds, all circles should have changed in 15.
  • 3000 elements is roughly my limit at the moment, so I'd be happy with it working for that :) In saying that, if a solution can effectively handle more than that, for a general case, that'd be really great.

older details, in case it helps

I'm creating a large map of US counties, of which there are over 3000; I'm using this Wikipedia svg file to get the relevant SVG paths to create the map, and am rendering the map using RaphaelJs.

Accordingly, I end up with over 3000 statements similar to the following:

    var cc_02130 = rsr.path("M 140.66674,.... 320.11635"); // county path
    cc_02130.attr({id: '02130',.. .."marker-start": 'none'}); // init attrs

I'm also creating a Paper.set() object to hold all of these elements:

var myset = paper.set([cc_56039, cc_56040, cc_56041 ...])

Forgetting for a moment that the file actually generated here is quite large, I would very much appreciate suggestions of how I can apply changes to the volume of objects detailed above, that is both quick and reasonably ok CPU wise (possible a big ask).

I'm definitely open to changing the structure of my code/objects, as long as I can individually change attributes of specific counties. For example, I would like to be able to apply a different colour to each path content in a second or two (for all 3000+).

The challenge I'm facing is not how to apply the colour changes, animations, etc, but how to do this quickly and efficiently. Right now, my machine screams at me if I loop and apply changes over the 3000+ objects; as an alternative, I was using setTimeout to break the changes out into smaller chunks (maybe 10 at a go, with a 40 ms delay). Over 3000 items, this becomes quite slow, and still uses a lot of CPU.

Thanks, oli

Was it helpful?

Solution

I don't know why, but D3.js is more efficient when animating a large number of elements at once. You can make them both work seamlessly by creating a Raphael function that receives a set and returns the html objects you want to animate:

Raphael.st.nodes = function() {
  var elements = [];
  this.forEach(function (i) {
     elements.push(i.node);
  });
  return elements;
}

And then you let d3 take it from there

//circleholder is a Raphael set
elements = circleholder.nodes()
d3.selectAll(elements)
  .transition()
  .attr("fill", function(d,i){return colours[randomNum(14)]})
  .duration(ANIMATION_DELAY)

Here is the jsfiddle: http://jsfiddle.net/mFecs/

OTHER TIPS

As I'm not currently that familiar with Rapael.js, I'd just suggest, in a generic JavaScript manner, that you could maybe create an changeRaphaelPathAttributeEvent, that you'd emit each time you'd like to change the paths, with the attribute changes as arguments to said event. And then attach event handlers to each of the paths that would carry out the attribute change, if that's possible.

That way, you would get around the problem of having a loop access all the paths variables and change the attributes synchronously (which would directly affect the page's responsiveness while processing); the method would have a lower impact on the performance that the user experiences.

Note: This is just a solution to get around the immediate performance hit. Maybe there are other solutions available of minimizing the amount of objects needed to access, that you should also consider (and that I'm not capable of telling you, as I don't know Raphael that well).

Did you write all the paths manually? I think is better if you put all the paths in an object. Then you can iterate the object and draw all the paths using, for example, for in. In that process you can give an internal ID to every path using element.id = 'name'. Once you have all the internal ID´s and the paths drawed, you can use the getById method: getById('pathId') If you are going to use Sets I recommend you to use Clousures, you can first declare the variables

var setName = Paper.Set()

and then push the paths in another loop

for (countie in Lousiana){
    setName.push(countie)
}

Then you have methods like Paper.forEach() if you want to apply some attributes to the elements. Or Set.FotEach(). Both take a callback function like argument, and then executes that function in each iteration.

If you want to share your code it wouldn´t be a problem for me to check it out and give my opinion on how to solve some of the situations. I´m also working with maps, I´m developing an Educative Video Game, if you want I can also share my code with you, there aren´t that much documentation about Raphael, so it would be useful for me to see how are you using it and maybe the same for you.

Bye!

To be honest, changing the attributes of 3000 items should be easy for any computer. Raphael uses a setInterval for each animated element. After the element's attribute is changed, the browser re-draws the entire page. This is 2999 un-necessary re-draws.

Here is what I would do: Instead of using the Raphael animation, use a loop to change each element. It is a little more complex, but it is faster. In addition, you can change the steps per second. If your animation is running too slow, just reduce the steps per second.

Here is an example: http://jsfiddle.net/dqJps/25/

Hope this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top