If I were to create this code from scratch, my approach would be something along these lines:
function linearEase(start, end, percent) {
return start + ((end - start) * percent);
}
function animateTo(settings) {
var elem = settings.element;
var ease = settings.ease;
var start = { left: elem.offsetLeft, top: elem.offsetTop };
var lastTime = new Date().getTime();
var timeLeft = settings.totalTime;
function update() {
var currentTime = new Date().getTime();
var elapsed = currentTime - lastTime;
timeLeft -= elapsed;
lastTime = currentTime;
var percentDone = 1 - timeLeft/settings.totalTime;
elem.style.top = ease(start.top, settings.top, percentDone) + "px" ;
elem.style.left = ease(start.left, settings.left, percentDone) + "px" ;
if(timeLeft > 0) {
setTimeout(update, 33);
}
}
update();
}
For instance, you can use this code snippet to move a div to coordinates (50,50) in the span of two seconds.
var elem = document.getElementById("animatable");
setTimeout(function() {
animateTo({
element: elem,
left: 50,
top: 50,
totalTime: 2000,
ease: linearEase
})
}, 10);
This is a commonly used pattern for handling animations like these. While there may be room for improvement in how element positions are obtained and style properties are applied, creating an ease
function can significantly simplify your workflow in the long term. I've included a basic linear easing function here, but more complex easing algorithms can adhere to the same structure.
It's important to note that timeouts and intervals may not always execute at precise times, so it's recommended to define the total transition duration and calculate the elapsed time between renders. This ensures smoother animation timing.
In cases where multiple elements require animation simultaneously, consider consolidating them into a single "render loop". Calls to animateTo
could add tasks to a worker queue, with only one main setTimeout
loop responsible for calculating elapsed time and triggering each worker. This helps prevent numerous unnecessary timeout closures from cluttering your code.
For further exploration, check out this fiddle here