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

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

P.S.: Просто реагируйте, никаких других зависимостей!

Репозиторий Github: Нажми на меня

Live CodeSandBox: Нажми на меня

Настраивать

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

npx create-react-app custom-cursor
cd custom-cursor
yarn start

Окончательная структура файла

использоватьMousePosition():

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

Вставьте этот код в src/hooks/useMousePosition.js

import { useEffect, useState } from "react";
export default function useMousePosition() {
  const [mousePosition, setMousePosition] = useState({ x: null, y: null });
  useEffect(() => {
    const mouseMoveHandler = (event) => {
      const { clientX, clientY } = event;
      setMousePosition({ x: clientX, y: clientY });
    };
    document.addEventListener("mousemove", mouseMoveHandler);
    return () => {
      document.removeEventListener("mousemove", mouseMoveHandler);
    };
  }, []);
  return mousePosition;
}

В двух словах, мы прослушиваем событие mousemove и вызываем функцию mouseMoveHandler при каждом движении мыши. Затем функция обновляет состояние новыми координатами, а затем наш драгоценный маленький хук возвращает эти новые координаты.

Пользовательский курсор

Вот простой курсор в виде точек и колец.

Вставьте этот код в src/components/DotRing/DotRing.js и прокрутите вниз для объяснения этого кода.

import "./DotRing.css";
import useMousePosition from "../../hooks/useMousePosition";
const DotRing = () => {
	// 1.
  const { x, y } = useMousePosition();
  return (
    <>
			{/* 2. */}
      <div
        style={{ left: `${x}px`, top: `${y}px` }}
        className="ring"
      ></div>
			{/* 3. */}
      <div
        className="dot"
        style={{ left: `${x}px`, top: `${y}px` }}
      ></div>
    </>
  );
};
export default DotRing;

Давайте разберем это:

  1. Мы вернули {x, y} из useMousePosition() и вот их используем.
  2. Это внешнее кольцо над точкой, и мы передаем координаты x и y слева и сверху этого элемента.
  3. Это точка, и мы делаем то же самое здесь, передавая left: x и top: y

DotRing.css

.ring {
  position: fixed;
  top: 0;
  left: 0;
  width: 22px;
  height: 22px;
  border: 2px solid rgba(31, 30, 30, 0.808);
  border-radius: 100%;
  transform: translate(-50%, -50%);
  -webkit-transition-duration: 100ms;
  transition-duration: 100ms;
  -webkit-transition-timing-function: ease-out;
  transition-timing-function: ease-out;
  will-change: width, height, transform, border;
  z-index: 999;
  pointer-events: none;
}
.dot {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 8px;
  height: 8px;
  background-color: black;
  border-radius: 100%;
  transform: translate(-50%, -50%);
  z-index: 999;
  pointer-events: none;
}

Здесь следует обратить внимание на свойство перехода: мы задерживаем движение кольца на 100 мс. Кстати, это все личные предпочтения.

Свойство will-change:

Свойство CSS will-change указывает браузерам, как ожидается изменение элемента. Браузеры могут настроить оптимизацию до фактического изменения элемента. Такого рода оптимизации могут повысить скорость отклика страницы за счет выполнения потенциально дорогостоящей работы до того, как она действительно потребуется.

Использование курсора

App.js

import "./App.css";
import DotRing from "./components/DotRing/DotRing";
function App() {
  return (
    <div className="App">
      <DotRing />
      <div className="container"></div>
      <div className="container" style={{ background: "peachpuff" }}></div>
    </div>
  );
}
export default App;

App.css

.container {
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}
a {
  text-decoration: none;
  color: black;
}

index.css

Добавьте это к index.css, чтобы курсор по умолчанию исчез!

* {
  cursor: none;
}

Мы закончили с красивым курсором, но здесь есть одна проблема: нет способа изменить внешний вид или поведение курсора, когда он находится на определенном элементе.

Именно этим мы и займемся в следующем разделе.

Контекст мыши

Вставьте этот код в src/context/mouse-context.js

import React, { createContext, useState } from "react";
export const MouseContext = createContext({
  cursorType: "",
  cursorChangeHandler: () => {},
});
const MouseContextProvider = (props) => {
  const [cursorType, setCursorType] = useState("");
  const cursorChangeHandler = (cursorType) => {
    setCursorType(cursorType);
  };
  return (
    <MouseContext.Provider
      value={{
        cursorType: cursorType,
        cursorChangeHandler: cursorChangeHandler,
      }}
    >
      {props.children}
    </MouseContext.Provider>
  );
};
export default MouseContextProvider;

Это очень простой контекст, в котором хранится строка cursorType и функция cursorChangeHandler для изменения этой строки.

Кстати, если вы впервые спотыкаетесь о контексте. Вот ссылка на мою статью Использование React Context API как профессионал

Большая идея

С помощью этого контекста мы пытаемся изменить cursorType, вызвав cursorChangeHandler() для событий onMouseEnter() и onMouseLeave() требуемого элемента.

Позже мы передадим это cursorType как имя класса курсору и определим для него класс в CSS нашего курсора.

Использование контекста

index.js

Вставьте код в index.js

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import MouseContextProvider from "./context/mouse-context";
ReactDOM.render(
  <React.StrictMode>
    <MouseContextProvider>
      <App />
    </MouseContextProvider>
  </React.StrictMode>,
  document.getElementById("root")
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: <https://bit.ly/CRA-vitals>
reportWebVitals();

App.js

Пас

import { useContext } from "react";
import "./App.css";
import DotRing from "./components/DotRing/DotRing";
import { MouseContext } from "./context/mouse-context";
function App() {
  const { cursorType, cursorChangeHandler } = useContext(MouseContext);
  return (
    <div className="App">
      <DotRing />
      <div className="container">
        <div
          onMouseEnter={() => cursorChangeHandler("hovered")}
          onMouseLeave={() => cursorChangeHandler("")}
        >
          <h1>Hover over me</h1>
        </div>
      </div>
      <div className="container" style={{ background: "peachpuff" }}></div>
    </div>
  );
}
export default App;

Обратите внимание на реквизиты onMouseEnter и onMouseLeave. Эти реквизиты помогают нам вызвать функцию cursorChangeHandler для изменения cursorType.

Теперь мы отредактируем файлы DotRing.js и DotRing.css, чтобы включить новые изменения.

DotRing.js

Перезапишите src/components/DotRing/DotRing.js этим кодом

import React, { useContext } from "react";
import "./DotRing.css";
import useMousePosition from "../../hooks/useMousePosition";
import { MouseContext } from "../../context/mouse-context";
const DotRing = () => {
	// 1.
  const { cursorType, cursorChangeHandler } = useContext(MouseContext);
  const { x, y } = useMousePosition();
  return (
    <>
			{/* 2. */}
      <div
        style={{ left: `${x}px`, top: `${y}px` }}
        className={"ring " + cursorType}
      ></div>
      <div
        className={"dot " + cursorType}
        style={{ left: `${x}px`, top: `${y}px` }}
      ></div>
    </>
  );
};

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

  1. Здесь мы извлекаем материал из нашего контекста.
  2. И динамически добавляя cursortype к className

DotRing.css

.ring {
  position: fixed;
  top: 0;
  left: 0;
  width: 22px;
  height: 22px;
  border: 2px solid rgba(31, 30, 30, 0.808);
  border-radius: 100%;
  transform: translate(-50%, -50%);
  -webkit-transition-duration: 100ms;
  transition-duration: 100ms;
  -webkit-transition-timing-function: ease-out;
  transition-timing-function: ease-out;
  will-change: width, height, transform, border;
  z-index: 999;
  pointer-events: none;
}
.dot {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 8px;
  height: 8px;
  background-color: black;
  border-radius: 100%;
  transform: translate(-50%, -50%);
  z-index: 999;
  pointer-events: none;
}
.ring.hovered {
  width: 50px;
  height: 50px;
  border-width: 3px;
  border-color: lightgray;
}
.dot.hovered {
  display: none;
}

Этого должно быть достаточно, чтобы вы начали. Вы можете сделать его настолько причудливым, насколько захотите, возможно, использовать ключевые кадры или движение кадра, чтобы добавить бесконечную анимацию на курсор, добавить разные cursorTypes для разных целей.

Репозиторий Github: Нажми на меня

Live CodeSandBox: Нажми на меня

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

Оставляйте свои отзывы.