I am currently doing some fun website, which requires audio cues (I assume that's the name?). I want the site to do something, when the song has been played for exactly X amount of time.
I can easily get the current time using element.currentTime, but I have no clue how to say: when element.currentTime == 5.2, runFunction() - If you know what I mean. Is there some kind of way this could be done? My current test code:
<----AUDIO WILL START PLAYING---->
http://jsfiddle.net/jfL4mcnh/
$("<audio id='audioElement'>").appendTo("body");
$("#audioElement").attr("src", "http://mp3ornot.com/songs/1B.mp3").attr("autoplay", "autoplay");
setInterval(function() {
//for some reason, $("#audioElement").currentTime won't work, so we're going old fashion
time = document.getElementById("audioElement").currentTime;
console.log(time);
}, 1000);
Also, I forgot to say this, I cannot do a setTimeout() and hit at the exact moment I want in milliseconds, because the audio can take some extra time to load, while the actual code runs exactly when it has been "seen", if you know what I mean. So no countdown. I need to be exact here.
If you need greater resolution than ontimeupdate provides, you can use a setInterval instead.
Live Demo (sound and alert box only!):
$("<audio id='audioElement'>").appendTo("body");
$("#audioElement").attr("src", "http://mp3ornot.com/songs/1B.mp3").attr("autoplay", "autoplay");
var triggered = false;
var ael = document.getElementById("audioElement");
var interval = setInterval(function(){
console.log(ael.currentTime);
if (!triggered && ael.currentTime >= 5.2) {
triggered = true;
alert("5.2 seconds reached");
}
if (ael.ended) clearInterval(interval);
}, 50);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
JSFiddle Version: http://jsfiddle.net/jfL4mcnh/15/
Well, I have done it myself.. It seems.
http://jsfiddle.net/jfL4mcnh/13/
$("#audioElement").bind("timeupdate", function() {
var currentTime = parseInt(this.currentTime, 10);
if(currentTime == 2) {
console.log("2 seconds in");
$(this).unbind("timeupdate");
}
});
You can bind timeupdate to it, then unbind it (apparently it runs the code 4 times, so I have to unbind it).
EDIT: Nope, it doesn't update fast enough to make it perfect on point. It increments each ~300ms it seems.
See this jsfiddle here
I've added the following line to the JavaScript setInterval() function:
if (time > 5.2) {
myFunction();
}
myFunction() does a console.log, which you'll see in the console.
The reason I used > rather than === is that the time reported is never precise due to fluctuations in processing. A Boolean in the condition would solve this problem:
triggered = false;
if (time > 5.2 && !triggered) {
triggered = true;
myFunction();
}
Related
What I'd like to do is control the clock ticks for a non-realtime Cesium application. Imagine there's expensive code running, plus I want to give the viewer time to load the tiles before continuing. So how do I disable automatic ticking, and then call tick() manually when my code is ready for it?
Docs for Cesium.Clock say "The clock will only tick when both Clock#canAnimate and Clock#shouldAnimate are true." but that's not what I'm getting.
What I currently see:
viewer.clock.canAnimate = false;
viewer.clock.shouldAnimate = false;
viewer.clock.onTick.addEventListener(function(clock){
console.log("Tick");
});
The result in the console shows the clock still ticking:
Tick
Tick
Tick
Tick
...
What I'd like to do:
viewer.clock.stopTicking(); // or whatever that command would be...
while (someCondition){
// run expensive code
tick(); // issue manual tick
}
Thanks for your help!
Max
It's a bit of a legacy quirk of the Cesium API that the Clock's onTick event fires for every animation frame rendered, regardless of whether the clock advances in time or not.
If you want to take control of Cesium's render loop yourself, you can do that like this:
viewer.useDefaultRenderLoop = false;
function myOwnRenderLoop() {
viewer.resize();
viewer.render();
Cesium.requestAnimationFrame(myOwnRenderLoop);
}
Cesium.requestAnimationFrame(myOwnRenderLoop);
Above, I'm using requestAnimationFrame, so the loop runs as fast as possible. But I could replace that with setTimeout to get a slower loop, emulating poor render performance. Note that interactivity and screen updates would slow down with such a method when longer time intervals are used.
viewer.useDefaultRenderLoop = false;
function myOwnRenderLoop() {
viewer.resize();
viewer.render();
window.setTimeout(myOwnRenderLoop, 500);
}
window.setTimeout(myOwnRenderLoop, 500);
So, your console.log is still printing 'Tick' because the onTick continues to fire, regardless of whether the clock is advancing. All you need to do is toggle both the canAnimate and shouldAnimate, as you suspected. So, your example code would basically be:
viewer.clock.canAnimate = false;
viewer.clock.shouldAnimate = false;
while (someCondition){
// run expensive code
// toggle someCondition so we can exit this
}
// set the animate bools back to true so the clock can advance
viewer.clock.canAnimate = true;
viewer.clock.shouldAnimate = true;
To better see this in action, try this (and maybe set the if conditional to 1000 instead of 100):
viewer.clock.canAnimate = false;
viewer.clock.shouldAnimate = false;
var s = 0;
viewer.clock.onTick.addEventListener(function(clock){
if (s < 100) {
console.log(viewer.clock.currentTime);
} else {
viewer.clock.canAnimate = true;
viewer.clock.shouldAnimate = true;
}
s++;
});
You'll see that the console.log is printing the same value for 100 (or 1000) times...this is because the currentTime isn't advancing because of the canAnimate and shouldAnimate. Once those are both toggled back to true, the currentTime will advance.
I have created a small generic 4 stage pipeline and it simulates for the clock period provided. However i wanted to pause it and resume it in the middle and am not able to do that. This is my FIDDLE and for the pause i thought of adding a flag.
if flag == 1 //keep simulating
if flag == 0 // pause simulation where it is
if flag == 0 and click resume resume simulation until its end
var flag = 1; //initial value
function pause(){
flag = 0;
}
function resume(){
flag = 1;
}
//i try to check if flag is 0 then alert pause or something but no luck
function startSim(){
document.getElementById('clockDiv').innerHTML = 'Clock Period '+clockPeriod;
if(index == 22){
clearInterval(sim);
}else if(flag == 0){
alert("Simulation Paused");
}else{
clockPeriod++;
fetch();
decode();
execute();
store();
index++;
}
}
I would appreciate tips on how to do this.
The problem is not so much your code, but rather how the default way of loading javascript in jsfiddle is working (nl, onload, so an anonymous function)
Therefor your functions pause / resume are no longer available when you click your buttons.
To change this, you can change the way the javascript is loaded inside your page, for you the easiest is:
At this time your code will partly work (alert is very annoying as it pops up at every interval), and your reset won't work because the var sim was defined locally (as #razzak mentioned in another answer)
You should change the JavaScript settings in Fiddler: set "Load Type" to no-wrap and your code will work.
Although I would suggest using clearTimeout instead of flags.
I think thats not just a fiddle issue, look at this:
function start(){
var sim = setInterval(function(){startSim()},500);
}
....
if(index == 22){
clearInterval(sim);
}else if(flag == 0){
alert("Simulation Paused");
....
you re creating sim inside the local scope of the function and then using it inside an other function... this is actually an error.
use this:
function start(){
return setInterval(function(){startSim()},500);
}
FIDDLE
If a user click pause, you setup the flag and handle it in the startSim callback. If flag == 0 you try to simulate pause, but your index counter becomes 22 at 11 secs. It will be clear very fast, before you click pause.
Here is my go: http://jsfiddle.net/j82j14r6/2/
Instead of setInterval, I am using setTimeout and having a function call itself:
function tick()
{
if(!paused)
{
// Do stuff
document.querySelector('#log').value = ticks;
ticks++;
}
setTimeout(tick, 500);
}
This allows for easy control over tickrate, pausing, ending, etc.
Please note that using setTimeout or setInterval is not exactly the most accurate way to calculate time and / or make loops like this, but in case your project is not too important - this is a ok way to do it.
As to why your buttons are not calling the corresponding functions... that is mostly a jsfiddle problem. I fixed this with addEventListener in the JS section, instead of having onclick on the HTML button itself.
<input type = "button" value = "togglepause" id = "pausebutton"/><br/>
document.querySelector('#pausebutton').addEventListener('click', togglepause);
I am trying to make a function that starts in exact intervals to keep stanble update rate. The problem is that it seems to execute in 2 channels. This is the log:
timeElapsed=141; lastDraw=1314040922291
timeElapsed=860; lastDraw=1314040923151
timeElapsed=141; lastDraw=1314040923292
timeElapsed=860; lastDraw=1314040924152
timeElapsed=141; lastDraw=1314040924293
timeElapsed=860; lastDraw=1314040925153
timeElapsed=141; lastDraw=1314040925294
timeElapsed=860; lastDraw=1314040926154
timeElapsed=141; lastDraw=1314040926295
timeElapsed=859; lastDraw=1314040927154
timeElapsed=143; lastDraw=1314040927297
timeElapsed=858; lastDraw=1314040928155
timeElapsed=143; lastDraw=1314040928298
timeElapsed=858; lastDraw=1314040929156
timeElapsed=142; lastDraw=1314040929298
First, I exectute my function using
drawTimer = setTimeout(function(){ draw() }, 1);
and the function looks like this:
var draw = function(){
if(!running)
return;
var miliseconds = getCurrentMiliseconds();
var timeElapsed = miliseconds - lastDraw;
lastDraw = miliseconds;
console.log("timeElapsed=" + timeElapsed + "; lastDraw=" + lastDraw);
onDrawListener(timeElapsed);
if(timeElapsed < timeLapse)
miliseconds = timeLapse - timeElapsed;
else
miliseconds = 1;
drawTimer = setTimeout(function(){ draw() }, miliseconds);
}
It happens in both, Chrome and Firefox. Do you know what is it caused by? And... How to fix it?
P.S. Since everyone seems to be so confused about the running variable, here it is: it's a private parent object member that indicates whether the mechanism is still running or has stopped. It's set by other functions and is just there to make sure this function doesn't continue working after stop() is called.
-- update --
timeLapse is set to 1000 (1 time per seconds) and never changed again.
onDrawListener is set to this function:
function(timeElapsed){
canvas.clear();
moveSnake();
if(snake.body[0] == food){
food = getRandomFreePosition();
++snake.body.lenght;
}
drawBackground();
drawFood();
drawSnake();
}
to explain it: canvas is kinda the engine that takes care of callbacks, key listening and also has a few functions. Other than that seems kinda self-explaining. they do nothing other than some int algorithms and drawing in the canvas.
-- Figured out --
I understood that I should calculate time spent for current function and not since the last one started. My old method worked not in 2 channels but rather in long-short-long-short-long-... delayes
first of all you dont set the running bool and also when you enter the function immediately do a on clearTimeout on drawTimer.
clearTimeout(drawTimer);
In a loop like that, you should consider to write:
if(timeElapsed >= AMOUNT_OF_TIME)
{
// run code
}
I'm working on ui tabs built using jQuery. Everything works except for one issue - I did a setInterval that runs a function that does a trigger("click") so that it goes to the next tab after 5000 miliseconds. It runs through each tab fine, the issue is that if the user manually clicks on a tab, the timer for the setInterval does not restart back at 0. For example if a user were to start on tab1 at 0 miliseconds and clicks on tab2 at 2000 miliseconds, the setInterval doesn't go back to 0, it would start at 2000 and run to 5000 miliseconds and would subsequently goto tab3. I understand why it's happening, I just wonder if there were a way to restart the setInterval timing without having to do a clearInterval() and creating an entirely new setInterval(). Any insight would be appreciated.
Update
Thanks for the replies - The reason I was trying to avoid using clearInterval was because I was having issues of how to write the code in a way where the clearInterval would stop the setInterval completely. The code is setup to track whenever a user has clicked a tab. The problem is the auto change function utilizes trigger('click'), so it runs the clearInterval function I wrote also when the tabs auto-change. It seems to run fairly fine on its own, but once the user starts clicking on tabs, the setInterval behaves unusually and switches tabs unpredictably. I suspect what is happening is that several setIntervals are running at once... Here's the code (If you haven't guessed it already, I'm pretty new at javascript/jquery). I've commented out parts so that it's functional, but it still doesn't function as I intended (from first post).
// auto change tabs
if( options.interval ) {
function timerCom() {
if( !$(".controller").hasClass('paused') ) {
var i = $(".tab-current > a").attr("rel");
//alert(i);
if( i == 3 ) {i = 0};
$container
.find('a')
.eq(i)
.trigger('click');
}
}
//$("#promo-items > li > a").click(function () {
//var timer;
//if( timer != null ) {clearInterval(timer);}
timer = setInterval(timerCom, options.interval);
//});
}
No, there is no way to restart a timer set by setInterval without clearing the timer.
You can't really alter intervals or timeouts, only clear them. That said it should be a simple thing to create a function that clears the interval, and then starts a new but identical one immediately with a fresh time value.
var intervalID;
var resetTimer = function() {
if (intervalID) { clearInterval(intervalID) };
intervalID = setInterval(function() {
console.log('doing stuff!');
}, 5000);
};
timer = setInterval(function() {
timerCom();
}, options.interval);
I know this post is well over 2 years old, but I ran into a similar problem just now, and I found a solution.
I was writing an image scroller that would automatically shift to the next image after a set amount of time, and whenever I clicked the navigation buttons, the transitions moved double-time.
Here's my solution:
Make the interval variable (timer in your case) somewhat global.
i.e. in the options section (assuming it was defined earlier, and then later assigned), add a null timer variable.
var options = {
'interval',
//Other variables
'timer',
};
Then, call clearInterval twice when you handle the click event.
$("#promo-items > li > a").click(function () {
if( options.timer != null ) {
clearInterval(options.timer);
clearInterval(options.timer);
}
options.timer = setInterval(timerCom, options.interval);
});
Worked like a charm for me.
Again, sorry if this is wayyyy too late.
EDIT: I figured out the answer to the original YUI3 question I posted here, but it led to another one and instead of starting a new thread I thought I'd just add it here. Please scroll down for the new question (it's bolded).
Original question:
I'm having some issues creating a JavaScript countdown timer inside a YUI definition, my guess is something to do with object scoping. Here's my code:
YUI({combine: true, timeout: 10000}).use("node", function (Y) {
var timer = new function(){};
Y.augment(timer, Y.EventTarget);
timer.on('timer:down', function() {
Y.log('timer down event fired', 'event');
Y.Lang.later(1000, Y, timer_trigger());
});
timer.on('timer:end', function() {
Y.log('timer end event fired', 'event');
});
var timer_from;
function startTimer(seconds){ // start a coundown from seconds to 0
timer_from = seconds;
timer_trigger();
}
function timer_display(){
var mins = Math.floor(timer_from/60);
var secs = timer_from - mins*60;
var secsDisp = secs;
if(secs<10){
secsDisp = '0' + secs;
}
Y.one('#timer').set('innerHTML', mins + ':' + secsDisp);
}
function timer_trigger(){
Y.log('timer from is: '+timer_from);
if(timer_from > 0){
timer_from--;
timer_display();
if(timer_from > 0){
timer.fire('timer:down');
}
} else {
timer.fire('timer:end');
}
}
function initializePage(){
startTimer(900);
}
});
The error I'm getting is that it doesn't wait the 1000ms like I'm asking it to to call timer_trigger() and Safari eventually asks me whether I want to stop running the code. When I do a few seconds after loading the page, the timer is already down to about 3, 4 minutes.
I've also tried using setTimeout but that also produces the same result. Can anyone help? I would really appreciate it!
EDIT:
I actually figured out a solution - this came after hours of trying tons of things, but a few more Google searches can sometimes still produce new results/answers (I found the answer on this site, actually).
So apparently my code was creating a race condition, and all I had to do to fix it is this:
setTimeout(function(){
timer_trigger();
}, 1000);
I looked up race conditions, but it's unclear to me what it means in my case, and how the seemingly trivial change to my code fixed the issue I was having. So the original question in answered, but I'd like to turn this into the question that arose from the answer.
How does threading in JavaScript work and what cause my race condition, and why did the minor change in code fix the error I had?
The problem is not a race condition. The reason the additional call to setTimeout "fixes" your code is because of a logic flaw in timer_trigger. Consider what happens in the case where timer_from is 1 when the function is called. Neither timer:down nor timer:end will be triggered.
function timer_trigger(){
Y.log('timer from is: '+timer_from);
if(timer_from > 0){ // Since timer_from is 1, the if block is entered
timer_from--; // timer_from is 0
timer_display();
if(timer_from > 0){ // This block is not entered, but it has no matching else
timer.fire('timer:down');
}
} else { // The matching if block was entered, so this is not
timer.fire('timer:end');
}
}
You added this code:
setTimeout(function(){
timer_trigger();
}, 1000);
This causes timer_trigger to be called once more with timer_from already set to 0, allowing the else block to be executed.
Also note that
Y.Lang.later(1000, Y, timer_trigger());
executes timer_trigger immediately and passes the return value to Y.Lang.later. You probably meant
Y.Lang.later(1000, Y, timer_trigger);