I've got some JavaScript to center images and <object>s on a page if they're over a threshold width. It also checks that certain classes haven't already been manually applied.
$('img,object').bind('load', function() {
w = $(this).width();
if (w > 400 && !( $(this).hasClass('inlineimage') | $(this).parent().hasClass('inlineimage') ))
$(this).css('margin', '10px ' + (parseInt((800-w)/2)-30) +'px');
});
It's horrific but the meaning behind this was all originally quite sane. The CMS doesn't make it easy to specify alignment and developing it to allow this would have taken significant time away from other jobs. A client-side hack works.
The only problem with it is that the JS waits until the whole image has loaded. Obviously this means that on slower networks, the page loads, the images start loading and some time later the images snap into position. Ugly.
But the browser seems to know the width of an image as soon as it starts to download it. I would really love to hook into this event and splat this visual bug.
Of course, if there's a CSS way of approaching this, I'm open to that too.
In browsers that support it, you can poll for natural dimensions:
var interval = setInterval( function() {
if( img.naturalWidth ) {
clearInterval(interval);
console.log( "Natural available: "+ (new Date - now );
console.log( img.naturalWidth, img.naturalHeight );
}
}, 0 );
In the demo here on uncached image I get:
Natural available: 782
62 71
Loaded: 827
So the real dimensions were available 50 milliseconds before load event. Unfortunately in IE, the readystate "loading" doesn't guarantee real dimensions.
Change the query string for the image before each test to ensure uncached.
Here's whatwg link on natural dimensions: http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content-1.html#dom-img-naturalwidth
var span = document.getElementById('span'); // The parent span
var check = function (){
if(span.offsetWidth > 0){
console.log('Width while loading', span.offsetWidth);
}
else{
setTimeout(check, 100);
}
};
check();
Demo. This should show the width in the console while it's loading first, and then the width after it's loaded. That is as long as the image isn't cached. (If the demo doesn't work for someone, try changing the hoo part of the image URL to anything else)
In the interest of this still working on more than the latest browsers, I've cobbled together a best effort brute force. It waits 500ms between attempts and checks images to see if the current run through is the same width as the last time it tried.
As soon as the width for an image is the same in two consecutive passes, we run the centring code.
This uses arrays to keep track of things so we're not constantly raping the DOM nor are we querying items that aren't applicable (because they've already been dealt with or ruled out).
attempts = 0;
arr = [];
$.each($('img,object').not('inlineimage'), function(){
arr.push([this, -2, $(this).width()]);
});
checkImgs = function() {
attempts++;
newarr = []
$.each(arr, function(){
if ($(this[0]).parent().hasClass('inlineimage'))
return;
w = $(this[0]).width();
this[1] = this[2];
this[2] = w;
if (this[1] != this[2])
return newarr.push(this);
// We know this image is loaded enough now - we can do things!
if (w >= 400)
$(this[0]).css('margin', '10px ' + (parseInt((800-w)/2)-30) +'px');
});
arr = newarr;
if (arr.length && attempts < 6)
setTimeout(checkImgs, 500);
}
setTimeout(checkImgs, 500);
It's not beautiful but it seems to work both efficiently (CPU was getting hammered by some of my earlier attempts) and quickly (cached images spring into place within 500ms).
Related
It's better to load all animations sprites in a array before start or do this that also works fine too:
var frame = new Image();
function update(){
window.requestAnimationFrame(update);
ctx.drawImage(frame, x, y, width, height)
}
window.addEventListener("keydown", key => {
if(key.keyCode == 65 || key.keyCode == 37){
for(var i = 0; i <= 3; i++){
frame.src = "./walkingspriteframes/frame"+i+".png";
};
}
})
Preloading is usually the best thing to do (at least for frequently used assets, such as animation sprites), for these reasons:
Fetching resources over a network has a latency cost associated with it. When you are doing it during a game that should be running at 30-60 frames per second and responding to user inputs quickly, it may significantly degrade the player's experience.
If you are loading images on demand, you will need to consider the possibility that the image loading may fail (because of a network failure, for example) and what should be done in such a situation. An advantage of preloading is that you can choose to not let your game start if important assets are not available.
In addition, the code you have posted will not work as you may have expected it to. It will only display frame3.png. This is because JavaScript in the browser is single-threaded: update and the keydown listener will never run concurrently, so the ctx.drawImage call in update will not see frame.src set to frame1.png or frame2.png.
I have an HTML5 audio player and I'm trying to monitor it's buffering progress until it reaches 100% so I can run it.
music = document.querySelector("#music");
progress = document.querySelector("#progress");
music.addEventListener("progress", function(){
var z = (music.buffered.end(0) / music.duration)*100;
progress.value = z;
}, false);
(Where #progress is an HTML <progress> element.)
When I attempt the above code, I get this error.
Uncaught IndexSizeError: Failed to execute 'end' on 'TimeRanges': The index provided (0) is greater than or equal to the maximum bound (0).
This error has been discussed here, but the code I am using is similar to the code used in the answer, and I am still getting the error.
Here's the weird part: I monitored music.buffered.length, and it only reached 1 when the audio was fully loaded, so for some reason I cannot monitor the buffered length of my audio element.
Here's a rather robust JS Bin to show the problem.
Has anyone else run into this problem? Am I doing something completely wrong here?
You can get buffered value in loadeddata listener:
audio.addEventListener ('loadeddata', function () {
if (typeof (this.buffered) !== 'undefined' && this.buffered.length > 0) {
audio.addEventListener ('progress', function () {
var z = (this.buffered.end (0) / this.duration) * 100;
progress.innerText = z;
});
}
});
keep in mind that it inherits this, not music!
Years down the line, I managed to determine the problem. #dandavis was correct. My server was not supplying metadata about audio files, so the browser had no clue how long an audio file was (or even how big the file was in size) until it was completely downloaded. There was no timerange because there was no information about time supplied at all.
If you're having this problem, make sure your server is supplying EVERYTHING about your audio file.
you might want to try and replace this part:
music.addEventListener("progress", function(){
var z = (music.buffered.end(0) / music.duration)*100;
progress.innerText = z;
}, false);
with this part:
if(music.onprogress){
var z = (music.buffered.end(0) / music.duration)*100;
progress.innerText = z;
};
I'm trying to make pacman move without using jquery.animation because I want more control. so I'm using setInterval, but it only works sometimes. if you refresh enough, it will eventually "click on" and work fine, but if you refresh again, it won't work, it's here http://pacman.townsendwebdd.com if you want to look at it, thank you
//earlier in the code
this.moveInterval = setInterval(_this.move, 40, _this);
move: function(_this)
{
if(_this.pause)//set to true for now
return false;
var horz = 0;
var vert = 0;
var dir = _this.dir;
//set horizontal and vertical directions
if(dir % 2 == 0)
horz = dir - 1;
else
vert = dir - 2;
_this.top += vert;
_this.left += horz;
$('#pacman').css('top', _this.top);
$('#pacman').css('left', _this.left);
},
I'm not sure if this will fix the problem, but I would recommend using request animation frame instead of setInterval.
The other thing I think would be the problem (and I've been stung by this too) is that you're possibly trying to start the animation before the page has fully loaded. Try putting your code into a function and calling it with the onload attribute of the body tag.
Good luck!
Griffork.
I'm doing some pretty processor heavy processing on a few audio files. Before I go in to this function called startProcessing I want to show a div which overlays the entire page and says calculating...Problem is the div is only shown after the function has terminated. When I click the button which activates tis code the button freezes and only when the process function has terminated does it unfreeze and show the loader. Anyone seen similar behaviour and was able to solve it?
document.getElementById('loader').innerHTML = "<p>Calculating...</p><p>Please Wait</p><img src='../img/loader.gif' />";
document.getElementById('loader').style.visibility = "visible";
document.getElementById('notification').style.visibility = "visible";
var speed = document.getElementById("playbackSpeed").value/100;
console.log("Changing speed");
if (speed > 1.5){
startProcessing(1.5);
backend.playbackSpeed = 1.5;
} else if (speed < 0.75){
startProcessing(0.75);
backend.playbackSpeed = 0.75;
} else {
startProcessing(speed);
backend.playbackSpeed = speed;
}
You could throw the heavy processing into a Web Worker. That would free up your UI.
Note: Its not IE friendly... only IE10 (I think)
Try to run heavy calculations with some delay:
setTimeout(function(){
var speed = document.getElementById("playbackSpeed").value/100;
console.log("Changing speed");
speed = Math.min(Math.max(speed, 0.75), 1.5);
startProcessing(speed);
backend.playbackSpeed = speed;
}, 13);
One approach could be to use setTimeout or setInterval for your div showing the progress bar. Another way to get around it is doing some (pseudo) multithreading.
Use Firefox 10.0.2 to open Framework.html from this project: http://code.google.com/p/unitspeeds-vhh/ (EDIT: I'm now preloading the icons. Use this revision if you'd like to try to debug the original problem.)
Hit F5 to refresh.
Hit F5 a few more times very quickly.
Expected: The unit icons (unitIconObj in the code) should always render.Actual: They never render on the first page load. First F5 usually shows all icons. Some very fast repetitions of F5 cause most most -- but not all -- icons to show.
The basic problem I'm trying to solve is icons not rendering properly the first time, and I assume this means I need to be preloading the images. I've been trying a few different methods to do this, but the behavior is not strictly reproducible, and I think I just don't know enough about refreshes and caching to figure this out myself. The icons themselves are very small image files, so I'm surprised to see that this is an issue at all.
Very sorry for the messy code and question -- I'm a noob! Any advice would be welcome.
Edit: Here's the part where I load the image file and render it:
for (var x = 0; x < sortedOutput.length; x++)
{
// Draw the unit icon
var unitIconObj = new Image();
if (sortedOutput[x].Filename == "--") // I don't have real icons for a few units
{
unitIconObj.src = "Icons/Creep.jpg";
} else
{
unitIconObj.src = "Icons/" + sortedOutput[x].Filename + ".jpg";
}
speedContext.drawImage(unitIconObj, ChartBuffer+textBlock+2+4*iconSpace, 50+(x*iconSpace), BarHeight, BarHeight);
}
Have you tried changing this so it waits to call drawImage function until the onload event on the image fires. This will ensure that the image is loaded before any of the other magic happens.
for (var x = 0; x < sortedOutput.length; x++)
{
var unitIconObj= new Image();
unitIconObj.onload = function(){
speedContext.drawImage(unitIconObj, ChartBuffer+textBlock+2+4*iconSpace, 50+(x*iconSpace), BarHeight, BarHeight);
};
if (sortedOutput[x].Filename == "--") // I don't have real icons for a few units
{
unitIconObj.src = "Icons/Creep.jpg";
} else
{
unitIconObj.src = "Icons/" + sortedOutput[x].Filename + ".jpg";
}
}
found this here: https://developer.mozilla.org/en/Canvas_tutorial/Using_images