CSS Sprites performance
Asked Answered
S

3

7

I have static image 500x640 sitting in folder catted by 20x20 pieces with css sprites, I am setting background-position to display each piece, I need such kind of display to be able to manipulate with each piece later.

css:

   .piece
        {
            width: 20px;
            height: 20px;
            display: inline-block;
            //display: inline;
            //zoom:1;
        }        

    .ob { background-image: url("/Images/ob.jpg");}

js:

<script id="flipTemplate" type="text/html">
    <div class="piece ob" data-bind="style: { backgroundPosition: viewModel.getLeftValue($index) + ' ' + viewModel.getTopValue($index) }, attr: {cond: Cond, id: Id }, click: viewModel.setClick ">
                </div>
</script>
<script type="text/javascript">
    viewModel = {
        flips: ko.observableArray([]),      

        setClick: function (data, e) {
            e.preventDefault();            
            //doing click
        },

        getLeftValue: function (index) {

            var position = 0;

            var currentLine = div(index(), 25);

            if (currentLine > 0)
                return '-' + (index() - (currentLine * 25)) * 20 + 'px';
            else
                return '-' + index() * 20 + 'px';
        },

        getTopValue: function (index) {

            return '-' + (div(index(), 25)) * 20 + 'px';
        }
    };    

    ko.applyBindings(viewModel);
</script>
function div(val, by){
    return (val - val % by) / by;
}

So am having some performance issues. For example in Opera and FF images loading very quickly about 1 sec, in IE about 3 sec, but in Chrome it is loading very slowenter image description here

it is taking about 17 sec to display all pieces in Chrome...

Browser doing just one request to get the image and than cutting small pieces from it, why it may take so long in Chrome?

Is there any way i can improve performance? enter image description here

just did CTRL+Refresh and here strange loading result: enter image description here

UPDATE: I just placed a sample here: http://bit.ly/TrcCdp

UPDATE: In my sample there is JSON array, it is contains 800 elements, so I just find out if I make it less, eg 600-700 elements the performance is getting better, but I need 800 elements anyway.

e.g When there is only 600 elements it is reducing the load in Chrome to about 6 sec....

So probably may be the problem somewhere at the point where knockout iterating template?

Sepaloid answered 28/8, 2012 at 17:32 Comment(22)
Sorry, the picture's a little small. How big is your sprite?Faustina
@Faustina if you right click on image and press open it will open in real size(i.sstatic.net/WGdAr.jpg). My sprite is 83.78kb.Sepaloid
@SLaks i will see what i can do, it may take some time.Sepaloid
That's odd, you get a 304 Not Modified response, yet it takes 17 seconds for it to load. Does loading the image manually (e.g. navigating to it) also take long?Hydroplane
Try clearing you cache (CTRL+Refresh), see if that helps.Taphouse
@Hydroplane not it is not, it is loading very quick when navigating, even when chrome loading file from the cache - same thing taking too long.Sepaloid
@Truth just did CTRL+Refresh - same thing...no luck.Sepaloid
How do you request the image? Is it a background image or do you load it with JavaScript?Opprobrium
@Torsten Walterbackground-image, just updated question with my cssSepaloid
I don't think the problem is related to your images. Why are you requesting images after the js? Put css and images first and later JS and see if the problem will continue..Gorrono
@Thiago Custodio in my original code css is first, so why browser request image last i have no idea. But probably browser load that image only when first div gets created?Sepaloid
How long is the "Waiting" vs "Receiving" time on your image?Inoculation
@Mike Robinson 2ms/17s i.sstatic.net/uISwD.jpgSepaloid
Try to use this tool: cssspritegenerator.netBreland
@bigb Your browser request 4 files each time. That's why you should put css first and the JS at the end of the page. developer.yahoo.com/performance/rules.htmlGorrono
@bigb I found other people with css sprite with Chrome too. Take a look and see if it helps github.com/CloudMade/Leaflet/issues/640Gorrono
Load picture after CTRL+Refresh pressed, i.sstatic.net/Njby4.jpg quite strange. May be it is just image loading issue in my Chorome?Sepaloid
@SLaks sample is here: bit.ly/TrcCdpSepaloid
Still fighting with that....if some one may take a look at my sample here bit.ly/TrcCdp to reproduce it in your Chrome browserSepaloid
Any CSS background image is only ever requested if a node uses it and is not display:none.Opprobrium
So maybe the problem is not in the loading but in layout? Have you profiled your page?Opprobrium
@Torsten Walter Looks like the problem is knockout foreach loop, it is slow with lots of elements, so I will replace it with jquery template and see what's happens.Sepaloid
S
4

The problem is not the image. The image can be fixed by placing a preload at the top, before any of the stylesheet or script tags:

<meta name="viewport" content="width=device-width">

<script type="text/javascript">
    var img = new Image();
    img.src = 'TestApp_files/obm000.jpg';
</script>

<link href="TestApp_files/jquery00.css" rel="stylesheet">
<link href="TestApp_files/jquery01.css" rel="stylesheet">
<!-- ad nauseum -->

After this, the image loads in 170ms (locally). However, the page still mucks about for another 10-15 seconds afterwards trying to decide what to do.

The root issue is that the javascript is an absolute mess. Image/file/function names are cryptic. Things in the middle of the page depend on code at the end depends on code at the beginning depends on code at the end. Controller/view/model logic is all over the map. Global variables and multi-file coupling... hokay, </soapbox>, now onto treating the symptom.

Problem 1: binding knockout before the DOM loads

Put applyBindings into a domready callback:

jQuery(function($) {
   ko.applyBindings(viewModel);
});

Problem 2: foreach is slow

The knockout foreach binding is incredibly slow with large data sets. You can try jQuery templates and move the foreach inside the template, as described in this SO question. It seems to drop the time down to about 3 seconds.

I don't really understand why this is necessary as it seems to render fine with your current foreach, it simply hangs forever while knockout does some magic in the background that, as far as I can tell, takes place after foreach completes.

Side note: is it necessary to put flips into an observable array? I assume you intend to use that later as nothing in the current code needs it. If not, take it out and it will help performance (though it won't solve this issue).

Cheers, I hope this helps.

Spermatium answered 29/8, 2012 at 17:23 Comment(0)
M
2

It’s some sort of strange rendering bug between the foreach binding and Chrome. I tried just adding a character in your template before the div and that fixed the delay (but also messed up the layout).

A good way to fix this is to use something other than foreach. My repeat binding works well here and solves the delay problem.

Here is that section of your code using repeat:

<div class="condListHolder" style="width:558px">
    <div class="cond2" title="Click to flip" data-bind="repeat: flips">
        <div class="piece obm" data-bind="
          style: { backgroundPosition: getLeftValue($index) + ' ' + getTopValue($index) },
          attr: {cond: $item().cond, id: $item().Id },
          click: setClick "></div>
    </div>
</div>

Because repeat doesn’t use an observable for $index, you’ll also need to change your getTopValue and getLeftValue functions to take out the parentheses () after index.

Middle answered 29/8, 2012 at 23:23 Comment(0)
O
1

You should also look into streamlining your code that is called by the foreach loop. I don't know how often you call the getLeftValue and getTopValue methods but they are pretty unoptimized.

  • Try limiting function calls that give you the same result, use local vars to cache since they are cheap
  • don't concatenate strings in large loops, this is way slower than using an array to join them.

I tried optimizing your two functions. You should see at least some improvement:

getLeftValue: function (index) {

    var position = 0,
        realIndex = index(),
        currentLine = div(realIndex, 25);


    if (currentLine > 0)
        return ["-", (realIndex - (currentLine * 25)) * 20, "px"].join("");
    else
        return ["-", realIndex, "px"].join("");
},

getTopValue: function (index) {

    return ["-",(div(index(), 25)) * 20,"px"].join("");
}
Opprobrium answered 30/8, 2012 at 8:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.