Я давно хотел переделать свое портфолио, и одной из главных вещей, которую я хотел, была плавная прокрутка. Итак, я создал очень простой эффект плавной прокрутки без дополнительных зависимостей, но реагирующий.

В этом блоге мы будем создавать это вместе. Итак, давайте приступим к делу.

Живая ссылка

КодПесочница

Гитхаб Репо

Настраивать

Выполните следующие команды, чтобы настроить реагирующее приложение.

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;

Давайте сломаем это.

  1. useWindowSize() — это пользовательский хук, который возвращает текущие значения innerWidth и innerHeight окна.
  2. scrollingContainerRef используется для применения свойства translateY к div на лету.
  3. data не является состоянием, потому что мы не хотим, чтобы наш реагирующий компонент перерисовывался каждый раз при прокрутке.
  4. Этот useEffect запускается, только если изменяется windowSize (если пользователь изменяет размер браузера). setBodyHeight делает свойство height на ‹body› равным высоте 'scrollingContainerRef div'. После передачи «position: fixed» в «родительский div» это гарантирует, что у нас достаточно места для прокрутки всего «scrollingContainerRef div».
  5. Этот 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;
}

Живая ссылка

КодПесочница

Гитхаб Репо

Спасибо за чтение!

Хотелось бы услышать вашу мысль!