Display the loading progress of my resources in my canvas - javascript

I'm facing a problem with canvas. No errors is returned. I'm trying to make a loader, that load every ressources like pictures, sounds, videos, etc, before the start of application. The loader must draw the number of ressourses loaded dynamically.
But at this moment, my loader has the result to freeze the browser until it draws the total of ressources loaded.
Tell me if i'm not clear :)
This is the code :
function SimpleLoader(){
var ressources ;
var canvas;
var ctx;
this.getRessources = function(){
return ressources;
};
this.setAllRessources = function(newRessources){
ressources = newRessources;
};
this.getCanvas = function(){
return canvas;
};
this.setCanvas = function(newCanvas){
canvas = newCanvas;
};
this.getCtx = function(){
return ctx;
};
this.setCtx = function(newCtx){
ctx = newCtx;
};
};
SimpleLoader.prototype.init = function (ressources, canvas, ctx){
this.setAllRessources(ressources);
this.setCanvas(canvas);
this.setCtx(ctx);
};
SimpleLoader.prototype.draw = function (){
var that = this;
this.getCtx().clearRect(0, 0, this.getCanvas().width, this.getCanvas().height);
this.getCtx().fillStyle = "black";
this.getCtx().fillRect(0,0,this.getCanvas().width, this.getCanvas().height)
for(var i = 0; i < this.getRessources().length; i++){
var data = this.getRessources()[i];
if(data instanceof Picture){
var drawLoader = function(nbLoad){
that.getCtx().clearRect(0, 0, that.getCanvas().width, that.getCanvas().height);
that.getCtx().fillStyle = "black";
that.getCtx().fillRect(0,0, that.getCanvas().width, that.getCanvas().height);
that.getCtx().fillStyle = "white";
that.getCtx().fillText("Chargement en cours ... " + Number(nbLoad) +"/"+ Number(100), that.getCanvas().width/2, 100 );
}
data.img = new Image();
data.img.src = data.src;
data.img.onload = drawLoader(Number(i)+1); //Update loader to reflect picture loading progress
} else if(data instanceof Animation){
/* Load animation */
} else if(data instanceof Video){
/* Load video */
} else if(data instanceof Sound){
/* Load sound */
}else {
}
}
};
So with this code, all resources are loaded, but i want to display the progress of loading. Some idea of what i missed?

You are "busy-looping" in the loader so the browser doesn't get a chance to redraw/update canvas.
You can implement a setTimeout(getNext, 0) or put the draw function outside polling current status in a requestAnimationFrame loop instead. I would recommend the former in this case.
In pseudo code, this is one way to get it working:
//Global:
currentItem = 0
total = numberOfItems
//The loop:
function getNextItem() {
getItem(currentItem++);
drawProgressToCanvas();
if (currentItem < total)
setTimeout(getNextItem(), 0);
else
isReady();
}
getNextItem(); //start the loader
Adopt as needed.
The setTimeout with a value of 0 will cue up a call next time there is time available (ie. after a redraw, empty event stack etc.). isReady() here is just one way to get to the next step when everything is loaded. (If you notice any problems using 0, try to use for example 16 instead.)
Using requestAnimationFrame is a more low-level and efficient way of doing it. Not all browsers support it at the moment, but there are poly-fills that will get you around that - For this kind of usage it is not so important, but just so you are aware of this option as well (in case you didn't already).

Related

If statements aren't working in p5.js. Why is this?

Basically, I am using Google's Teachable Machine for something, and I am making a p5.js webpage to go around the data. I have some if statements for a timer because I want to only be changing it if it has been a few seconds (note that I haven't implemented that yet, so that won't work yet even if the code starts to work). I have console.log() statements to help with things, and I am using booleans to help with my if statements just in case that helps for some reason (neither does, but I know doing console.log(myBool) returns the value being true when it should be, but the if statements still don't work. Anyways, enough rambling. Here is my code:
<div>
Teachable Machine Audio Model - p5.js and ml5.js
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/addons/p5.dom.min.js"></script>
<script src="https://unpkg.com/ml5#0.4.3/dist/ml5.min.js"></script>
<script type="text/javascript">
// Global variable to store the classifier
let classifier;
// Label
let label = 'listening...';
// Teachable Machine model URL:
let soundModel = 'https://teachablemachine.withgoogle.com/models/khgwJCtEk/';
function preload() {
// Load the model
classifier = ml5.soundClassifier(soundModel + 'model.json');
}
function setup() {
createCanvas(320, 240);
// Start classifying
// The sound model will continuously listen to the microphone
classifier.classify(gotResult);
}
function draw() {
background(0);
// Draw the label in the canvas
fill(255);
textSize(32);
textAlign(CENTER, CENTER);
text(label, width / 2, height / 2);
}
let firstTime = true;
// The model recognizing a sound will trigger this event
function gotResult(error, results) {
if (error) {
console.error(error);
return;
}
// The results are in an array ordered by confidence.
console.log(results[0]);
console.log(results[0]["confidence"]);
var startTimer = results[0]["confidence"] > 0.8 && firstTime == true;
var continueTimer = results[0]["confidence"] > 0.8 && firstTime == false;
var newThing = results[0]["label"] != label;
if (startTimer == true) {
var milliseconds = now.getMilliseconds();
label = results[0].label;
firstTime = false;
console.log("First time!")
}
else if (continueTimer == true) {
if (newThing == false) {
console.log("Not first.")
var newTimeout = 1000 - milliseconds;
this.timeoutVariable = setTimeout((function(thisObj) { return function() { thisObj.update(); } })(this), newTimeout);
console.log(newTimeout);
}
else if (newThing == true) {
label = results[0].label;
firstTime = true;
}
}
}
</script>
Any idea why my if statements aren't working? If it is a silly problem, sorry, silly problems happen often for me.
When running your code, I got an error:
Uncaught (in promise) ReferenceError: now is not define
It turns out when you do:
var milliseconds = now.getMilliseconds();
in one of your if statements, you are using an undefined now. You can try adding a definition of now before that line:
var now = new Date();
var milliseconds = now.getMilliseconds();
This should fix the error, and the code should start to work (although other problems may still occur).

How to give fake GPU info to site

I need to create web browser using CefSharp.Wpf with ability to give fake data to site for example CPU cores, browser plugins, platform name etc.
There are site that can retrieve all this info: https://www.deviceinfo.me/
My quesiton is: How to hide GPU info from this site? Using javascript or CefSharp functionality
I have tried to redefine WebGLRenderingContext.getParameter method, which gives an info about GPU renderer and vendor:
var canvas = document.createElement('canvas');
var gl;
try {
gl = canvas.getContext("webgl2") || canvas.getContext("webgl") || canvas.getContext("experimental-webgl2") || canvas.getContext("experimental-webgl");
} catch (e) {
}
var oldParam = WebGLRenderingContext.prototype.getParameter;
WebGLRenderingContext.prototype.getParameter = function(parameter){
console.log("we have guests");
if(parameter == debugInfo.UNMASKED_RENDERER_WEBGL){
return "GTX 1080";
}
if(parameter == gl.getExtension("WEBGL_debug_renderer_info").UNMASKED_RENDERER_WEBGL){
return "GTX 1080";
}
if(parameter == debugInfo.UNMASKED_RENDERER_WEBGL){
return "NVidia";
}
if(parameter == gl.VERSION){
return "GTX 1080";
}
return oldParam(parameter);
};
I expected to completely redefine this method and return some fake info, but when i called gl.getParameter(param) again, it still gave me an old gpu info
If you still want Canvas2D and WebGL to still work then you can't hide since they can finger print by actually rendering.
You could disable them with
HTMLCanvasElement.prototype.getContext = function() {
return null;
};
Though the fact they don't exist is also a data point.
Otherwise your wrapper appears to have some issues.
First you really should set the function before creating the context.
Second your last line should be
oldParam.call(this, parameter);
Also you didn't show debugInfo but you can use WebGLRenderingContext instead or you can just hard code the numbers
As for http://www.deviceinfo.me you need to make sure your patch runs in all iframes and workers before any other JavaScript.
WebGLRenderingContext.prototype.getParameter = function(origFn) {
const paramMap = {};
paramMap[0x9245] = "Foo"; // UNMASKED_VENDOR_WEBGL
paramMap[0x9246] = "Bar"; // UNMASKED_RENDERER_WEBGL
paramMap[0x1F00] = "Nobody"; // VENDOR
paramMap[0x1F01] = "Jim"; // RENDERER
paramMap[0x1F02] = "Version 1.0"; // VERSION
return function(parameter) {
return paramMap[parameter] || origFn.call(this, parameter);
};
}(WebGLRenderingContext.prototype.getParameter);
// --- test
const gl = document.createElement('canvas').getContext('webgl');
const ext = gl.getExtension('WEBGL_debug_renderer_info');
show(gl, gl, [
'VENDOR',
'RENDERER',
'VERSION',
]);
if (ext) {
show(gl, ext, [
'UNMASKED_VENDOR_WEBGL',
'UNMASKED_RENDERER_WEBGL',
]);
}
function show(gl, base, params) {
for (const param of params) {
console.log(param, ':', gl.getParameter(base[param]));
}
}
There is WebGLRenderingContext and WebGL2RenderingContext

Problem streaming video using media source extensions

I seem to have a very strange problem. I am trying to play a video which is being streamed live using a web browser. For this, I am looking at the MediaSource object. I have got it in a way so that the video is taken from a server in chunks to be played. The problem is that the first chunk plays correctly then playback stops.
To make this even more strange, if I put the computer to sleep after starting streaming, then wake it up andthe video will play as expected.
Some Notes:
I am currently using chrome.
I have tried both of them ,with and without calling MediaSource's endOfStream.
var VF = 'video/webm; codecs="vp8,opus"';
var FC = 0;
alert(MediaSource.isTypeSupported(VF));
var url = window.URL || window.webkitURL;
var VSRC = new MediaSource();
var VURL = URL.createObjectURL(VSRC);
var bgi, idx = 1;
var str, rec, dat = [], datb, brl;
var sbx;
//connect the mediasource to the <video> elment first.
vid2.src = VURL;
VSRC.addEventListener("sourceopen", function () {
// alert(VSRC.readyState);
//Setup the source only once.
if (VSRC.sourceBuffers.length == 0) {
var sb = VSRC.addSourceBuffer(VF);
sb.mode = 'sequence';
sb.addEventListener("updateend", function () {
VSRC.endOfStream();
});
sbx = sb;
}
});
//This function will be called each time we get more chunks from the stream.
dataavailable = function (e) {
//video is appended to the sourcebuffer, but does not play in video element
//Unless the computer is put to sleep then awaken!?
sbx.appendBuffer(e.result);
FC += 1;
//These checks behave as expected.
len.innerHTML = "" + sbx.buffered.length + "|" + VSRC.duration;
CTS.innerHTML = FC;
};
You are making two big mistakes:
You can only call sbx.appendBuffer when sbx.updating property is false, otherwise appendBuffer will fail. So what you need to do in reality is have a queue of chunks, and add a chunk to the queue if sbx.updating is true:
if (sbx.updating || segmentsQueue.length > 0)
segmentsQueue.push(e.result);
else
sbx.appendBuffer(e.result);
Your code explicitly says to stop playing after the very first chunk:
sb.addEventListener("updateend", function () {
VSRC.endOfStream();
});
Here is what you really need to do:
sb.addEventListener("updateend", function () {
if (!sbx.updating && segmentsQueue.length > 0) {
sbx.appendBuffer(segmentsQueue.shift());
}
});

How to precache tiles with OpenLayers for date animation

I am working on animating an OpenLayers map with multiple layers over a period of time. I would like to precache ol.layer.tile tiles to have smooth transitions between the dates. Any suggestions on how to precache/preload these tiles?
You'll want to rely on your browser cache here. And it requires that your server sends proper cache headers, so the browser does not re-fetch the images with every request. With these prerequisites in mind, proceed as follows:
Call ol.source.TileImage#getTileUrlFunction on your source so you can compute the urls of the tiles you want to cache.
Call ol.source.TileImage#getTileGrid on your source so you can get the tile coordinates for the extent and zoom level you want to cache
Call ol.tilegrid.TileGrid#forEachTileCoord with a function that computes the url for each tile and sets it as src on an image object. When doing so, keep track of the number of loading tiles so you know when you're done.
Repeat the above for all dimensions, after making the respective dimension changes to your source.
In the end, your code for preloading a single dimension could look something like this:
var loadingCount = 0;
var myTileUrlFunction = mySource.getTileUrlFunction();
var myTileGrid = mySource.getTileGrid();
myTileGrid.forEachTileCoord(myExtent, myZoom, function(tileCoord) {
var url = myTileUrlFunction.call(mySource, tileCoord, ol.has.DEVICE_PIXEL_RATIO, myProjection);
var img = new Image();
img.onload = function() {
--loadingCount;
if (loadingCount == 0) {
// ready to animate
}
}
++loadingCount;
img.src = url;
}
Solution that gets around cache-preventing headers.
var i = 0;
var renderer = new ol.renderer.canvas.TileLayer(layer);
var tileSource = layer.getSource();
var datePromise = new Promise(function(resolve, reject) {
tileGrid.forEachTileCoord(extent, currentZ, function(tileCoord) {
tile = tileSource.getTile(tileCoord[0], tileCoord[1], tileCoord[2], pixelRatio, projection);
tile.load();
var loader = function(e) {
if(e.type === 'tileloadend') {
--i;
if(i === 0) {
resolve();
}
} else {
reject(new Error('No response at this URL'));
}
/* remove listeners */
this.un('tileloadend', loader);
this.un('tileloaderror', loader);
};
tileSource.on('tileloadend', loader);
tileSource.on('tileloaderror', loader);
++i;
});
});

Javascript object not setting image to an array

I am working on a HTML5 game and I have a function that should load an image and append it to an array for later displaying to a canvas. The code for the loading function is here...
function loadImage(url) {
box.images.total++;
image = new Image();
image.src = url;
image.onload = function () {
box.images.loaded++;
};
box.images[box.images.length] = image;
}
The code for the holding array is here...
var box = new Object({});
box.images = new Array([]);
And the rendering code is here...
loadImage("images/background.png");
while (box.images.loaded<box.images.total) {console.log("lol");}
box.ctx.drawImage(box.images[0], 0, 0);
My hope was that this would attempt to load the set of images and would increase the counter each time. Then when an image loads it would increase the loaded counter and then once all of the code has run the rest of the code would run. But I get an error saying "function was found that matched the signature provided" and the array appears to contain an empty element.
Also i'm in Chrome on Xubuntu 12.04
Update It turns out that the image was being put in index 1 not 0 and that was why the image wasn't loading. But it still doesn't render the image please help.
Another Update So it turns out that both total and loaded were NaN and so the while loop wasn't able to load at all. I set them to zero and the wile loop didn't terminate and it crashed my browser tab.
You need some sort of Promise or a callback function. For simplicity, I would suggest the callback. Example:
var box = {};
box.images = [];
box.images.total = box.images.loaded = 0;
function loadImage(url, callback) {
box.images.total++;
image = new Image();
image.onload = function () {
box.images.loaded++;
// We call something when the image has loaded
callback();
};
image.src = url;
box.images[box.images.length] = image;
}
function loadAllImages(urls, callback) {
// Loop through each image and load url
for (var i = 0; i < urls.length; ++i) {
var url = urls[i];
loadImage(url, function() {
// When everything loads, call callback
console.log(box.images.total, box.images.loaded);
if (box.images.loaded === box.images.total) callback();
});
}
}
window.onload = function() {
box.ctx = document.querySelector("canvas").getContext("2d");
loadAllImages([
"https://lh4.googleusercontent.com/-rNTjTbBztCY/AAAAAAAAAAI/AAAAAAAAAFM/RNwjnkgQ9eo/photo.jpg?sz=128",
"https://www.gravatar.com/avatar/20e068dd2ad8db5e1403ce6d8ac2ef99?s=128&d=identicon&r=PG&f=1"
], function() {
// Do whatever once everything has loaded
box.ctx.drawImage(box.images[0], 0, 0);
});
};
<canvas>
</canvas>

Categories