Как сделать плавную прокрутку на Javascript

Задача на сегодня - плавная прокрутка страницы (или любого компонента с полосой прокрутки) на чистом Javascript без использования дополнительных библиотек.

Разберем несколько вариантов реализации поставленной задачи.

Вариант №0 - моментальная прокрутка

По-умолчанию программная прокрутка выполняется мгновенно, и для того, чтобы было от чего отталкиваться, напишем стандартную функцию прокрутки:

function gotop() {
	document.scrollingElement.scrollTop = 0;
	// эквивалент: document.scrollingElement.scrollTo(0, 0);
}

Более детально: scrollTo(x, y), и scrollTop.

Браузер при этом просто переносит положение скроллера на указанную позицию, без использования какой-либо анимации.

Вариант №1 - наверх за 500мс

Реализуем вариант, когда необходимо прокрутить элемент с любой позиции за 500мс. То есть, не важно на какой позиции находится скроллер, прокрутка всегда будет выполняться за 500мс. Таким образом, чем меньше расстояние от текущей позиции до цели - тем медленнее будет выглядеть анимация прокрутки.

Для реализации данного варианта нам потребется стандартная javascript функция setInterval.

Алгоритм заключается в следующем:

  1. задаём время, за которое необходимо выполнить прокрутку - 500мс
  2. задаём необходимую плавность, т.е. интервал времени между сдвигами, например, 20мс
  3. вычисляем сколько понадобится сдвигов: 500 мс / 20 мс = 25 сдвигов
  4. вычисляем размер одного сдвига в пикселях, разделив расстояние прокрутки на количество сдвигов: (позиция - цель) / 25 сдвигов
function gotopPer500ms() {
	const target = 0;
	const smoothless = 20;
	const time = 500;
	const shift = (el.scrollTop - target) / (time / smoothless);
	
    const timer = setInterval(() => {
		el.scrollTop = Math.max(target, el.scrollTop - shift);
        if (target === el.scrollTop) clearInterval(timer);
    }, smoothless);
}

Вариант №2 - наверх с анимацией

Этот вариант предполагает использование функции requestAnimationFrame. Реализация аналогична предыдущему варианту, за исключением того, что за плавность прокрутки будет отвечать частота перерисовки страницы браузером.

function gotopAnimate() {
	const target = 0;
	const speed = 0.5;
	const shift = el.offsetHeight * speed / 100; 

	function _step(timestamp) {
		el.scrollTop = Math.max(target, el.scrollTop - shift);
		if (target !== el.scrollTop) window.requestAnimationFrame(_step);
	}
	window.requestAnimationFrame(_step);
}

Поддерживаемость данной функции браузерами весьма высока - 96.43% (источник)

То же самое, но через setInterval:

function gotopConstSpeed() {
	const target = 0;
	const speed = 0.5;
	const smoothless = 20;
	const shift = el.offsetHeight * speed / 100; 
	
    const timer = setInterval(() => {
		el.scrollTop = Math.max(target, el.scrollTop - shift);
        if (target === el.scrollTop) clearInterval(timer);
    }, smoothless);
}

Если сравнить реализации на requestAnimationFrame и setInterval, то в случае setInterval на низких скоростях прокрутка выглядит слегка рваной, в то время как реализация на requestAnimationFrame сохраняет плавность хода.

Вариант №3 - наверх в одну строку

Предыдущие варианты предполагают вашу собственную реализацию способа прокрутки, но если не так важна реализация, сколько сам факт плавности, то есть решение в одну строку. Этот вариант использует стандартный способ плавной прокрутки, реализованный в каждом браузере по-своему.

Вызываем функцию scrollTo и передаем объект в качестве параметра:

function gotopSmooth() {
	el.scrollTo({top: 0, behavior: 'smooth'}); // поддерживаемость браузерами 86.67%
}

Стоит учитывать, что поддержка различными браузерами scrollTop - 95.12%, а scrollTo({behavior}) - 86.67% (источник)

Сравнение вариантов на живом примере

Посмотреть на работу каждого из методов и сравнить реализации можно на отдельной странице: https://asilichenko.blogspot.com/p/blog-page.html

No comments:

Post a Comment

Why BQ28Z610 function Current() returns 0 mA

Fixing 0 mA Current Readings on the BQ28Z610 Device Custom driver for the BQ28Z610 device was connected directly via I2C. It is p...