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 рекурсивно? Если да, прошу подтолкнуть в нужном направлении! Если нет - посоветуйте другой инструмент, пожалуйста!
  • Вопрос задан
  • 217 просмотров
Решения вопроса 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 пикселей вниз, и, следовательно, прокручивает страницу ниже, чем надо!
Ответ написан
Комментировать
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы