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 slow
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?
just did CTRL+Refresh and here strange loading result:
UPDATE: I just placed a sample here:
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?
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 slow
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?
just did CTRL+Refresh and here strange loading result:
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?
Share Improve this question edited Aug 29, 2012 at 15:39 angularconsulting.au asked Aug 28, 2012 at 17:32 angularconsulting.auangularconsulting.au 28.3k14 gold badges90 silver badges105 bronze badges 22- Sorry, the picture's a little small. How big is your sprite? – canon Commented Aug 28, 2012 at 17:36
- @canon if you right click on image and press open it will open in real size(i.sstatic/WGdAr.jpg). My sprite is 83.78kb. – angularconsulting.au Commented Aug 28, 2012 at 17:43
- @SLaks i will see what i can do, it may take some time. – angularconsulting.au Commented Aug 28, 2012 at 17:44
- 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? – pimvdb Commented Aug 28, 2012 at 18:11
- 3 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.. – Thiago Custodio Commented Aug 28, 2012 at 18:37
3 Answers
Reset to default 4The 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 pletes.
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.
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
.
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("");
}