0
votes

I would like to accurately measure the velocity of mouse movement when using requestAnimationFrame. Is this even possible? Im not sure if it is possible because the frame rate for requestAnimationFrame is not constant?

JSFiddle Demo

HTML:

<canvas id="demo" width="400" height="400"></canvas>
<p id="output">Velocity: </p>

CSS:

* {
  margin: 0px;
  cursor: none;
}

#demo {
  border: 1px solid #000;
}

JS:

var canvas = document.getElementById("demo");
var output = document.getElementById("output");
var context = canvas.getContext("2d");
var x = 0;
var y = 0;
var velocity = 0;
var radius = 20;

function draw() {
    context.fillStyle = 'red';
    context.beginPath();
    context.arc(x, y, radius, 0, 2 * Math.PI);
    context.fill();
}

document.addEventListener('mousemove', function(e) {
    x = e.clientX;
    y = e.clientY;
});

function loop() {
    context.clearRect(0, 0, 400, 400);
    draw();
  output.innerHTML = "Velocity: " + velocity;
    requestAnimationFrame(loop);
}
    requestAnimationFrame(loop);
1
You can always use Date.now() to get a current timestamp in msPhil
There is an High_precision Timestamp pased as only parameter of the rAF callback.Kaiido

1 Answers

1
votes

The idea to measure mouse movement with rAF might be wrong in the first place. rAF calls are always behind input events. If you check how often rAF is being called in comparison to number of calls of the function passed to "mousemove" event and check time difference between these calls you will see that rAF is reacting to mouse move with a small delay.

As for rAF not being fired in constant time frames - to make measurement fluent you can use time delta - just measure time from the last update call and scale measurement accordingly.

The best place to measure mouse velocity is the mouse move event it self. May I ask why you want to make that measurement with rAF?

I would ask what unit you are interested in, is it pixels per second?

Taking the above you could to it like this:
https://jsfiddle.net/mateuszfryc/m3xdc7o0/

const velocity = document.getElementById('velocity');
let x = 0;
let y = 0;

let lastTime = performance.now();
function mouseStopped() {
  velocity.innerHTML = 0;
}
let stopTimer = setTimeout(mouseStopped, 10);

document.addEventListener('mousemove', (event) => {
    clearTimeout(stopTimer);

  const e = event || window.event;
  const xNow = e.pageX || e.clientX || 0;
  const yNow = e.pageY || e.clientY || 0;

  // get distance between current and saved position
  const xDiff = xNow - x;
  const yDiff = yNow - y;
  const distance = Math.abs(Math.sqrt(xDiff * xDiff + yDiff * yDiff));

  const now = performance.now();
  const timeDelta = now - lastTime;
  const v = distance + distance / timeDelta;
  console.log(v);
  velocity.innerHTML = v;

  x = xNow;
  y = yNow;
  lastTime = now;
  stopTimer = setTimeout(mouseStopped, 10);
});
<div>
  Velocity (px/sec): <span id="velocity">0</span>
</div>

I've added timeout that clears velocity when the mouse stopped.

Also its better to use performance.now() instead of Date object. If you are targeting very old browsers check its compatibility:
https://developer.mozilla.org/en-US/docs/Web/API/Performance/now