views:

146

answers:

2

Hi! I've recently come up with the idea to create a tag cloud like animation shaped like the earth. I've extracted the coastline coordinates from ngdc.noaa.gov and wrote a little script that displayed it in my browser. Now as you can imagine, the whole coastline consists of about 48919 points, which my script would individually render (each coordinate being represented by one span). Obviously no browser is capable of rendering this fluently - but it would be nice if I could render as much as let's say 200 spans (twice as much as now) on my old p4 2.8 Ghz (as a representative benchmark). Are there any javascript optimizations I could use in order to speed up the display of those spans?

One 'coordinate':

<div id="world_pixels">
<span id="wp_0" style="position:fixed; top:0px; left:0px; z-index:1; font-size:20px; cursor:pointer;cursor:hand;" onmouseover="magnify_world_pixel('wp_0');" onmouseout="shrink_world_pixel('wp_0');" onClick="set_askcue_bar('', 'new york')">new york</span>
</div>

The script:

$(document).ready(function(){

        world_pixels = $("#world_pixels span");

        world_pixels.spin();

        setInterval("world_pixels.spin()",1500);

});


z = new Array();

$.fn.spin = function () {

    for(i=0; i<this.length; i++) {

            /*actual screen coordinates: x/y/z --> left/font-size/top
                  300/13/0  300/6/300
                         | /
                         |/
            0/13/300 ----|----  600/13/300
                        /|
                       / |
              300/20/300    300/13/600
            */

            /*scale font size*/
        var resize_x = 1;
            /*scale width*/
        var resize_y = 2.5;
            /*scale height*/
        var resize_z = 2.5;

        var from_left = 300;
        var from_top = 20;

            /*actual math coordinates:

                        1  -1
                         | /
                         |/
                  1  ----|---- -1
                        /|
                       / |
                      1  -1 
            */

         //var get_element = document.getElementById();
         //var font_size = parseInt(this.style.fontSize);
         var font_size = parseInt($(this[i]).css("font-size"));

         var left = parseInt($(this[i]).css("left"));




         if (coast_line_array[i][1]) {

         } else {

            var top = parseInt($(this[i]).css("top"));

            z[i] = from_top + (top - (300 * resize_z)) / (300 * resize_z);
            //global beacause it's used in other functions later on 

            var top_new = from_top + Math.round(Math.cos(coast_line_array[i][2]/90*Math.PI) * (300 * resize_z) + (300 * resize_z));

            $(this[i]).css("top", top_new);

            coast_line_array[i][3] = 1;

         }


         var x = resize_x * (font_size - 13) / 7;

         var y = from_left + (left- (300 * resize_y)) / (300 * resize_y);




         if (y >= 0) {

             this[i].phi = Math.acos(x/(Math.sqrt(x^2 + y^2)));

         } else {

             this[i].phi = 2*Math.PI - Math.acos(x/(Math.sqrt(x^2 + y^2)));
             i
         }

         this[i].theta = Math.acos(z[i]/Math.sqrt(x^2 + y^2 + z[i]^2));

         var font_size_new = resize_x * Math.round(Math.sin(coast_line_array[i][4]/90*Math.PI) * Math.cos(coast_line_array[i][0]/180*Math.PI) * 7 + 13);

         var left_new = from_left + Math.round(Math.sin(coast_line_array[i][5]/90*Math.PI) * Math.sin(coast_line_array[i][0]/180*Math.PI) * (300 * resize_y) + (300 * resize_y));



         //coast_line_array[i][6] = coast_line_array[i][7]+1;

         if ((coast_line_array[i][0] + 1) > 180) {

            coast_line_array[i][0] = -180;

         } else {

            coast_line_array[i][0] = coast_line_array[i][0] + 0.25;

         }

         $(this[i]).css("font-size", font_size_new);

         $(this[i]).css("left", left_new);




    }

}

resize_x = 1;

function magnify_world_pixel(element) {

    $("#"+element).animate({
        fontSize: resize_x*30+"px"
                    }, {
        duration: 1000  
                    });

}

function shrink_world_pixel(element) {

    $("#"+element).animate({
        fontSize: resize_x*6+"px"
                    }, {
        duration: 1000  
                    });



}

I'd appreciate any suggestions to optimize my script, maybe there is even a totally different approach on how to go about this. The whole .js file which stores the array for all the coordinates is available on my page, the file is about 2.9 mb, so you might consider pulling the .zip for local testing:

metaroulette.com/files/31218.zip

metaroulette.com/files/31218.js

P.S. the php I use to create the spans:

<?php

                //$arbitrary_characters = array('a','b','c','ddsfsdfsdf','e','f','g','h','isdfsdffd','j','k','l','mfdgcvbcvbs','n','o','p','q','r','s','t','uasdfsdf','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9',);
                $arbitrary_characters = array('cat','table','cool','deloitte','askcue','what','more','less','adjective','nice','clinton','mars','jupiter','testversion','beta','hilarious','lolcatz','funny','obama','president','nice','what','misplaced','category','people','religion','global','skyscraper','new york','dubai','helsinki','volcano','iceland','peter','telephone','internet', 'dialer', 'cord', 'movie', 'party', 'chris', 'guitar', 'bentley', 'ford', 'ferrari', 'etc', 'de facto');


                    for ($i=0; $i<96; $i++) {

                        $arb_digits = rand (0,45);

                        $arbitrary_character = $arbitrary_characters[$arb_digits];
                        //$arbitrary_character = ".";

                        echo "<span id=\"wp_$i\" style=\"position:fixed; top:0px; left:0px; z-index:1; font-size:20px; cursor:pointer;cursor:hand;\" onmouseover=\"magnify_world_pixel('wp_$i');\" onmouseout=\"shrink_world_pixel('wp_$i');\" onClick=\"set_askcue_bar('', '$arbitrary_character')\">$arbitrary_character</span>\n";

                    }

                ?>
A: 

You could always use the <canvas> element. It renders much more quickly on browsers that support it.

You'll have to use a workaround for Internet Explorer, though, until version 9 comes out. You can use ExplorerCanvas to emulate canvas support for IE. However, just know that it's very slow--possibly even slower than your algorithm. If IE support is important to you, you could ask users to install Google Chrome Frame if they want a better experience while still using the Internet Explorer browser; but other than that, there isn't much you can do to speed this kind of thing up in IE.

musicfreak
same old story in IE. Means I'll probably have to check on the serverside which browser the user has and send the appropriate script. definitely gonna give canvas a try - thnx!
johnny
A: 

Well I see one easy optimization:

for(i=0;i<this.length; i++) {

You are checking this.length each time this loop iterates. This is expensive, and unless you expect the length to change, unnecessary.

Try:

for(i=0,ii=this.length;i<ii; i++) {

instead.

I don't do much speed-crucial work in JS, so I can only offer some more tentative suggestions.

You can possibly move some of the heavy calculation to a WebWorker, but I'm not sure if your situation would benefit much from it.

Check them out here: https://developer.mozilla.org/En/Using_web_workers

Also, if you are not using any of the Array object's functions for an array, try using an object with integer "keys" instead.

I've never personally done a benchmark on object vs array lookups, but I have friends who insist that objects are much faster in some cases (even though, in theory, they should be comparable).

It's a very quick and easy mod of your code, so why not try it?

MisterMister