2
\$\begingroup\$

I want to create a function that splits an image into many separate blocks that when put together look like the image as a whole, often called a Rasterbator. Given the number of tiles for height and length I want and the image to Rasterbate I want the function to draw the split up image with a little padding in between.

I have created the function successfully but with a high length/height it takes a long time to create that many DOM elements sure to the nester loop, for example 100 x 100 is 10,000 divs being created and having math applied to their backgrounds.

http://jsfiddle.net/n4a7cr9z/

var img = new Image();
var width;
var height;
var length = 30;
var ylength = 20;
img.onload = function() {
width = this.width;
  height = this.height;
    for (var j = 0; j < length; j++){
    for (var i = 0; i < length; i++){
        var left = (-1 * width/length * i).toString() +"px";
        var top = (-1 * height/ylength * j).toString() +"px";
        var element = jQuery('<div/>', {
            id: i + ""+j,
            class: "splitImg",
            css: {
            "width" : Math.floor(width/length),
            "height":Math.floor(height/ylength),
          "background-position": left  + " " +  top,
          "background-image" : 'url(' + img.src + ')'
            }
            })
            element.appendTo('#wrapper');
        $("#wrapper").width(width + (length *2))
    }
    }
}
img.src = 'http://www.jqueryscript.net/images/Simplest-Responsive-jQuery-Image-Lightbox-Plugin-simple-lightbox.jpg';

I was thinking maybe creating rows or columns in batches somehow since they'll all share the same background position on either the X or Y level, like first row will all have a Y offset of 0, but I'm not sure how or if that will help.

\$\endgroup\$
  • \$\begingroup\$ You should add your elements to an array and add the array to the dom after both loops have ended. Another technique might be to draw the image on canvas and do some pixel manipulation to separate portions with white as padding, that was you can do the same visual effect by just once canvas dom node \$\endgroup\$ – juvian Oct 3 '16 at 14:23
  • \$\begingroup\$ Agree with thoughts on using canvas for this. \$\endgroup\$ – Mike Brant Oct 3 '16 at 19:09
2
\$\begingroup\$

Before you opt for a <canvas> solution you might try adapting your current code with a few go-faster tricks.

var t = Date.now(); 
var img = new Image();
var length = 30;
var ylength = 20;
img.onload = function() {
    var width = this.width,
        height = this.height,
        _length = -length,
        i, j;

    // create a <div/> with all basic characteristics, to be cloned over and over in the loops below.
    var $basicDiv = jQuery('<div/>', {
        class: 'splitImg',
        css: {
            'width': Math.floor(width/length),
            'height': Math.floor(height/ylength),
            'background-image': 'url(' + img.src + ')'
        }
    });
    // Finding a node in the DOM is slow. Do it once and assign the returned jQuery collection.
    // Also, #wrapper's width can be set here.
    var $wrapper = $('#wrapper').width(width + length * 2); 

    for (i = 0; i > _length; i--) {
        for (j = 0; j > _length; j--) {
            $basicDiv.clone().css({'background-position': `${width/length * j}px ${height/ylength * i}px`}).appendTo($wrapper);
        }
    }
    console.log(Date.now() - t);
}
img.src = 'http://www.jqueryscript.net/images/Simplest-Responsive-jQuery-Image-Lightbox-Plugin-simple-lightbox.jpg';

Updated fiddle

Playing around with the code, I found all of the following to make a difference :

  • cloning a basic div created outside the nested loops,
  • avoiding assignments (other than loop counters) in the nested loops,
  • avoiding the need to multiply by -1 (twice) by counting backwards in the for(...) expressions,
  • using a template literal to build the background-position string. But beware, this is not yet well supported in browsers.

With the modified code, execution time on my computer halved from about 1200 - 2000 ms to 600 - 1000 ms.

You may well find a <canvas> approach to be even faster. I was surprised at what could be achieved with <canvas> when I wrote this answer.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.