this goes out to the most most professionals among you.
I am planing a Never-Ending-Loop of an (Javascript)-one-minute-animation.
The complete Animation ends after 59.999 Milliseconds.
-> Tween 1 starts at 0 ms
-> Tween 2 starts at 1000 ms
-> Tween 3 starts at 2000 ms
-> Tween 4 starts at 3000 ms
-> Tween 5 starts at 4000 ms
-> Tween 6 starts at 5000 ms
...loop
so far, so good.
I want, that the Loop is Synchronised on every device, on every operating system, any Browser, from anywhere arround the world exactly in Milliseconds. NO INACCURACY.
My try was: get the Seconds (to know the tween-no.) and Milliseconds (to know, when the next tween starts) of Server-Time via Ajax. And then, let the animation start at Tween(x) after x Milliseconds. But it is not synchronised. There are up to 2 Seconds difference.
How do i have to fix it? Please dont say, it is not possible, it has to be possible anywhere!
If its not possible with javascript, tell me something else.
Thanks a lot.
The most brutal would be to implement NTP but if half a second difference is enough you can implement a simplified version.
The main method is to start a communication between a time server and the client to get a) the time and b) the difference between the client time and server time. With a small serverprogram that is nothing more than a stopwatch you can do the following:
server -> client: server time (or start with client->server)
client: sets own time to server time
client -> server: client time
(you may repeat it a couple of times to get an average)
The serve can calculate the lag time now and send it to the client and the client corrects its time accordingly. You canot set the computer time of the client in Javascript, you can only set the time for the actual session, so you need it to repeat for every reload and in between, too, once in a while (not too often, I would say that once every hour is more than sufficient).
The overhead should be as small as possible, it would be a good idea to use a smaller protocol than HTTP if possible
A small and hastily knocked up example with PHP on the server side:
<?php
$client_ts = $_GET["date"];
// UTC timestamp in milliseconds
$server_ts = round(microtime(true) * 1000);
// send back that timestamp and the lag
echo $server_ts."|".($client_ts - $server_ts);
?>
The HTML
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>LAG test</title>
<style type="text/css">
table {
margin-left: 1em;
}
td,th {
border: 2px solid black;
padding: .5ex .5em .5ex .5em;
text-align: center;
}
input {
margin: 3ex 1em 0 4em;
}
p {
max-width: 40em;
}
</style>
<script type="text/javascript">
// everything is highly simplified and crude here! No checks, no balances!
// Simple AJAX. Websockets would be better, of course, but also way more complex
var ajax_request;
function get_data(){
var ret, lag, diff,client_time, server_time;
if (ajax_request.readyState === 4){
if (ajax_request.status === 200){
ret = ajax_request.responseText;
}
else{
ret = null;
}
}
else {
ret = null;
}
if(ret){
// only one check here, more are better especially
// if 'diff' is very large
client_time = Date.now();
server_time = ret.split("|");
lag = server_time[1];
diff = Math.abs(client_time - server_time[0]);
document.getElementById("ctos").textContent = lag;
document.getElementById("stoc").textContent = diff;
document.getElementById("diff").textContent = (lag - diff);
}
else {
document.getElementById("ctos").textContent = "Nope";
document.getElementById("stoc").textContent = "Nada";
document.getElementById("diff").textContent = "Rien";
}
}
function sendTo(text,uid,url){
ajax_request = new XMLHttpRequest();
ajax_request.open("GET", url + "?date=" + text + "u=" + uid, true);
ajax_request.send(null);
ajax_request.onreadystatechange = get_data;
}
function testLAG(){
// cheap pseudo-UID to avoid caching
var unifier = Math.floor(Math.random() * 0xffffffff).toString();
// date.now() returns UTC time
var client_time = Date.now();
sendTo(client_time,unifier ,"phpdate.php");
}
</script>
</head>
<body>
<h1> To test lag, press LAG-Button</h1>
<p>The difference in "Diff." is the probablity that it was the same route both ways. You need to allow for some milliseconds for the HTTP overhead but in general: the lower the better.</p>
<table id="out" caption="latencies">
<tr><th>Client to Server</th><th>Server to Client</th><th>Diff.</th></tr>
<tr><td id="ctos">n/a</td><td id="stoc">n/a</td><td id="diff">n/a</td>
</table>
<input type="button" value="LAG-Button" onclick="testLAG()" /> <- press here and press often. But only if you don't mind.
</body>
</html>
Related
I've been trying to figure out how to run an infinite loop while pausing for user click, then allow for a break out.
When the loop starts, the user is presented with an image, and must choose the identical image from one of 4 displayed. If they successfully click the match within 5 seconds, they are presented another image, and the game goes on.
If they either choose an incorrect image, or 5 seconds elapses, the game ends.
I've got all of the functionality worked out, except this pause while waiting for a click or the time to expire.
Ideally, I'd also like the time to be adjustable on each iteration. Say start at 5 seconds, then shorten the time slightly (10ms) on each loop.
I believe it must be solvable using setTimeout() or setInterval(), but just can't wrap my head around it.
Here is a minimal concept of what I'm trying to accomplish.
$('#playnow').on('click',function(){
var speed = 5000;
var speed_reduce = 10;
var game_running = true;
/* create array of images */
var imgs = ['puppy.png','kitten.png','bunny.png','goldfish.png'];
var runnow = setInterval(
function(){
//get random image from loaded theme
rand_img = imgs[Math.floor(Math.random() * imgs.length) ];
//display chosen image
$('#goal_image').html('<img src="'+theme_dir+rand_img+'" />');
// wait up to 5 seconds for user to click or time to expire
if(*clicked and matched*){
//get new random image and reset timer (less 10ms)
}
if(*time expired*){
//bail out and game ends
}
/* reduce time */
speed -= speed_reduce;
},
speed);
});
You'll want something like this I think:
var speed = 5000, // the initial time
currentimage,
timer,
gamerunning;
function newimage(){
var imgs = ['puppy.png','kitten.png','bunny.png','goldfish.png'];
currentimage=Math.floor(Math.random() * imgs.length);
$('#goal_image').html('<img src="'+theme_dir+imgs[currentimage]+'" />');
timer = setTimeout(speed, lost)
}
function answer(id){
if(!gamerunning){return}
clearTimeout(timer)
if(id==currentimage){
speed -= 10; // time decrease every time.
newimage();
}else{
lost()
}
}
function lost(){
gamerunning=0;
speed=5000;
// what to do when lost.
}
$("#puppy").on("click",function(){answer(0)}); // here #puppy is the id of the answer image, and 0 the index in the imgs array.
$("#kitten").on("click",function(){answer(1)});
$("#bunny").on("click",function(){answer(2)});
$("#fish").on("click",function(){answer(3)});
$("#gamestartbutton").on("click",function(){gamerunning=1})
One way to solve this problem is to use setTimeout() and clearTimeout() rather than setInterval. Also, you need some event for the successful button click (I've pretended you have a special "#successfulmatch" button):
var speed = 5000;
var speed_reduce = 10;
var game_running = true;
var imgs = ['puppy.png','kitten.png','bunny.png','goldfish.png'];
var myTimeout;
function runNow(speed){
rand_img = imgs[Math.floor(Math.random() * imgs.length) ];
$('#goal_image').html('<img src="'+theme_dir+rand_img+'" />');
// Keep track of the timeout so we can cancel it later if the user clicks fast enough.
myTimeout = window.setTimeout(function(){
game_running = false;
gameEnds();
},speed);
}
$('#successfulmatch').on('click',function(){
if(game_running){
// Cancel the timeout because the user was fast enough
window.clearTimeout(myTimeout);
// Give the user less time than before
runNow(speed - speed_reduce);
}
else{
// Throw an error: you forgot to hide the clickable buttons when the game ended.
}
}
$('#playnow').on('click',function(){
runNow(speed);
}
Looks like you are mixing the logic for checking "has the user clicked the image? was it correct?" with the one for checking "has time expired?"
You can listen for onclick events on the images
and set a timeout event for the game over
so the user has to cancel that timer, to cancel imminent game over, by clicking on the images
if the right image is clicked the timer is reset
if not, it's game over
you can cancel a timeout event before it runs with cancelTimeout()
see W3C here for a reference.
here is a quick prototype:
$('#playnow').on('click', function() {
var speed = 5000;
var speed_reduce = 10;
var game_running = true;
/* create array of images */
var imgs = ['puppy.png', 'kitten.png', 'bunny.png', 'goldfish.png'];
// function that ends the game if it's called
function gameover() {
alert("GAME OVER");
game_running = false;
}
// in order to use clearTimeout() you must store the timer in a global variable
// setting a timeout that will end the game if it's not cleared before
window.timer = setTimeout(gameover, speed);
// function that is called whenever the user clicks on a image
function onclickimage(event) {
if (!game_running) return;
if ( /*clicked right img*/ ) {
// get random image from loaded theme
var rand_img = imgs[Math.floor(Math.random() * imgs.length)];
// display chosen image
$('#goal_image').html('<img src="' + theme_dir + rand_img + '" />');
// delete timer, user now has one more opportunity
clearTimeout(timer);
// speed is less 10ms
speed -= speed_reduce;
// launch timer again
window.gametimer = setTimeout(loop, speed);
} else { // if click did not match correct image
gameover();
}
}
});
Well, firstly, you need to clearInterval() when they either click or fail in order to stop the current interval. Then, you can restart an interval with the new speed. The interval seems to be working for.
Every 5 seconds a new picture is displayed. So, you want an onclick event for the picture that clears the interval and starts a new one. So, you may want to use setTimeout instead of setInterval since it is only a single iteration at a time.
You could use setInterval, I suppose, but there's no real benefit to it. This way also makes it relatively easy to reduce the speed each time.
I am currently working on a battle system.
The health gets calculated every 200ms, and I'm using a Interval. It works pretty good, until I start the game - the Interval again. It doesn't stop anymore.
It is a lot of code - I have also an online live demo here http://wernersbacher.de/pro/coinerdev/
Like I said - works the first, but not the second.
So, just the main code:
var frameStop;
// Draws Startscreen
function showStartRaid(name) {
playerBTC = btc;
playerBTCs = btcs;
playerName = nick;
// Sets stats for called level
enemyBTC = dun[name]["buyer"]["btc"];
enemyBTCs = dun[name]["buyer"]["btcs"];
enemyName = dun[name]["buyer"]["label"];
enemyNum = dun[name]["meta"]["base"];
/* Reset everything in html */
}
var battle = false;
$(".raid_building").click(function() {
//Draws level
showStartRaid(name);
//Sets start BTC as fighting stats (they will decrease during battle)
fplayerBTC = playerBTC;
fenemyBTC = enemyBTC;
//Click on "Start"
$("#startRaid").click(function() {
function raiden() {
//Calculates fighting
fenemyBTC -= playerBTCs/frameMinus;
fplayerBTC -= enemyBTCs/frameMinus;
/*Draws stats and health here in html */
if(fplayerBTC >= 0 && fenemyBTC >= 0)
console.log("battle goes on")
else {
//If battle is over, stop it
clearInterval(frameStop);
}
}
//Start battle
frameStop = setInterval(raiden, frameRaid);
});
});
Thanks for any help, I'm helpless.
With your code, every time .raid_building is clicked, you hook up a new handler for clicks on #startRaid. So that means, if .raid_building is clicked twice, you'll have two handlers for clicks on #startRaid, both of which start a new interval timer. Your frameStop variable will only contain the handle of one of them; the other will continue. And of course, a third click will compound the problem (you'll have three click handlers, each of which fires up a new interval timer). And so on...
Move the code hooking click on #startRaid outside the click handler on .raid_building.
I'm trying to display a progress bar on a html page using javascript. However,
when the browser tab containing the code becomes inactive, the progress bar stops updating,
being resumed when the tab is active again.
How can I prevent the browser from stopping/pausing the execution of javascript code when the window is inactive?
Although it may be irrelevant, here is the code:
Object.progressBar = function(){
$( "#question-progress-bar" ).progressbar({
value: false,
complete: function(event, ui) { ... }
});
var seconds = 15.0,
progressbar = $("#question-progress-bar"),
progressbarValue = progressbar.find(".ui-progressbar-value");
progressbarValue.css({
"background": '#c5b100',
"opacity" : '0.8'
})
var int = setInterval(function() {
var percent = (15-seconds)/15*100;
seconds=seconds-0.1;
progressbar.progressbar( "option", {
value: Math.ceil(percent)
});
$("#question-progress-bar-seconds").html((seconds).toFixed(1)+"s");
if (seconds <= 0.1) {
clearInterval(int);
}
}, 100);
}
Instead of using setInterval and assuming a certain amount of time has passed between calls (even when it's up front, setInterval has hit or miss accuracy) use the Date object to get a time when the bar starts, and compare that to the current time at each iteration.
<html>
<head>
<script>
function go()
{
var pb = new ProgressBar(5, "targ");
}
window.onload = go;
function ProgressBar(l, t)
{
var start = Date.now();
var length = l * 1000;
var targ = document.getElementById(t);
var it = window.setInterval(interval, 10);
function interval()
{
var p = 100 * (Date.now() - start) / length;
if(p > 100)
{
p = 100;
window.clearInterval(it);
alert("DONE"); // alternatively send an AJAX request here to alert the server
}
targ.value = (Math.round(p) + "%");
}
}
</script>
</head>
<body>
<input type="text" id="targ" />
</body>
</html>
I've made an example object, here, that immediately starts a countdown when instantiated and calls an alert and kills the interval timer when done. Alternatively an AJAX call, or any other sort of call can be done upon completion.
It should be noted that this will NOT complete the call if the browser stops Javascript all together. It will, however, complete it as soon as the tab has been given focus again if enough time has passed in the interim. There is no way for a website to alter this sort of browser behavior from the scripting side.
Hope that helps!
I'm having trouble with fast forwarding a timer. It is very basic at this stadium. I have a interval that add numbers. Like this:
setInterval(function () {
//+1 second
//Format output to 00:00
//Handle minute update
}, 1000);
This works perfect. The timer is going at normal speed. What I want to do is fast forwarding this timer. I want a timer minute to take 1 real second. I have tried:
setInterval(function () {
//+1 second
//Format output to 00:00
//Handle minute update
}, 15);
That works sometimes and sometimes not. Sometimes it stops att 01:02 instead of 01:00. It may be my lack of math knowledge but I don't know. How would you do it? I am going to stop and start the timer every "timer minute" so it's important that the interval is correct.
EDIT
Here is a fiddle of how I want it to work: http://jsfiddle.net/tbleckert/pF4gs/
EDIT 2
Maybe I should just adjust the time when I stop the timer?
EDIT 3
It seems like 15 ms works most of the times. But something makes ut unreliable, I think the best way is to just adjust the time.
I think what you should be doing is storing your interval in a variable so that you can clear it, and start it again with a different delay.
var delay = 1000; // JavaScript uses milliseconds, so 1000 = 1 second
var theTimer = '';
function startTimer(){
theTimer = setInterval(function () {
// Do awesome stuff here
}, delay);
}
startTimer();
Now when you want to change the interval, or fast forward the timer, all you have to do is clear the current setInterval and define it again -
clearInterval(theTimer); // stop and clear the current timer
delay = 500; // Crank it up to twice the speed! 0.5 seconds!
startTimer(); // start a new setInterval();
Here is a simple demo
<!DOCTYPE html>
<html>
<head>
<script>
function myTimer(){
this.startTime=0;
this.intervalID=0;
this.timePassed=0;
this.multiplier=1;
this.outputElement=null;
this.start=function(){
clearInterval(this.intervalID);
this.timePassed=0;
this.outputElement=document.getElementById("output");
this.startTime=new Date();
var me = this;
this.intervalID=setInterval(function(){
me.update(me);
},100);
}
this.toTwoDigit=function(num){
if(num<10){
return "0"+num;
}
return new String(num);
}
this.toThreeDigit=function(num){
if(num<10){
return "00"+num;
}
if(num<100){
return "0"+num;
}
return new String(num);
}
this.update=function(me){
me.timePassed=me.timePassed+(100*me.multiplier);
var seconds=Math.floor(me.timePassed/1000);
var hours = Math.floor(seconds/3600);
var minutes = seconds-(hours*3600);
seconds = seconds%60;
minutes=Math.floor(minutes/60);
me.outputElement.innerHTML= me.toTwoDigit(hours)+":"
+me.toTwoDigit(minutes)+":"
+me.toTwoDigit(seconds)
+":"+me.toThreeDigit(Math.floor(me.timePassed%1000));
}
this.speedup=function(){
this.multiplier=this.multiplier*2;
}
this.slowDown=function(){
this.multiplier=this.multiplier/2;
}
this.stop=function(){
clearInterval(this.intervalID);
this.update(this);
}
}
var t = new myTimer();
</script>
</head>
<body onload="t.start();">
<input type="button" value="speed up" onclick="t.speedup()"></input>
<input type="button" value="slow down" onclick="t.slowDown()"></input>
<input type="button" value="stop" onclick="t.stop()"></input>
<input type="button" value="restart" onclick="t.start()"></input>
<input type="button" value="(re)start times 60" onclick="t.multiplier=60;t.start()"></input>
<div id="output"></div>
</body>
</html>
Ok so I'm going to answer this myself. I don't think I was clear enough. When I start the timer a timeout starts at the same time, that after one second stops the timer. This is where it goes wrong, the timer doesn't always show 01:00 when it stops.
So, the final solution is the set the seconds to 00 every time it stops, and because it all happens so fast, you wont notice.
setTimeout(function () {
clearInterval(interval);
$('.clock').html(rMin.slice(-2) + ':00');
}, 1000);
Check my updated fiddle:
http://jsfiddle.net/tbleckert/pF4gs/2/
I am currently working on porting a vb.net winforms program over to a web based version, and one of the functions in the original program has be stumped.
In the original program, every 5 minutes, a form pops up for user input. There is also a label control on the main form which counts down to the next popup. This is accomplished with a single timer control with a 1 second duration. every tick, it decrements the countdown, and when the countdown reaches 0, it pops up the form and then resets. Simple enough, but in my web app, I can't afford to be doing a postback every second, so what I am attempting is to combine a javascript countdown widget with an AJAX timer. Essentially, what should happen is that when the page loads, the countdown begins decrementing from 300 seconds, and the AJAX timer begins with a duration of 300 seconds. My idea is that when the timer ticks, it will run my function, as well as reset the countdown to 300 seconds again.
My problem, is that I am not able to reset the countdown with the code that I have, and I know that I am doing something (likely very simple) wrong, but I don't know enough Java to know what.
If I hardcode the Timer var to 300, the countdown works, and the timer ticks (fires the additional functons), but the countdown just keeps counting down (into negative numbers). How do I reset the countdown variable from code behind?
Here is the countdown function
var Timer = <%= CountDown %>;
function updateClock() {
// Update Countdown
Timer -= 1;
var TimerMin = Math.floor(Timer / 60);
var TimerSec = Timer - (TimerMin * 60);
TimerSec = (TimerSec < 10 ? "0" : "") + TimerSec;
var TimerFormat = TimerMin + ":" + TimerSec;
// Update the countdown display
document.getElementById("javaCountdown").firstChild.nodeValue = TimerFormat
}
Here is the body code
<body onload="updateClock(); setInterval('updateClock()', 1000 )">
And the Code Behind
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Countdown = 300
End Sub
PProtected Sub Timer1_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles Timer1.Tick
Countdown = 300
'Additional Functions
End Sub
This solution uses jQuery.
<script>
var intervalSecond = null;
var interval5minutes = null;
$(document).ready(function() {
// enable both intervals
enableIntervals();
// onsubmit event for your form
$("#popupForm").submit(function() {
// hide the form again
$("#popupForm").css("display", "none");
// enable intervals again.
enableIntervals();
});
});
function enableIntervals() {
// every second interval
intervalSecond = setInterval(function() {
$("#updateMeEverySecond").html(new Date());
}, 1000);
// every 5 minutes interval
interval5minutes = setInterval(function() {
// display form and shut off the interval timers
$("#popupForm").css("display", "block");
clearInterval(intervalSecond);
clearInterval(interval5minutes);
}, 5 * 60 * 1000);
}
</script>
<div id="popupForm" style="display:none;">
<form>
<input type="text" />
</form>
</div>
<label id="updateMeEverySecond">Test</label>