I saw a post on S/O looking for a pure Javascript solution to animation; then the body started talking about CSS. 🤷🏻♂️ I decided to write a pure Javascript solution anyway that serves as a miniature example of tweening values linearly using time.
Example: https://jsfiddle.net/nqfud4j0/
It's intentionally using setInterval rather than requestAnimationFrame to demonstrate the example's use of time + controlled framerate rather than a delta or 'fast as possible.' A good solution would abstract this logic into a tweening library that combines both RAF + intervals to manage latency between frames.
Here's the commented code:
/**
* Fade an HTMLElement to a specific value using style attributes
*
* @param HTMLElement element
* @param number toValue
* @param number duration
* @return void
*/
function fadeTo(element, toValue = 0, duration = 200) {
// Store our element's current opacity
// (or default to 1 if null)
const fromValue = parseFloat(element.style.opacity) || 1;
// Mark the start time (in ms). We use this to calculate
// a ratio over time that applied to our supplied duration
// argument
const startTime = Date.now();
// Determines time (ms) between each frame. Sometimes you
// may not want a full 60 fps for performance reasons or
// aesthetic
const framerate = 1000 / 60; // 60fps
// Store reference to interval (number) so we can clear
// it later
let interval = setInterval(() => {
const currentTime = Date.now();
// This creates a normalized number between now vs when we
// started and how far into our desired duration it goes
const timeDiff = (currentTime - startTime) / duration;
// Interpolate our values using the ratio from above
const value = fromValue - (fromValue - toValue) * timeDiff;
// If our ratio is >= 1, then we're done..
// so stop processing
if (timeDiff >= 1) {
clearInterval(interval);
interval = 0;
}
// Apply visual. Style attributes are strings.
element.style.opacity = value.toString();
}, framerate);
}
// Element reference
const element = document.querySelector('div');
// Fade in and out on click
element.addEventListener('click', e => {
// Animates our element from current opacity (1.0) to 0.25 for 1000ms
fadeTo(element, 0.25, 1000);
// Waits 1s, then animates our element's opacity to 1.0 for 500ms
setTimeout(() => {
fadeTo(element, 1.0, 500);
}, 1000);
});
Top comments (1)
Thanks a bunch for the comments included. I'm still early on with my study, but being able to read through it like this is super helpful. Rock on!