Тормозит плавный скролл js на телефоне

Мне нужно сделать плавный скролл. scroll-behavior: smooth; и window.scrollTo({ top: to, behavior: "smooth" }); - не подходит т.к. не поддерживается в Safari. Делал по такой схеме:

  1. Получаю положение элемента (где он находиться относительно всей страницы по высоте)
  2. Получаю window.pageYOffset - текущее положение вьюпорта.
  3. Вычисляю дистанцию.
  4. Через window.scrollBy скроллю вниз на дистанция/скорость
  5. Повторяю выше указанные действия если расстояние до цели больше шага прокрутки через setTimeout.

На пк всё работает нормально, скроллиться с замедлением до элемента плавно. На телефоне же (реальном, не DevTools) - лагает. Почему так и что можно сделать?

Использование библиотек крайне не желательно.

Вот код:

class jscroll {
        
            static to(item)
            {
                const stepCoefficient = 100;
                const timer = 1;
        
                let to = document.querySelector(item).getBoundingClientRect().top;
                let distance = to - window.pageYOffset + stepCoefficient;
        
                let step = 10;
                let oldStep;
        
                function scroll() {
                    oldStep = step;
                    step = distance / stepCoefficient;
        
                    window.scrollBy(0, step);
        
                    distance = to - window.pageYOffset + stepCoefficient;
        
                    if (step > 1 && step != oldStep)
                        setTimeout(scroll, timer);
                    else
                        window.scrollTo(0, to);
                }
                scroll();
            }
        }
        
        export { jscroll }

Ответы (2 шт):

Автор решения: Swartex

Попробуй вместо

window.scrollTo(0, to);

выбрать какой-то самый верхний елемент (например хедер)

и скролить к нему так:

scrollTargetEl.scrollIntoView({
  behavior: 'smooth',
  block: 'start',
});

scrollIntoView doc

у меня есть проект где такой способ работает везде.

И тогда вам не нужно будет писать костили static to(item)

→ Ссылка
Автор решения: Вася Воронцов

Проверено на мобильном устройстве.

Думаю, причиной неплавной работы может служить слишком частый вызов функции (всего 1 миллисекунда задержки перед новым выполнением). Вызывать её следует примерно в 17 раз реже (для обыкновенного экрана с частотой 60 Гц). С помощью window.requestAnimationFrame можно делать вызовы, которые "чаще всего совпадают с частотой обновления экрана" и не перегружать устройство.

В моём примере нумерация облегчает проверку плавности. Переменная offset ответственна за отступ, а speed - за скорость пролистывания.

function scrollTo(item) {
    let offset = 0;
    let speed = 20;

    let to = document.querySelector(item).getBoundingClientRect().top;
    let dy = to - window.pageYOffset + offset;
    let moreZero = (dy >= 0)? 1 : -1;
    let dist = dy * moreZero;
    let step = speed * moreZero;
    window.requestAnimationFrame((scroll = function() {
        if ((dist -= speed) > 0) {
            window.scrollBy(0, step);
            window.requestAnimationFrame(scroll);
        }
    }));
}

document.getElementsByTagName("button")[0].onclick = () => {
    scrollTo("#l");
}


// ниже - создание элементов для проверки
function createDivs() {
    for (let i = 0; i < 200; i++) {
        let e = document.createElement("div");
        e.innerHTML = i;
        document.body.appendChild(e);
    }
    let e = document.createElement("div");
    e.id = "l";
    document.body.appendChild(e);
}
createDivs();
<button>Scroll</button>

→ Ссылка