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;
};
Related
I'm currently in the process of creating a webapp that detects whether the user is sitting or standing. I have had great success with detecting the tilt of the device, changing a boolean variable and sending the correct data to a graph.
The following code runs perfectly in the background, updating the data every second.
var trackInterval = setInterval(function(){
if(isSitting){
addData(myPieChart, "Sitting");
} else{
addData(myPieChart, "Standing");
}
}, 1000)
My issue is that the listening function that changes the variable 'isSitting' does not continue once the browser is closed. This means that the last value of 'isSitting' gets data added to it, even though the device might be tilted otherwise.
This is the code that creates the accelerometer updates:
window.addEventListener("devicemotion", accelerometerUpdate, true);
var isSitting = true;
function accelerometerUpdate(event) {
var aX = event.accelerationIncludingGravity.x * 100 ;
var aY = event.accelerationIncludingGravity.y * 100 ;
var aZ = event.accelerationIncludingGravity.z * 100 ;
if (aY > 600 || aY < -900 ){
isSitting = false;
} else{
isSitting = true;
}
}
I have tried Chrome, Opera and Firefox on my Android device.
Any help or tips to work around this would be greatly appreciated
This sounds like regular behaviour as your javascript code, to be executed, should run in your browser.
To achieve your goal, a possible guess would be to look into a way to use this api in service workers, which is currently not possible with the devicemotion api but should be (at least with chrome) with the new Generic Sensors API described here : https://developers.google.com/web/updates/2017/09/sensors-for-the-web
I have been trying to preload images in pure Javascript following the ideas of Mark Meyer in https://www.photo-mark.com/notes/image-preloading/, which is exactly what I need.
The function I use is:
function preload(index) {
index = index || 0;
if (index < 10) {
preImage[index] = new Image();
preImage[index].onload = function() { preload(index+1) };
preImage[index].src = "img/IMG" + ("0000"+(index+1)).slice(-4)+"A.png";
};
return;
}
I want to preload images IMG0001A.png through to IMG0009A, in that order. preImage is an empty array.
My problem is that when I see the network activity in Chrome devtools, even though I simulate a slow connection and the cache is disabled, the images show a size of 0 bytes, and, of course, they are 'loaded' just too fast. Can anybody tell me what's going on? thanks in advance
Here is a screenshot of devtools:
Following is my method for adding an image using three.js -
rendererModule.addImage = function (primitive){
var self = this;
var textureLoader = new THREE.TextureLoader();
console.log("HERE 1");
textureLoader.load("image/myimage.jpg", function(map){
console.log("HERE 2");
map.minFilter = THREE.LinearFilter;
var mat = new THREE.SpriteMaterial({map:map, color:0xFFFFFF, fog:true});
var sprite = new THREE.Sprite(mat);
sprite.name = primitive.id;
self.setProperties(sprite,primitive);
sprite.position.z = -15;
var distance = camera.position.distanceTo(sprite.position);
var halfHeight = distance * Math.atan( camera.fov/2 * Math.PI / 180 );
sprite.scale.x = halfHeight * 2;
sprite.scale.y = halfHeight * 2;
scene.add(sprite);
self.renderView();
});
console.log("HERE 3");
}
and I am using the method like this -
workitems.forEach(function(item, index, array){
if(item['type'] === "planar-item" ) {
vedit.renderer.addPlane(item);
} else if(item['type']==="image-item"){
vedit.renderer.addImage(item);
}
});
Though my plane method is working fine but not the image one.....another surprise is that "HERE 1" & "HERE 3" is printing in the console, but not 2. Let me know what I am doing wrong here.
The second question has the easier answer: function(map) {...} is a callback for an asynchronous operation, so inline code before and after it is guaranteed to run first (the Javascript engine won't pick up async operations until after the current context finishes) and it's not necessarily the case that it will run at all. Since it's an "onload" in this case, it won't run if the resource is never loaded. You probably want to put an onError callback in there to see what's going on.
As for the first, I can't reliably answer that without an error message, but the most likely cause of course is just that the file doesn't exist (at that path). An easy way to test is to just create an <img src="image/myimage.jpg"/>, programatically or hard-coded, and see if it appears.
In according to other answer the problem is most likely the opening failure of the image; you can easly check in your browser developer tool in the network section.
The main causes of this issue can be:
Wrong path, if your image path is http://yoursite/imahe/myimage.jpg try to add slash in your load parameter
textureLoader.load("/image/myimage.jpg", ...
Privilege error, you have to check the privilege, the owner of the file and permission (644 can be fine)
Other problems can be caused bywrong virtualhost, .htaccess or other webserver configuration, but is hard to say what...
Notice that if you try lo load image from other domain (like http://lorempixel.com/image_output/nature-q-c-150-150-8.jpg) you have a cross-domain error and the load fail!
I am loading a large number of images into a dynamic DIV and I am using a preloader to get the images.
imageObj = new Image();
imageObj.src = imgpath + imgname;
Each of these events creates a GET that I can see and monitor in Firebug.
If I know the name and path of an image, can I watch the relevant XMLHttpRequest to see if the GET has completed?
I do not want to rely on (or use) .onload events for this process.
The pseudo would look something like this...
if (imageObj.GET = 'complete')
Has anyone had any experience of this?
EDIT 1
Thanks to the help from Bart (see below) I have changed my image preloader to store an array of the image objects...
function imagePreLoader(imgname) {
images[imgnum] = new Image();
images[imgnum].src = imgpath + imgname;// load the image
imgnum ++;
}
And then, after all my other functions have run to build the content DIVs, I used the image.complete attribute in the following...
var interval = setInterval(function () {
imgcount = imgnum - 1; // because the imgnum counter ++ after src is called.
ok = 1;
for (i=0; i<imgcount; i++) {
if (images[i].complete == false){
ok = 0;
}
}
if (ok == 1) {
clearInterval(interval);
showIndexOnLoad();
}
}, 1000);
This waits until all the images are complete and only triggers the showIndexOnLoad() function when I get the 'ok' from the interval function.
All images now appear as I wanted, all at once with no additional waits for the GETs to catch up.
Well done Bart for putting me on to the image.complete attribute.
You can watch the complete property of the image to see if the image is fully loaded or not.
Here's an example.
http://jsfiddle.net/t3esV/1/
function load (source) {
var img = new Image();
img.src = source;
console.log('Loading ' + source);
var interval = setInterval(function () {
if (img.complete) {
clearInterval(interval);
complete(img);
}
}, 400);
};
function complete(img) {
console.log('Loaded', img.src);
document.body.appendChild(img);
};
Note: This example fails to clear the interval when something goes wrong and complete is never set to true.
Update
I wrote a simple jQuery.preload plugin to take advantage of the image.complete property.
This is a very interesting problem, and I am afraid there is no actual solution to this. The load event for images is when the image is being rendered and the browser knows the width and height of it.
What you would be after would be a tag-applicable readystatechange event. Alas, only IE allows you to bind those to non-document elements, so this is not an option.
There are a bunch of plug-ins that allow you to go around it, as well. One pretty hot one is https://github.com/desandro/imagesloaded , which has the added advantage of dealing with all the browser differences very efficiently. It, however, still relies on the load event (and I am pretty sure this is the only way to start doing what you want to do).
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).