Задать вопрос
Abr_ya
@Abr_ya

Можно ли использовать setTimeout рекурсивно?

Пытаюсь написать функцию плавной прокрутки на чистом JS. При клике на кнопке происходит прокрутка к элементу "каталог". В какой-то момент получился вот такой вариант:
function scrollToStep(coord = 0, step = 20, temp = 1000) {
        if (coord > step) {
            setTimeout(function() {
                window.scrollBy(0, step);
            }, temp);
            coord -= step;
            console.log(coord);
            scrollToStep(coord, step, temp)
        } else {
            return false;
        }
    }

    buttonChoice.addEventListener('click', function() {
        let catalog = document.querySelector('.catalog');
        scrollToStep(catalog.getBoundingClientRect().top);
    });


Я ждал, что раз в секунду будет выводиться в console промежуток, на который осталось сдвинуть и происходить сдвиг на указанный шаг.
Вместо этого: все промежутки выводятся сразу, а сдвиг происходит скачком через одну секунду.
Отсюда и вопрос: можно ли использовать setTimeout рекурсивно? Если да, прошу подтолкнуть в нужном направлении! Если нет - посоветуйте другой инструмент, пожалуйста!
  • Вопрос задан
  • 225 просмотров
Подписаться 2 Простой 1 комментарий
Решения вопроса 1
sergiks
@sergiks Куратор тега JavaScript
♬♬
Перенесите следующий вызов в тело функции, которая выполнится через паузу:
function scrollToStep(coord = 0, step = 20, pause = 1000) {
  if (coord > step) {
    setTimeout(function() {
	    coord -= step;
      window.scrollBy(0, step);
	    scrollToStep(coord, step, pause)
    }, pause);
  } else {
    return false;
  }
}

buttonChoice.addEventListener('click', function() {
  let catalog = document.querySelector('.catalog');
  scrollToStep(catalog.getBoundingClientRect().top);
});
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
profesor08
@profesor08 Куратор тега JavaScript
Рекурсивно использовать можно. Но если хочется плавных анимаций, таймауты это не то, что надо использовать. Тебе нужен requestAnimationFrame, и вычисление производить там, плавность гарантируется. Меня функцию ease можно добиться разного рода поведения прокрутки, от приятных и плавных, до резиновых, прыгучих и забавных.

Примеры распространенных функций:
https://gist.github.com/gre/1650294

Демо:
https://jsfiddle.net/profesor08/h7fmcs35/

let currentScrollY = 0;
let targetScrollY = 0;
let t = 0;

document.body.addEventListener("click", () => {
	targetScrollY = Math.floor(Math.random() * getScrollheight());
  currentScrollY = getScrollTop();
  t = 0;
});

function getScrollheight() {
	return document.documentElement.scrollHeight || document.body.scrollHeight;
}

function getScrollTop() {
	return document.documentElement.scrollTop || document.body.scrollTop;
}

function setScrollTop(to) {
  document.documentElement.scrollTop = to;
	document.body.scrollTop = to;
}

function ease(t) { return (--t)*t*t+1 }
  

function animate() { 
  if (t < 1) {
    t += 0.01;
  
  	setScrollTop(
    	currentScrollY + (targetScrollY - currentScrollY) * ease(t)
    );
  }
    
  requestAnimationFrame(animate);
}
  
animate();
Ответ написан
Комментировать
Abr_ya
@Abr_ya Автор вопроса
Интересное поведение исправленной функции обнаружено в Firefox!
Он поочередно скроллит то на 20, то на 40 пикселей вниз, и, следовательно, прокручивает страницу ниже, чем надо!
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы