CreateJS' PreloadJS progress event inaccurate - javascript

I'm trying to load some assets using the PreloadJS class from the CreateJS suite, but when very first progress event fired reports e.loaded and e.progress as 0.83, the numbers then decrease over the next few event before finally going back up again.
t.assets = new createjs.LoadQueue();
t.assets.installPlugin(createjs.Sound);
t.assets.setMaxConnections(10);
t.assets.addEventListener("progress", function(e){
console.log(e.loaded);
});
t.assets.addEventListener("complete", function(){
callback();
});
t.assets.loadManifest([
{ id: 'facebook_btn', src: 'img/ingame/facebook_white_btn.png' },
{ id: 'twitter_btn', src: 'img/ingame/twitter_white_btn.png' },
{ id: 'embed_btn', src: 'img/ingame/embed_white_btn.png' },
]);
I get these results in the console
0.8333333333333334
0.7142857142857143
0.625
0.5555555555555556
0.5
0.45454545454545453
0.4984983736293216
0.5894074645384125
0.6363636363636364
0.6663361591119469
0.7572452500210378
0.8261679748749072
0.9170770657839982
0.9390634318392196
1
Is this because it's working through the manifest initally and doesn't take into account everything right away?
Is checking the the progress is going the correct way before displaying these results in a preloader a good method of making sure it's sorted itself out?

Essentially the manifest is just providing a convienient way to deal with a batch of assets without you having to loop through them yourself. Have a look at the code, it's almost the same as the loadFile method, but loops through the array of objects. So it's not doing anything fancy in terms of better calculating the progress.
http://createjs.com/Docs/PreloadJS/files/.._src_preloadjs_LoadQueue.js.html#l898
You have a couple options though to make it look more accurate.
If you're not loading much and it's only going to take a few seconds you might just want to not use the progress event. Instead you can just catch load errors or other hangups.
If you are loading lots of things and it could take some time then consider waiting a few seconds before displaying the progress. That gives the manifest time to add enough stuff that it should just be moving in a positive direction towards 1.

Related

Looping audio in midijs?

I'm trying to use MIDIjs to play audio (specifically because I'm using WAD.js, which uses it). Unfortunately I can't figure out how to loop audio, there's a frustrating lack of documentation, and I'm not finding anything in the source code. At the very least I'd like to know when the file has naturally ended to restart using MIDIjs.play(), but I'm not even seeing anything for that.
I'd appreciate it if someone pointed me towards a solution, thank you.
Correct, there's no loop function (that's documented anywhere here).
However, we can at least simulate it with MIDIjs.player_callback, which is called every 100ms with an object that looks like { time : number }. This is time in seconds elapsed. We can combine this with get_duration to detect the end of playback.
function playAutoReset(url)
{
// get_duration requires a callback as it returns nothing.
MIDIjs.get_duration(url, function (duration)
{
console.info(`Duration: ${duration}`);
// Start the initial play
MIDIjs.play(url);
// This lets us keep track of current play time in seconds
MIDIjs.player_callback = function (mes)
{
if (mes.time / duration >= 1)
{
console.info('End reached');
// This plays the audio again
MIDIjs.play(url);
}
};
});
}
Note:
This does call play each time, so it will probably re-download the source (the docs seem to imply that this is always the case). If you want something more network-efficient, you may want to look into XHRs/AJAX and creating an object URL, as the docs do specify that play takes a URL. However, I thought this would be the simplest solution, and it does show what you have to do to play the midi track again when it ends.
I had this same issue and wasn't entirely satisfied with the workaround in the other answer because of how it reloaded the file every time the url was passed into a MIDIjs function and it seemed to loop slightly too early.
So, I started digging into the MIDIjs source, which also has a map file, but I didn't bother trying to maximize/deobfuscate it. I noticed a couple mentions of loop that are set by second parameter t in function X. I wasn't sure, but thought maybe X was play, so I tried it.
MIDIjs.play(url, true);
It worked!
Since I'm working with Kotlin/JS, here's my external code:
external object MIDIjs {
fun play(url: String, loop: Boolean = definedExternally)
fun stop()
}
Call it in Kotlin exactly the same way, without the semicolon.
MIDIjs.play(url, true)

Pybossa JS - taskLoaded() function executes twice when fetching 1 task

I am currently building a custom task presenter for my PYBOSSA project. I have almost implemented it, but am stuck at the following javascript function -
pybossa.taskLoaded(function(task, deferred) {
if ( !$.isEmptyObject(task) ) {
console.log("Hello from taskLoaded");
// load image from flickr
var img = $('<img />');
img.load(function() {
// continue as soon as the image is loaded
deferred.resolve(task);
pybossaNotify("", false, "loading");
});
img.attr('src', task.info.url).css('height', 460);
img.addClass('img-thumbnail');
task.info.image = img;
console.log("Task ##"+task.id);
}
else {
deferred.resolve(task);
}
});
According to the docs -
The pybossa.taskLoaded method will be in charge of adding new items to the JSON task object and resolve the deferred object once the data has been loaded (i.e. when an image has been downloaded), so another task for the current user can be pre-loaded.
But notice my function. I have logged the task ids, the function loads. It loads 2 tasks. After logging, the console shows -
Task ##256
Task ##257
Also I have tried various other statements. They also execute twice. What I think is that if now I try to insert question of the current task, the function of the next task will also be put along with its respective image. How do I resolve this?
You are seeing double for a good reason :-) PYBOSSA preloads the next task, so the final user does thinks that the next task loads really fast (actually instantly).
While for some projects this might not be a problem, in some cases the user needs to download big images, check other APIs, etc. so it takes 2 or 3 seconds (or even more) to get everything before presenting the task to the user.
PYBOSSA.JS handles this scenario, as soon as the data has been downloaded, it requests a new task, but instead of presenting it, you have it in your window. As you are building your own template, you will have to add that data into the dom (via hidden elements) and then in the pybossa.presentTask method, you will check which task is being loaded, and show/hide the previous one.
In pybossa.saveTask, you can delete the previous DOM elements.
I hope this is now more clear. If you don't want this, you can use jQuery or Axios to request a task, save it and load the next one when you want ;-)

requestAnimationFrame called right before the end of the frame?

I've been experimenting with jank-free rendering of complex scenes on HTML5 canvas. The idea is to split rendering into multiple batches with each batch taking a maximum of, say 12 ms, so that the concurrently running animations (very cheap to execute) are not interrupted.
Conceptually, batch-rendering is implemented like this:
function draw(ctx) {
var deadline = window.performance.now() + 12; // inaccurate, but enough for the example
var i = 0;
requestAnimationFrame(function drawWithDeadline() {
for (; i < itemsToRender.length; i++) {
if (window.performance.now() >= deadline) {
requestAnimationFrame(drawWithDeadline);
return;
}
var item = itemsToDraw[i];
// Draw item
}
});
}
The complete code is in this JSFiddle: https://jsfiddle.net/fkfnjrc2/5/. The code does the following things:
On each frame, modify the CSS transform property of the canvas (which is an example of the concurrently-running fast-to-execute animation).
Once in a while, initiate re-rendering of the canvas in the batched fashion as shown above.
Unfortunately, I'm seeing horrible janks exactly at the times when canvas contents is re-rendered. What I don't seem to be able to explain is what the Chrome Developer Tools timeline looks like:
The dropped frames seem to be caused by the fact that the requestAnimationFrame is not called right at the start of the frame, but towards the end of the ideal 16ms period. If the callback started right after the previous frame completed rendering, the code would most likely complete in time.
Rendering to an off-screen canvas (https://jsfiddle.net/fkfnjrc2/6/) and then copying the complete image to the screen canvas helps a little, but the janks are still there with exactly the same characteristics (delayed execution of rAF callback).
What is wrong with that code? Or maybe something's wrong with my browser / machine? I'm seeing the same behaviour on Chrome (49.0.2623.112) on Windows 10 and Mac OS.
The issues seem to be caused by Chrome's specific requestAnimationFrame callback scheduling. I've filed a bug to track this, it contains some simpler reproduction code examples.

Phaser: how to load assets after preload?

I wonder whether it would be possible to load an asset dynamically at a given time in Phaser rather than loading everything in the preload function. The reason for this is simple: I have a game with three different levels, all of which have different background songs; and so I'd rather only load a single song at startup to reduce loading times.
Right now, my preload function looks like this:
preload: function()
{
game.load.audio('pixel_world',
['assets/music/pixel_world_lo.ogg', 'assets/music/pixel_world_lo.mp3']);
game.load.audio('second_source',
['assets/music/second_source_lo.ogg', 'assets/music/second_source_lo.mp3']);
game.load.audio('reboot_complete',
['assets/music/reboot_complete_lo.ogg', 'assets/music/reboot_complete_lo.mp3']);
game.load.image('pickup', 'assets/img/pickup.png');
}
I tried moving one of the game.load.audio() calls to the create function instead:
create: function()
{
game.load.audio('pixel_world',
['assets/music/pixel_world_lo.ogg', 'assets/music/pixel_world_lo.mp3']);
// good things follow...
}
However, the following calls fail:
this.cache.isSoundDecoded(level.song)
// Phaser.Cache.isSoundDecoded: Key "pixel_world" not found in Cache.
song = game.add.audio(level.song);
// Phaser.Cache.getSound: Key "pixel_world" not found in Cache.
Do you know how I can get this to work, or any other way to ensure that the three songs are not loaded at game startup? Thank you!
From the documentation, that big unknown for noobs like me:
audio(key, urls, autoDecode) → {Phaser.Loader}
Adds an audio file to the current load queue.
The file is not loaded immediately after calling this method. The file is added to the queue ready to be loaded when the loader starts.
So basically, game.load.audio() after preload isn't loading the song, just adding it to a queue for later. In order to load the song, I also need to invoke game.load.start():
create: function()
{
game.load.audio('pixel_world',
['assets/music/pixel_world_lo.ogg', 'assets/music/pixel_world_lo.mp3']);
game.load.start(); // THIS!
// good things follow...
}

Jasmine specs fail with Velocity.js animations an $.Velocity.mock = true

I'm attempting to run some Jasmine specs on some functions that contain animations built with Velocity.js
The method looks like this:
#changeTitle = (e, data) ->
newTitle = data.title
$pageTitle = #select("pageTitleSelector")
$pageTitle.velocity
opacity: 0
,
duration: 750
complete: -> $pageTitle.text(newTitle)
.velocity
opacity: 1
duration: 800
I'm trying to tests that the page's title changes to the new title. Here is my Jasmine spec:
describe 'submit uiTitleChange', ->
it 'changes the title', ->
#clock = sinon.useFakeTimers()
#component.trigger "uiTitleChange", {title: "New Title"}
#clock.tick(16)
expect(#component.select("pageTitleSelector")).toContainHtml "New Title"
#clock.restore()
Now, I use requirejs, and have a shim that adds $.Velocity.mock = true before any test so that animation duration goes to zero (though actually it's more like 16ms to get the next animation frame).
I've verified that $.Velocity.mock is indeed true before the animation runs, and when running the spec file alone, this works just fine, but when running my entire suite, which tests a few different animations, several of them always fail.
I've tried using setTimeout instead of sinon's fake timers, I've tried setting the clock tick to a very large number (e.g. 100000), and I've also added $.Velocity.mock right before the animations in question, and none of these fix the problem.
The failure looks like this:
Failure/Error: Expected '<div class="page-title velocity-animating">Old Title</div>' to contain html 'New Title'.
The velocity-animating class is there, and the animation's complete callback was never called.
mock must be true not only before an animation runs, but also before any animation calls are made to begin with.
also, just for doubly good measure -- although you prob already know: the velocity calls are still async with mock (since it's on the next rAF tick)
beyond that, i'm not sure. if you showed me a stripped down codepen.io pen, i could be of more use :)
p.s. i don't use jasmine, but rather qunit

Categories

Resources