Во-первых в функции animate должна быть проверка на то, что анимация должна остановиться. Т.е. вызывать requestAnimationFrame больше не нужно, иначе получится бесконечный цикл.
Во-вторых у функции animate есть параметр - время. Его надо использовать, что бы узнавать время от начала анимации или от предыдущего вызова animate, что бы рассчитывать какие произвести изменения.
Условная схема такая
let tbeg;
let tlast;
function animate(t) {
tbeg ??= t;
tlast ?? = t
const dtbeg = t - tbeg; // прошло от начала анимации
const dtlast = t - tlast; // прошло от прошлой анимации
render(); // расчет и внесение изменений с учетом dtbeg и/или dtlast
tlast = t;
if (какое то условие окончания) return; // Завершить анимацию?
requestAnimationFrame(animate);
}
function render() {
// код для анимации каких ни будь элементов
}
animate(performance.now())
Примеры можно посмотреть тут
https://developer.mozilla.org/en-US/...AnimationFrame