refactor: Переработка компонентов на использование хука useGSAP

This commit is contained in:
Ilia Mashkov
2025-11-23 15:39:04 +03:00
parent 53d7b90c72
commit c57957f15c
5 changed files with 80 additions and 62 deletions

View File

@@ -20,6 +20,7 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@gsap/react": "^2.1.2",
"classnames": "^2.5.1", "classnames": "^2.5.1",
"gsap": "^3.13.0", "gsap": "^3.13.0",
"react": "^19.0.0", "react": "^19.0.0",

14
pnpm-lock.yaml generated
View File

@@ -8,6 +8,9 @@ importers:
.: .:
dependencies: dependencies:
'@gsap/react':
specifier: ^2.1.2
version: 2.1.2(gsap@3.13.0)(react@19.2.0)
classnames: classnames:
specifier: ^2.5.1 specifier: ^2.5.1
version: 2.5.1 version: 2.5.1
@@ -1111,6 +1114,12 @@ packages:
resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@gsap/react@2.1.2':
resolution: {integrity: sha512-JqliybO1837UcgH2hVOM4VO+38APk3ECNrsuSM4MuXp+rbf+/2IG2K1YJiqfTcXQHH7XlA0m3ykniFYstfq0Iw==}
peerDependencies:
gsap: ^3.12.5
react: '>=17'
'@humanfs/core@0.19.1': '@humanfs/core@0.19.1':
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
engines: {node: '>=18.18.0'} engines: {node: '>=18.18.0'}
@@ -6799,6 +6808,11 @@ snapshots:
'@eslint/core': 0.17.0 '@eslint/core': 0.17.0
levn: 0.4.1 levn: 0.4.1
'@gsap/react@2.1.2(gsap@3.13.0)(react@19.2.0)':
dependencies:
gsap: 3.13.0
react: 19.2.0
'@humanfs/core@0.19.1': {} '@humanfs/core@0.19.1': {}
'@humanfs/node@0.16.7': '@humanfs/node@0.16.7':

View File

@@ -8,9 +8,10 @@
* Поддерживает клик по точкам для переключения периодов. * Поддерживает клик по точкам для переключения периодов.
*/ */
import { useGSAP } from '@gsap/react'
import classNames from 'classnames' import classNames from 'classnames'
import { gsap } from 'gsap' import { gsap } from 'gsap'
import { memo, useCallback, useEffect, useMemo, useRef } from 'react' import { memo, useCallback, useMemo, useRef } from 'react'
import styles from './CircleTimeline.module.scss' import styles from './CircleTimeline.module.scss'
import { calculateCoordinates } from '../../lib/utils/calculateCoordinates/calculateCoordinates' import { calculateCoordinates } from '../../lib/utils/calculateCoordinates/calculateCoordinates'
@@ -71,10 +72,11 @@ export const CircleTimeline = memo(function CircleTimeline({
const titlesRef = useRef<(HTMLSpanElement | null)[]>([]) const titlesRef = useRef<(HTMLSpanElement | null)[]>([])
/** /**
* Эффект для анимации поворота круга и контр-поворота точек * Анимация поворота круга и контр-поворота точек
* Запускается при изменении rotation * Использует useGSAP hook для автоматической очистки анимаций
*/ */
useEffect(() => { useGSAP(
() => {
// Анимация поворота контейнера круга // Анимация поворота контейнера круга
if (circleRef.current) { if (circleRef.current) {
gsap.to(circleRef.current, { gsap.to(circleRef.current, {
@@ -96,7 +98,7 @@ export const CircleTimeline = memo(function CircleTimeline({
// Анимация заголовка // Анимация заголовка
const title = titlesRef.current[index] const title = titlesRef.current[index]
if (title) { if (title) {
// Сбрасываем предыдущие анимации для этого элемента // Останавливаем предыдущие анимации для предотвращения конфликтов
gsap.killTweensOf(title) gsap.killTweensOf(title)
if (index === activeIndex) { if (index === activeIndex) {
@@ -116,7 +118,9 @@ export const CircleTimeline = memo(function CircleTimeline({
} }
} }
}) })
}, [rotation, activeIndex]) },
{ dependencies: [rotation, activeIndex] }
)
/** /**
* Мемоизированный расчет позиций точек на круге * Мемоизированный расчет позиций точек на круге

View File

@@ -4,9 +4,10 @@
* Отображает список исторических событий в виде слайдера * Отображает список исторических событий в виде слайдера
*/ */
import { useGSAP } from '@gsap/react'
import classNames from 'classnames' import classNames from 'classnames'
import { gsap } from 'gsap' import { gsap } from 'gsap'
import { memo, useEffect, useRef, useState } from 'react' import { memo, useRef, useState } from 'react'
import { Swiper, SwiperSlide } from 'swiper/react' import { Swiper, SwiperSlide } from 'swiper/react'
import 'swiper/css' import 'swiper/css'
@@ -45,7 +46,7 @@ export interface EventsCarouselProps {
* *
* Использует Swiper для создания слайдера с кастомной навигацией. * Использует Swiper для создания слайдера с кастомной навигацией.
* Поддерживает адаптивное количество слайдов на разных размерах экрана. * Поддерживает адаптивное количество слайдов на разных размерах экрана.
* Анимирует появление/исчезновение с помощью GSAP. * Анимирует появление/исчезновение с помощью GSAP useGSAP hook.
* *
* @example * @example
* ```tsx * ```tsx
@@ -62,13 +63,11 @@ export const EventsCarousel = memo(
const [isEnd, setIsEnd] = useState(false) const [isEnd, setIsEnd] = useState(false)
/** /**
* Эффект для анимации появления/исчезновения карусели * Анимация появления/исчезновения карусели
* Использует GSAP для плавной анимации opacity и y-позиции * Использует useGSAP hook для автоматической очистки анимаций
*/ */
useEffect(() => { useGSAP(
if (!containerRef.current) return () => {
const ctx = gsap.context(() => {
if (visible) { if (visible) {
gsap.fromTo( gsap.fromTo(
containerRef.current, containerRef.current,
@@ -86,10 +85,9 @@ export const EventsCarousel = memo(
duration: HIDE_DURATION, duration: HIDE_DURATION,
}) })
} }
}, containerRef) },
{ scope: containerRef, dependencies: [visible, events] }
return () => ctx.revert() )
}, [visible, events])
/** /**
* Обработчик инициализации Swiper * Обработчик инициализации Swiper

View File

@@ -3,6 +3,7 @@
* Главный компонент временной шкалы с круговой диаграммой и каруселью событий * Главный компонент временной шкалы с круговой диаграммой и каруселью событий
*/ */
import { useGSAP } from '@gsap/react'
import classNames from 'classnames' import classNames from 'classnames'
import { gsap } from 'gsap' import { gsap } from 'gsap'
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
@@ -21,7 +22,7 @@ import { EventsCarousel } from '../EventsCarousel/EventsCarousel'
* *
* Отображает исторические периоды на круговой диаграмме с возможностью * Отображает исторические периоды на круговой диаграмме с возможностью
* переключения между ними. Для каждого периода показывается карусель событий. * переключения между ними. Для каждого периода показывается карусель событий.
* Центральные даты анимируются при смене периода с помощью GSAP. * Центральные даты анимируются при смене периода с помощью GSAP useGSAP hook.
* *
* @example * @example
* ```tsx * ```tsx
@@ -66,13 +67,11 @@ export const TimeFrameSlider = memo(() => {
}, [activePeriod, anglePerPoint]) }, [activePeriod, anglePerPoint])
/** /**
* Анимация центральных дат с использованием GSAP * Анимация центральных дат с использованием GSAP useGSAP hook
* Плавно изменяет числа при смене периода * Плавно изменяет числа при смене периода
*/ */
useEffect(() => { useGSAP(
if (!containerRef.current) return () => {
const ctx = gsap.context(() => {
if (startYearRef.current) { if (startYearRef.current) {
gsap.fromTo( gsap.fromTo(
startYearRef.current, startYearRef.current,
@@ -109,10 +108,12 @@ export const TimeFrameSlider = memo(() => {
{ opacity: 1, visibility: 'visible', duration: 1 } { opacity: 1, visibility: 'visible', duration: 1 }
) )
} }
}, containerRef) },
{
return () => ctx.revert() scope: containerRef,
}, [currentPeriod.yearFrom, currentPeriod.yearTo]) dependencies: [currentPeriod.yearFrom, currentPeriod.yearTo],
}
)
/** /**
* Переключение на предыдущий период * Переключение на предыдущий период