I'm using Tracking JS to help detect if a users eyes are open and then only playing audio if it detects less than 3 objects, face, mouth, and one eye. I have it mostly convinced and working but the event is tracking and updating the data way too frequently, resulting in a very choppy and jarring experience.
Currently, I have the tracker checking constantly with the tracker on as follows:
tracker.on('track', function(event) {
if (event.data.length > 2 ) {
document.body.classList.add("hide");
pauseAudio();
} else {
document.body.classList.remove("hide");
playAudio();
}
Is there a way to run this function less frequently or specify how many times it should run per second?
You can use some sort of throttling mechanisem, lodash has it build it.
_.throttle, gets a function & time of throttling, and returns a throttled function, so you can use it as:
const handler = function(event) {
if (event.data.length > 2 ) {
document.body.classList.add("hide");
pauseAudio();
} else {
document.body.classList.remove("hide");
playAudio();
}
};
const throttledHandler = _.throttle(handler, 100); // 100ms
tracker.on('track', throttledHandler);
Related
I have a Chrome extension in which I'm trying to jump forward or backward (based on a user command) to a specific time in the video by setting the currentTime property of the video object. Before trying to set currentTime, a variety of operations work just fine. For example:
document.getElementsByTagName("video")[1].play(); // works fine
document.getElementsByTagName("video")[1].pause(); // works fine
document.getElementsByTagName("video")[1].muted = true; // works fine
document.getElementsByTagName("video")[1].muted = false; // works fine
BUT as soon as I try to jump to a specific point in the video by doing something like this:
document.getElementsByTagName("video")[1].currentTime = 500; // doesn't work
No errors are thrown, the video pauses, and any attempted actions after this point do nothing. So the items shown above (play/pause/mute/unmute) no longer work after attempting to set currentTime. If I read the value of currentTime after setting it, it correctly displays the new time that I just set it to. Yet nothing I do will make it play, and in fact even trying to make the video play by clicking the built-in toolbar no longer works. So, apparently setting currentTime wreaks all kinds of havoc in the video player. Yet if I reload the video, all works as before as long as I don't try to set currentTime.
I can easily jump to various times (backward or forward) by sliding the slider on the toolbar, so there must be some way internally to do that. Is there some way I can discover what code does a successful time jump? Because it's a Chrome extension I can inject custom js into the executing Hulu js, but I don't know what command I would send.
Any ideas?
Okay I fiddled around with it for a little while to see how I could reproduce the click event on the player and came up with the following solution:
handleViewer = function(){
var thumbnailMarker = $('.thumbnail-marker'),
progressBarTotal = thumbnailMarker.parent(),
controlsBar = $('.controls-bar'),
videoPlayer = $('#content-video-player');
var init = function(){
thumbnailMarker = $('.thumbnail-marker');
progressBarTotal = thumbnailMarker.parent();
controlsBar = $('.controls-bar');
videoPlayer = $('#content-video-player');
},
check = function(){
if(!thumbnailMarker || !thumbnailMarker.length){
init();
}
},
show = function(){
thumbnailMarker.show();
progressBarTotal.show();
controlsBar.show();
},
hide = function(){
controlsBar.hide();
},
getProgressBarWidth = function(){
return progressBarTotal[0].offsetWidth;
};
return {
goToTime: function(time){
var seekPercentage,
duration;
check();
duration = videoPlayer[0].duration;
if(time > 0 && time < duration){
seekPercentage = time/duration;
this.jumpToPercentage(seekPercentage);
}
},
jumpToPercentage: function(percentage){
check();
if(percentage >= 1 && percentage <= 100){
percentage = percentage/100;
}
if(percentage >= 0 && percentage < 1){
show();
thumbnailMarker[0].style.left = (getProgressBarWidth()*percentage)+"px";
thumbnailMarker[0].click();
hide();
}
}
}
}();
Once that code is initialized you can do the following:
handleViewer.goToTime(500);
Alternatively
handleViewer.jumpToPercentage(50);
I've tested this in chrome on a MacBook pro. Let me know if you run into any issues.
Rather than try to find the javascript responsible for changing the time, why not try to simulate the user events that cause the time to change?
Figure out the exact sequence of mouse events that trigger the time change.
This is probably some combination of mouseover, mousedown, mouseup, and click.
Then recreate those events synthetically and dispatch them to the appropriate elements.
This is the approach taken by extensions like Stream Keys and Vimium.
The video should be ready to play before setting the currentTime.
Try adding this line before setting currentTime?
document.getElementsByTagName("video")[1].play();
document.getElementsByTagName("video")[1].currentTime = 500;
Looks like it works if you first pause, then set currentTime, then play again.
document.getElementsByTagName("video")[1].pause()
document.getElementsByTagName("video")[1].currentTime = 800.000000
document.getElementsByTagName("video")[1].play()
Probably would need to hook into some event like onseeked to put in the play command to make it more robust.
I'm currently working on a game in node using socket.io and drawing onto canvas. I'm noticing that when I click during the game, character movement slows to a crawl. I would ideally like to maintain the draw rate that the game begins at and determine what's causing these memory issues. I'm looking to find out how I can debug this in a deterministic way
For context: I'm updating x,y coordinates in the game loop according to which keys are active (dictated by keyup and keydown events). I'm sending player information to the server via sockets about ~60 times a second.
To get more specific, here's a recording of the page's memory timeline:
The steep increase happens right around the time of the click event. The original jump (before the 6 extra listeners are added) is happening once the game starts. As you can see it's going a little wild on that click.
I have no handlers bound to the click event but the JS Heap grows rapidly and listeners seem to be added which confuses me. Here's a link to my repo, below is a chunk from game.js that has to do with keypresses and may be somehow involved:
var sendKey = function (activeK) {
player.xSpeed = 0;
player.ySpeed = 0;
if (activeK[KEY_W]) {
player.ySpeed = -SPEED;
}
if (activeK[KEY_A]) {
player.xSpeed = -SPEED;
}
if (activeK[KEY_S]) {
player.ySpeed = SPEED;
}
if (activeK[KEY_D]) {
player.xSpeed = SPEED;
}
if (activeK[SPACE]) {
activate(player)
}
}
document.onkeydown = function(e) {
if (activeKeys.hasOwnProperty(e.keyCode)) {
activeKeys[e.keyCode] = true;
sendKey(activeKeys)
}
}
document.onkeyup = function(e) {
if (activeKeys.hasOwnProperty(e.keyCode)) {
activeKeys[e.keyCode] = false;
sendKey(activeKeys)
}
}
Thanks!
Note: I originally started from scratch to try to learn a bunch but will likely switch to a game making framework.
I played a bit with the javascript function called requestAnimationFrame(), and made this stroke by stroke kanji painter. It works well... the code is clean enough, considered my newbie-ness. I followed a nice tutorial here and landed on a structure like this :
function here_a_closure() {
var some_state = 0;
var last_frame = false;
function machinery() {
// do mysterious stuff
return last_frame;
}
function frame_display() {
handle = window.requestAnimationFrame(frame_display);
if (machinery()) {
window.cancelAnimationFrame(handle);
}
// do the display
}
frame_display();
}
However, I would like to extend this, and paint some more kanjis next to the first one. Should I wait for the end of the first animation to launch the next one ? I would prefer (more modularity and re-use), but how ? Or should I make the machinery more complex to animate more than one character in the same animation ?
Use a single requestAnimationFrame to drive all your animations.
If you have multiple animations that run with different timings then you can make use of the elapsed time that's automatically fed into each call.
function animate(time){
requestAnimationFrame(animate);
}
This way you have the flexibility of running each of your animations either consecutively or serially without increasing the complexity of your 'machinery'.
if(time>doSomethingOnAnimation1whenThisTimeElapses){ ... }
if(time>doSomethingOnAnimation2whenThisTimeElapses){ ... }
if(time>doSomethingOnAnimation3whenThisTimeElapses){ ... }
Here's a more detailed code about using requestAnimationFrame's timer on a previous post:
How to control animation speed (requestAnimationFrame)?
There is a way to detect from javascript if the google earth plugin is crashed, so I can reload automatically the page without user action?
Thanks
I don't think you can directly assess whether the plug-in has crashed. However, you could set up an endless timer that is constantly checking something like getView()
function hasCrashed() {
try {
var view = ge.getView();
if (view) {
// plugin good
} else {
// plugin not good
}
} catch (e) {
// plugin not good
}
// a timer that checks every second
setTimeout('hasCrashed()', 1000);
}
I haven't tested that, but it should work. If not, try other options besides from getView() - perhaps getWindow() or getGlobe() options
I had a page which executes heavy javascript code after loading. To prevent the page from freezing upon loading, I spaced the execution into batches with some "no-execution" time in between (using Timeouts), and things worked well.
Lately, I've had to add additional heavy javascript code which can execute upon client actions, yet these actions can occur even before the original heavy script is done executing. This time, spacing the action won't help, since at the "downtime" of one script the other can run and vice versa, which will cause the browser to freeze.
The problem is actually more complicated as there are multiple such actions, each executing a different heavy script, and each script sort of has a different "priority" as to how fast i'd like it to finish, compared to the other ones.
My question is, what is the common practice in such situations? I tried thinking of a way to solve it, but all I could think of was quite a complex solution which would pretty much be like writing an operating system in javascript - i.e., writing a "manager" code which executes every X time (using an "interrupt"), and chooses which "context to switch to" ( = which job should run right now), etc...
This however sounds pretty complicated to me, and I was hoping there might be other solutions out there. My problem sounds like one which I'd assume many people have stumbled upon before, so even if the only solution is what I suggested, I'd assume someone already wrote it, or there is some library support for this.
Any help would be greatly appreciated. Thank you.
== EDIT ==
by "heavy code", I mean for example the DOM manipulation of a great number of elements.
You will need to think of defining your UI/Problem domain as a set of Asynchronous tasks. Here's some more insight http://alexmaccaw.com/posts/async_ui until I formulate a better answer for you.
If you don't want to block your script you can use web workers. See MDN: Using web workers for a good introduction. Note that web workers are still relative new and not supported by most browser.
However, if you want to support all browser and add some kind of priority for your "heavy scripts", you should define something yourself, e.g:
function WorkerQueue(this_argument){
this.queue = [];
this.this_argument = this_argument;
this.priority = 1;
}
WorkerQueue.prototype.enqueue = function(callback){
this.queue.push(callback);
}
WorkerQueue.prototype.dequeue = function(){
return this.queue.splice(0,1)[0];
}
function WorkerPool(){
this.pool = [];
this.status = "running";
this.timeout = null;
}
WorkerPool.prototype.addWorker = function(this_argument){
this.pool.push(new WorkerQueue(this_argument));
return this.pool[this.pool.length - 1];
}
WorkerPool.prototype.nextTask = function(){
var max_priority = 0;
var max_priority_task = this.pool.length;
for(var i = 0; i < this.pool.length; ++i){
if(this.pool[i].priority > max_priority && this.pool[i].queue.length !== 0){
max_priority = this.pool[i].priority;
max_priority_task = i;
}
}
// pool is empty or all tasks have an invalid priority
if(max_priority_task === this.pool.length)
return;
if(this.pool[max_priority_task].this_argument)
this.pool[max_priority_task].dequeue().apply(this.pool[max_priority_task].this_argument);
else
this.pool[max_priority_task].dequeue().apply();
if(this.status !== "running")
return;
this.timeout = setTimeout(function(t){return function(){t.nextTask();};}(this),1000);
}
var Workers = new WorkerPool();
var worker1 = Workers.addWorker();
worker1.enqueue(function(){
console.log("Hello");
});
worker1.enqueue(function(){
console.log("World");
});
var worker2 = Workers.addWorker();
worker2.priority = 2;
worker2.this_argument = worker2;
worker2.enqueue(function(){
console.log("Worker 2 - changing priority");
this.priority = .2;
});
worker2.enqueue(function(){
console.log("Worker 2 - after change");
});
Workers.nextTask();
Demo
In this case, every "heavy script" is a worker, which is basically a queue of tasks. You create a new worker in the pool by using addWorker and add tasks to the specific workers queue by using worker.enqueue(callback).