Я хочу реализовать медленную прокрутку. Я решил создать контейнер в 10 раз больше, чем его содержимое. Затем содержимое перемещается с помощью JavaScript на основе событий прокрутки.
Пример на React, но он простой. Я отметил этот вопрос тегом JavaScript, потому что проблема, с которой я столкнулся, связана с самим JavaScript, а не с React/TypeScript/и т. д.
'use client'
import { useEffect, useRef, useState } from 'react'
const targetHeight = 100 // container's height
const rootHeight = 10000 // 10x times larger container
export default function FooBar() {
const ref = useRef<HTMLDivElement | null>(null)
useEffect(() => {
function onScroll() {
const scrollTop =
document.documentElement.scrollTop || document.body.scrollTop
const scrollHeight =
document.documentElement.scrollHeight || document.body.scrollHeight
const clientHeight =
document.documentElement.clientHeight || window.innerHeight
const scrollProgress = scrollTop / (scrollHeight - clientHeight) // from 0 to 1
const result = Math.floor((rootHeight - targetHeight) * scrollProgress)
setTop(result)
}
window.addEventListener('scroll', onScroll, { passive: true })
return () => {
window.removeEventListener('scroll', onScroll)
}
}, [])
const [top, setTop] = useState(0)
return (
<div
ref = {ref}
style = {{
height: rootHeight,
}}
>
<div
style = {{
willChange: 'transform',
transform: `translate3d(0, ${top}px, 0)`,
transition: 'width 0s',
}}
>
<div style = {{ width: 100, height: 100, background: 'white' }} />
</div>
</div>
)
}
Все не сложно. Однако при прокрутке мышкой анимация дергается. Смотрите следующую репродукцию: https://stackblitz.com/edit/stackblitz-starters-fqrzww?file=app%2Fpage.tsx
Попробуйте прокрутить пример. Я ожидаю, что белый прямоугольник будет двигаться плавно. Однако это так нервно.
Я действительно понятия не имею, в чем причина такого поведения. Вычисленные значения не являются «неровными», они являются последовательными.
Фактически, для плавности было использовано свойство CSS «переход», но вынесена функция вычисления за пределы «useEffect» и попробуйте.
На самом деле вы хотели переместить элемент, вычитая величину прокрутки страницы из величины прокрутки всей страницы, но я немного манипулировал вашим кодом и на этот раз получил процент прокрутки, вычислив величину прокрутки и изменив состояние элемента на плавающее, это хорошо.
export default function FooBar() {
const ref = useRef<HTMLDivElement | null>(null);
useEffect(() => {
function onScroll() {
const scrollTop =
window.scrollY;
const computingElement = (scrollTop * 100) / rootHeight;
setTop(computingElement);
}
window.addEventListener('scroll', onScroll);
return () => {
window.removeEventListener('scroll', onScroll);
};
}, []);
const [top, setTop] = useState(0);
return (
<div
ref = {ref}
style = {{
position: "relative",
height: rootHeight,
}}
>
<div
style = {{
position: "sticky",
top: `${top}%`,
transition: 'width 0s',
}}
>
<div style = {{ width: 100, height: 100, background: 'white', color: "black" }}>{top}</div>
</div>
</div>
);
}
Не работает, к сожалению. Пожалуйста, смотрите обновленную репродукцию.