Home > Enterprise >  Making a clock that takes 2560ms to complete a revolution in JavaScript
Making a clock that takes 2560ms to complete a revolution in JavaScript

Time:04-07

I'm trying to make a clock for a Libet task - a cognitive task used by psychologists. By convention these clocks take 2560 ms to complete a revolution. Mine seems to be running quite a lot slower and I can figure out why.

I have made an example of the issue here: https://jsfiddle.net/Sumoza/re4v7Lcj/8/

On each iteration of a setInterval with a 1ms delay, I increase the angle of rotation of the hand by (Math.PI*2)/2560, i.e. 1/2560th of a circle in radians. So, incrementing one of these each ms should complete the circle in that number of ms. As you can see from the clock, this runs a fair bit slower. Can anyone shed any light as to why. Interestingly, *10 seems to look a lot closer to what I want - but that doesn't make much sense to me so I'm wary of the fix. Relevant JS from the fiddle below. Thanks for any help.

var time = 0;
var rad_tick = (Math.PI*2)/2560; //radians per tick: last number is ms per revolution

// Draw Clock
const clock = document.getElementById('clock')
const clock_ctx = clock.getContext('2d')
clock_ctx.translate(clock.width/2, clock.height/2);
radius = (clock.height/2) * 0.7;

function drawClock() {
    clock_ctx.clearRect(-clock.width/2, -clock.height/2, clock.width, clock.height);
    clock.style.backgroundColor = 'transparent'; 
    clock_ctx.beginPath();
    clock_ctx.arc(0, 0, radius, 0 , 2 * Math.PI);
    clock_ctx.stroke();
}

drawClock();

// Draw Hand
const hand = document.getElementById('hand')
const hand_ctx = hand.getContext('2d')
hand_ctx.translate(hand.width/2, hand.height/2);

function drawHand(time){
    hand_ctx.clearRect(-hand.width/2, -hand.height/2, hand.width, hand.height);
    hand_ctx.beginPath();
    hand_ctx.moveTo(0,0);
    hand_ctx.rotate(time);
    hand_ctx.lineTo(0, -radius*0.9);
    hand_ctx.stroke();
    hand_ctx.rotate(-time);
}

function drawTime(){
    time =rad_tick;//*10
    drawHand(time)
}

var intervalId = setInterval(drawTime, 1);

CodePudding user response:

Try this

var starttime = new Date().getTime();

function drawTime() {
    var elapsed = new Date().getTime() - starttime;
    var time = elapsed % 2560;
    time = time * rad_tick;
    console.log(elapsed   "->"   time);
    drawHand(time)
}

CodePudding user response:

You ask setInterval to call your callback every 1ms, but does it actually call it every 1ms?

Try this code:

let cnt = 0;
setInterval(() => cnt  , 1);
setTimeout(() => { console.log(cnt); }, 1000);

For me, it prints something between 230 and 235. So looks like the actual (average) minimum interval for me is a bit over 4ms.

CodePudding user response:

Adding on to Stig, since javascript isn't super fast when it comes to these things, it won't be super accurate. Using something like getTime() will allow proper timing. How about you call getTime in your drawTime function, so that around every millisecond, it will check the actual time and use that value.

CodePudding user response:

Base things on the current time, and don't depend on setInterval to fire at the exact time you set it to fire. If there are things in-queue at the time that setInterval fires, it will do those things in-queue first and only then run the function you've wired to setIterval (causing the delays you see --which build up more and more over time). Learn about the event loop and requestAnimationFrame.

Instead, get the time (freshly) prior to doing something, and calculate how much time has passed since the start time. Base your animations on these calculations. This, way, even if there are slight delays, your last action will always sync things to the way they're supposed to look right now.

When you do it this way, you might lose some frames (skipping things the engine didn't have time to do), but it will likely complete much closer to the expected completion time.

The bottom line is, if you tell the browser to do more than it can do in a particular amount of time, there will be delays. However, you can skip frames of an animation to make things complete much closer to the expected completion time.

  • Related