Я давно хотел переделать свое портфолио, и одной из главных вещей, которую я хотел, была плавная прокрутка. Итак, я создал очень простой эффект плавной прокрутки без дополнительных зависимостей, но реагирующий.
В этом блоге мы будем создавать это вместе. Итак, давайте приступим к делу.
Настраивать
Выполните следующие команды, чтобы настроить реагирующее приложение.
npx create-react-app smooth-scroll
cd smooth-scroll
yarn start
Обзор
По сути, то, что мы пытаемся сделать, это просто перевести div в направлении Y с задержкой. Этот div будет содержать полное SPA (одностраничное приложение), что приведет к плавному эффекту прокрутки.
<div className="parent"> <div ref={scrollingContainer}> {/* The Complete App */} </div> </div
.parent{ position: fixed; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden; pointer-events: none; }
В этой настройке div со ссылкой scrollingContainer
будет перемещаться в направлении Y. Обратите внимание, что div с классом «родительский» имеет значение position: fixed
. Это важно, иначе дочерний элемент div просто переместится вверх, оставив пространство внизу пустым.
Делая это, мы, по сути, сообщаем браузеру, что все наше приложение представляет собой фиксированный контейнер «ширина = 100%» и «высота = 100%», без прокрутки и прочего.
Позже мы установим высоту тега ‹body› равной «scrollingContainer
div», и это позволит нам прокручивать.
При прокрутке мы переведем «scrollingContainer
div».
Не волнуйтесь, если это не имеет смысла. Надеюсь, код сделает это более понятным.
Окончательная структура файла
SmoothScroll.js
Создайте файл в src/components/SmoothScroll/SmoothScroll.js
и вставьте приведенный ниже код. Пока не беспокойтесь об импорте. Мы создадим их в ближайшее время.
import React, { useEffect, useRef } from "react";
import "./SmoothScroll.css"; import useWindowSize from "../../hooks/useWindowSize";
const SmoothScroll = ({ children }) => { // 1. const windowSize = useWindowSize();
//2. const scrollingContainerRef = useRef();
// 3. const data = { ease: 0.1, current: 0, previous: 0, rounded: 0, };
// 4. useEffect(() => { setBodyHeight(); }, [windowSize.height]);
const setBodyHeight = () => { document.body.style.height = `${ scrollingContainerRef.current.getBoundingClientRect().height }px`; };
// 5. useEffect(() => { requestAnimationFrame(() => smoothScrollingHandler()); }, []);
const smoothScrollingHandler = () => { data.current = window.scrollY; data.previous += (data.current - data.previous) * data.ease; data.rounded = Math.round(data.previous * 100) / 100;
scrollingContainerRef.current.style.transform = `translateY(-${data.previous}px)`;
// Recursive call requestAnimationFrame(() => smoothScrollingHandler()); };
return ( <div className="parent"> <div ref={scrollingContainerRef}>{children}</div> </div> ); };
export default SmoothScroll;
Давайте сломаем это.
- useWindowSize() — это пользовательский хук, который возвращает текущие значения innerWidth и innerHeight окна.
- scrollingContainerRef используется для применения свойства translateY к div на лету.
data
не является состоянием, потому что мы не хотим, чтобы наш реагирующий компонент перерисовывался каждый раз при прокрутке.- Этот useEffect запускается, только если изменяется
windowSize
(если пользователь изменяет размер браузера).setBodyHeight
делает свойство height на ‹body› равным высоте 'scrollingContainerRef
div'. После передачи «position: fixed» в «родительский div» это гарантирует, что у нас достаточно места для прокрутки всего «scrollingContainerRef
div». - Этот useEffect запускается только один раз и вызывает функцию
smoothScrolling
. ФункцияsmoothScrolling
запускается рекурсивно, изменяя свойство перевода в 'scrollingContainerRef
div' всякий раз, когда пользователь прокручивает.
Обратите внимание, что мы вызываем функцию smoothScrolling
через функцию requestAnimationFrame()
.
Метод
window.requestAnimationFrame(**)**
сообщает браузеру, что вы хотите выполнить анимацию, и запрашивает, чтобы браузер вызвал указанную функцию для обновления анимации перед следующей перерисовкой. Метод принимает обратный вызов в качестве аргумента, который должен быть вызван перед перерисовкой.
Примечание. Ваша подпрограмма обратного вызова должна снова вызвать
requestAnimationFrame()
, если вы хотите анимировать другой кадр при следующем перерисовывании.requestAnimationFrame()
— это 1 выстрел.
SmoothScrolling.css
Создайте файл в src/components/SmoothScroll/SmoothScroll.css
и вставьте приведенный ниже код.
.parent {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
pointer-events: none;
}
использоватьWindowSize.js
Создайте файл в src/hooks/useWindowSize.js и вставьте приведенный ниже код.
import { useState, useEffect } from "react";
export default function useWindowSize() { const getSize = () => { return { width: window.innerWidth, height: window.innerHeight, }; };
const [windowSize, setWindowSize] = useState(getSize);
useEffect(() => { const handleResize = () => { setWindowSize(getSize()); };
window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []);
return windowSize; }
Это довольно простой хук, который прослушивает событие окна resize
и возвращает последние innerWidth
и innerHeight
окна.
Section.js
Создайте файл src/components/Section/Section.js
и вставьте приведенный ниже код.
import React from "react";
import "./section.css";
const section = ({ flexDirection }) => { return ( <div className="section" style={{ flexDirection: flexDirection }}> <div className="left-container"> <div className="block"></div> </div>
<div className="right-container"> <div className="container"> <p> Lorem ipsum dolor sit amet consectetur adipisicing elit. In laudantium esse fugiat illum tempore sapiente soluta labore voluptas iusto deleniti ab suscipit dolores quisquam corrupti facilis, id temporibus mollitia repellat omnis tempora commodi eveniet. Incidunt, perspiciatis, adipisci laboriosam dolores quos dolor voluptate odio magnam aperiam, alias asperiores pariatur! Nisi, libero! </p> </div> </div> </div> ); };
export default section;
SmoothScrolling.css
Создайте файл src/components/Section/Section.css
и вставьте приведенный ниже код.
.section { display: flex; justify-content: space-around; width: 100%; align-items: center; height: 100vh; }
.block { width: 250px; height: 250px; padding: 60px; background-color: peachpuff; }
.container { width: 500px; }
p { font-size: 1.5rem; }
Просто реагирующий компонент, чтобы заполнить место в нашем контейнере с прокруткой.
App.js
import React from "react";
import "./App.css"; import Section from "./components/Section/Section"; import SmoothScroll from "./components/SmoothScroll/SmoothScroll";
function App() { return ( <SmoothScroll> <h2>Smooth Scrolling</h2> <Section flexDirection="row" /> <Section flexDirection="row-reverse" /> <Section flexDirection="row" /> <Section flexDirection="row-reverse" /> <Section flexDirection="row" /> <Section flexDirection="row-reverse" /> </SmoothScroll> ); }
export default App;
App.css
h2 {
text-align: center;
margin: 40px auto;
font-size: 4rem;
}
Спасибо за чтение!
Хотелось бы услышать вашу мысль!