From 042d24df4d9dacea3308154f4b4b18b2c91566cc Mon Sep 17 00:00:00 2001 From: vasya Date: Sun, 22 Mar 2026 18:19:55 +0300 Subject: [PATCH] =?UTF-8?q?React=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=20=D0=BF=D1=80=D0=B5=D0=BB=D0=BE=D0=B0=D0=B4?= =?UTF-8?q?=D0=B5=D1=80=D0=B0:=20=D1=81=D1=82=D0=B8=D0=BB=D0=B8,=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D1=82=D0=B5=D0=BA=D1=81=D1=82=20=D0=B4=D0=BB=D1=8F?= =?UTF-8?q?=20=D1=83=D0=BD=D0=B8=D0=B2=D0=B5=D1=80=D1=81=D0=B0=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF?= =?UTF-8?q?=D0=B0=20=D0=B8=D0=B7=20=D0=BB=D1=8E=D0=B1=D0=BE=D0=B9=20=D1=82?= =?UTF-8?q?=D0=BE=D1=87=D0=BA=D0=B8=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F,=20=D1=81=D0=B0=D0=BC=20tsx=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/css/components/preloader.css | 173 ++++++++++++++++++ .../js/components/preloader/Preloader.tsx | 39 ++++ resources/js/contexts/PreloaderContext.tsx | 40 ++++ 3 files changed, 252 insertions(+) create mode 100644 resources/css/components/preloader.css create mode 100644 resources/js/components/preloader/Preloader.tsx create mode 100644 resources/js/contexts/PreloaderContext.tsx diff --git a/resources/css/components/preloader.css b/resources/css/components/preloader.css new file mode 100644 index 0000000..c928ad7 --- /dev/null +++ b/resources/css/components/preloader.css @@ -0,0 +1,173 @@ +@import url('./../variables.css'); + +#preloader_container { + position: fixed; + width: 100%; + height: 100%; + top: 0; + left: 0; + background: rgba(0, 0, 0, 0.3); + z-index: 900; + backdrop-filter: blur(5px); + + &.hide { + display: none; + } + + &>#preloader{ + width: 200px; + height: 200px; + background: none; + position: relative; + top: 30%; + left: 0; + right: 0; + margin-left: auto; + margin-right: auto; + + &>.circle { + position: absolute; + border-radius: 50%; + box-sizing: border-box; + border: 10px solid transparent; + } + + &>#circle_one { + width: 80%; + height: 80%; + border-top-width: 10px; + left: 5%; + top: 5%; + animation: roll_one 4.5s; + animation-fill-mode: forwards; + animation-iteration-count: infinite; + animation-timing-function: linear; + + &.preloader-emerald-circle{ + border-top-color: var(--color_emerald_light); + } + &.preloader-ruby-circle{ + border-top-color: var(--color_ruby_main); + } + &.preloader-graphite-circle{ + border-top-color: var(--color_graphite_main); + } + } + + &>#circle_two { + width: 70%; + height: 70%; + left: 10%; + top: 10%; + border-right-width: 10px; + animation: roll_two 2.5s; + animation-fill-mode: forwards; + animation-iteration-count: infinite; + animation-timing-function: linear; + + &.preloader-emerald-circle{ + border-right-color: var(--color_emerald_light); + } + &.preloader-ruby-circle{ + border-right-color: var(--color_ruby_main); + } + &.preloader-graphite-circle{ + border-right-color: var(--color_graphite_main); + } + } + + &>#circle_three { + width: 60%; + height: 60%; + left: 15%; + top: 15%; + border-bottom-width: 10px; + animation: roll_three 3s; + animation-fill-mode: forwards; + animation-iteration-count: infinite; + animation-timing-function: linear; + + &.preloader-emerald-circle{ + border-bottom-color: var(--color_emerald_light); + } + &.preloader-ruby-circle{ + border-bottom-color: var(--color_ruby_main); + } + &.preloader-graphite-circle{ + border-bottom-color: var(--color_graphite_main); + } + } + + + + &>#preloader_text { + width: 150%; + left: -25%; + font-size: 1.5rem; + position: absolute; + bottom: -50px; + text-align: center; + color: white; + } + + &>#logo { + position: absolute; + background: none; + width: 40%; + height: 40%; + top: 25%; + left: 25%; + + &>.logo_square { + position: absolute; + box-sizing: border-box; + } + + &>#left-top { + width: 60%; + height: 70%; + top: 15%; + left: 20%; + border-left: 7px solid white; + border-top: 7px solid white; + border-top-left-radius: 45%; + } + + &>#right-bottom { + width: 40%; + height: 50%; + top: 15%; + left: 40%; + border-right: 7px solid white; + border-bottom: 7px solid white; + border-bottom-right-radius: 45%; + border-bottom-left-radius: 10%; + } + } + } +} + +@keyframes roll_one { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} +@keyframes roll_two { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(-360deg); + } +} +@keyframes roll_three { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(720deg); + } +} diff --git a/resources/js/components/preloader/Preloader.tsx b/resources/js/components/preloader/Preloader.tsx new file mode 100644 index 0000000..ef69e2a --- /dev/null +++ b/resources/js/components/preloader/Preloader.tsx @@ -0,0 +1,39 @@ +import "./../../../css/components/preloader.css"; +import React from "react"; + +export default function Preloader ( props: {visible: boolean, text?: string | null}) { + const {text = 'загрузка'} = props; + let preloaderColorArray: string[] = ['preloader-ruby-circle', 'preloader-emerald-circle', 'preloader-graphite-circle'], + //Код ниже реализует возможность присваивать каждую перезагрузку разные классы с цветом полос + //Рандомное число, по которому получим первый класс + firstColorIndex: number = Math.floor(Math.random() * 3), + firstCircleClass: typeof preloaderColorArray[number] = preloaderColorArray[firstColorIndex], + secondCircleClass: typeof preloaderColorArray[number], + thirdCircleClass: typeof preloaderColorArray[number]; + //Удалим уже используемый класс цвета из массива + preloaderColorArray.splice(firstColorIndex, 1); + //Оставшиеся 2 класса распределеяем в зависимости от проостой проверки четности/нечетности + if (Math.floor(Math.random() * 2) % 2 === 0) { + secondCircleClass = preloaderColorArray[0]; + thirdCircleClass = preloaderColorArray[1]; + } else { + secondCircleClass = preloaderColorArray[1]; + thirdCircleClass = preloaderColorArray[0]; + } + return ( + props.visible ? +
+
+
+
+
+ +
{ text }
+
+
+ : null + ) +} diff --git a/resources/js/contexts/PreloaderContext.tsx b/resources/js/contexts/PreloaderContext.tsx new file mode 100644 index 0000000..c82dd25 --- /dev/null +++ b/resources/js/contexts/PreloaderContext.tsx @@ -0,0 +1,40 @@ +import React, { createContext, useState } from "react"; +import Preloader from "../components/preloader/Preloader"; + +interface PreloaderProps{ + setPreloaderVisible: (preloaderVisible: boolean) => void; + setPreloaderText: (preloaderText: string) => void; +} + +export const PreloaderContext = createContext({ + setPreloaderVisible: () => {}, + setPreloaderText: () => {}, +}); + +export function PreloaderProvider({ children }){ + const [visible, setVisible] = useState(true); + const [text, setText] = useState('Страница загружается'); + + function setPreloaderVisible(preloaderVisible: boolean){ + setVisible(preloaderVisible); + } + + function setPreloaderText(preloaderText: string){ + setText(preloaderText); + } + + let value = { + setPreloaderVisible: setPreloaderVisible, + setPreloaderText: setPreloaderText + } + + return ( + + + {children} + + ) +}