Reputation: 5144
I have a situation where I want to animate the width of a 600px wide div to 0px in 1 second. I could use requestAnimationFrame()
for this. But I wouldn't really be a 100% sure if the animation will take 1 second.
It would look something like this:
let width = 600;
let animation;
function animateWidth(){
if(width <= 0){
window.cancelAnimationFrame();
}else{
width -= 10; // (600px/60fps)
}
document.getElementById('id1').setAttribute('width', width);
animation = window.requestAnimationFrame(animateWidth);
}
animation = window.requestAnimationFrame(animateWidth);
The thing is, when a device has a different fps it will affect the duration of the animation (at 30fps it will take 2 seconds and at 60 fps it will take one). I want to make sure this duration of the animation is always one second. If the fps-rate is different I would want to change the new values of the width based on the duration (so animating at 30 fps we would change the width by 20 each step(600px/30fps)).
requestAnimationFrame
? If I could get the average interval between frames or the fps that would work I think. documentation: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame
Upvotes: 1
Views: 1922
Reputation:
requestAnimationFrame
passes the time since the page loaded to the callback so for example
const startValue = 600;
const endValue = 0;
// return a value between start and end as l goes from 0 to 1
function lerp(start, end, l) {
return start + (end - start) * l;
}
const startTime = performance.now();
const durationInSeconds = 1;
const element = document.getElementById('id1');
function animateWidth(now) {
const timeSinceStartInSeconds = (now - startTime) * 0.001;
// l goes from 0 to 1 over durationInSeconds;
const l = Math.min(timeSinceStartInSeconds / durationInSeconds, 1);
element.setAttribute('width', lerp(startValue, endValue, l));
// if we haven't finished request another animation frame
if (l < 1) {
requestAnimationFrame(animateWidth);
}
}
requestAnimationFrame(animateWidth);
#id1 { background: red; }
<canvas id="id1"></canvas>
If it was me I'd probably try to make it a function
// return a value between start and end as l goes from 0 to 1
function lerp(start, end, l) {
return start + (end - start) * l;
}
function animateAttribute(element, attribute, startValue, endValue, duration) {
const startTime = performance.now();
function animate(now) {
const timeSinceStart = (now - startTime) * 0.001;
// l goes from 0 to 1 over durationInSeconds;
const l = Math.min(timeSinceStart / duration, 1);
element.setAttribute(attribute, lerp(startValue, endValue, l));
// if we haven't finished request another animation frame
if (l < 1) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
}
const element = document.getElementById('id1');
const attribute = 'width';
const start = 600;
const end= 0;
const duration = 1;
animateAttribute(element, attribute, start, end, duration);
#id1 { background: red; }
<canvas id="id1"></canvas>
Also by passing l
to any of these functions and passing the result to lerp you can change how the animation happens to match any of these styles of motion.
As for your other questions
Is there any way I can achieve this while using requestAnimationFrame? If I could get the average interval between frames or the fps that would work I think.
You can compute the interval between frames. Example:
let then = performance.now();
function animate(now) {
deltaTimeInMilliseconds = (now - then);
then = now;
...
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
Am I perhaps worrying about something that isn't really a big issue?
Only you can answer that question
What fps can I expect on different devices (mobile, pc, tablet, etc.)?
Most devices are 60fps but there are gamer monitors that run at 120fps or 240fps as well as VR (and WebVR) which generally runs at 90fps or more and you can use browsers inside VR. Whether or not you care about any of those situations is up to you.
Upvotes: 4
Reputation: 1429
Measuring time since the animation start and using it as a reference in width calculation would be a common practice here.
Using this code the animation will last one second no matter what FPS the device has.
const el = document.getElementById('id1');
const start = Date.now();
function animateWidth() {
const t = (Date.now() - start) / 1000; // 1000 - one second
if(t >= 1) {
el.setAttribute('width', 0);
}
else {
el.setAttribute('width', 600 * (1 - t));
window.requestAnimationFrame(animateWidth);
}
}
animateWidth();
Upvotes: 0