StepByStepTutorial.tsx
Обзор
Файл StepByStepTutorial.tsx реализует React-компонент пошагового интерактивного туториала, предназначенного для обучения пользователей основным этапам игрового процесса. Компонент выделяет ключевые элементы интерфейса с помощью подсветки и отображает позиционированные подсказки (tooltip) с описанием каждого шага. Туториал структурирован по фазам игры (например, выбор локации, выбор элемента, выбор элементаля), что позволяет последовательно сопровождать пользователя в различных частях интерфейса.
Основные задачи компонента:
Автоматический поиск и подсветка целевого элемента UI для текущего шага.
Позиционирование подсказки рядом с элементом с учётом размеров окна и предотвращением перекрытий.
Управление переходами между шагами, включая автоматический переход и переход по клику пользователя.
Адаптация к динамически меняющемуся интерфейсу с использованием
ResizeObserverиrequestAnimationFrame.Блокировка интерфейса фоновым затемнением и выделением целевого элемента для фокусировки внимания.
Составные части и описание
Интерфейс TutorialStep
Определяет структуру шага туториала.
Свойство | Тип | Описание |
|---|---|---|
|
| Уникальный идентификатор шага. |
|
| CSS-селектор целевого элемента для подсветки. |
|
| Заголовок шага, отображаемый в подсказке. |
|
| Текстовое описание шага для пользователя. |
| ['menu' \ | 'elementSelection' \ |
| `'top' \ | 'bottom' \ |
|
| Требуется ли от пользователя действие (например, клик) для перехода к следующему шагу. |
| `'click' \ | 'auto'` (опционально) |
Пропсы компонента StepByStepTutorial
interface StepByStepTutorialProps {
isActive: boolean; // Активен ли туториал в текущий момент
currentPhase: string; // Текущая фаза игры (например, 'menu', 'elementSelection')
steps: TutorialStep[]; // Массив всех шагов туториала
onComplete: () => void; // Коллбэк при завершении всего туториала
onSkip: () => void; // Коллбэк при пропуске туториала пользователем
}
Компонент StepByStepTutorial
Описание
Основной React-компонент, отвечающий за отображение и управление пошаговым интерактивным туториалом. На основе текущей фазы и активного шага он:
Находит целевой элемент по CSS-селектору из шага.
Подсвечивает этот элемент, добавляя класс
tutorial-target.Отображает подсказку с заголовком, описанием, прогрессом и кнопкой пропуска.
Рассчитывает положение подсказки и рамки подсветки с учётом размеров элемента и окна.
Обрабатывает переходы между шагами, включая автоматические и по клику.
Поддерживает динамическое обновление позиций при изменении размера элемента, окна или прокрутке.
Управляет классом
tutorial-activeу body для затемнения интерфейса.
Внутренние состояния
Состояние | Тип | Описание |
|---|---|---|
|
| Индекс активного шага в массиве шагов текущей фазы. |
| `HTMLElement \ | null` |
|
| Координаты для позиционирования всплывающей подсказки. |
|
| Позиция и размеры рамки подсветки вокруг целевого элемента. |
Основные методы и логика
updateAllPositions(element: HTMLElement)
Использует
getBoundingClientRect()для определения позиции и размеров целевого элемента.Рассчитывает позиционирование подсказки с учётом направления (
top,bottom,left,right), размеров тултипа и окна браузера.Вычисляет позицию и размеры рамки подсветки, включая радиус скругления, взятый из стилей элемента.
Реализует "умное" позиционирование, чтобы избежать наложения подсказки на выделенный элемент.
Обновляет состояния
tooltipPositionиhighlightPosition.
Поиск и обновление целевого элемента
При изменении активного шага или фазы компонент запускает поиск элемента через
document.querySelectorпоtargetSelector.Для особого шага с
id === 'elemental-select'реализован специальный алгоритм поиска первого доступного (не отключённого) элементаля в сетке.elemental-grid.Реализован механизм повторного поиска с интервалом и задержкой, чтобы дождаться рендера динамически загружаемых элементов.
При успешном поиске элементу добавляется класс
tutorial-target, а вbody— классtutorial-active.
Обновление позиций в реальном времени
Используется
ResizeObserverдля отслеживания изменений размеров целевого элемента и body.Используется
requestAnimationFrameдля плавного обновления положения подсветки и подсказки при изменениях.Обрабатываются события окна
resizeиscrollдля корректной адаптации интерфейса.
Переход между шагами (nextStep)
Находит текущий шаг в полном массиве
steps.Если есть следующий шаг, проверяет, меняется ли фаза — если да, сбрасывает индекс шага в 0, иначе инкрементирует.
Если шагов больше нет — вызывает
onComplete.
Автоматический переход
Для шагов с
triggerNext === 'auto'запускается таймаут в 2 секунды, по истечении которого вызываетсяnextStep.
Обработка кликов по целевому элементу
Для шагов с
actionRequired === trueиtriggerNext === 'click'добавляется слушатель клика на целевой элемент.При клике временно отключается блокировка интерфейса (
tutorial-active), затем вызываетсяnextStep.После перехода блокировка восстанавливается.
UI компонента
Полупрозрачный затемнённый фон (
tutorial-backdrop-step) для фокусировки внимания.Рамка подсветки (
tutorial-highlight-ring) вокруг выделенного элемента с небольшим отступом и скруглениями.Всплывающая подсказка (
tutorial-step-tooltip) с:Заголовком шага.
Кнопкой пропуска туториала.
Описанием.
Индикатором прогресса (текст и прогресс-бар).
Инструкцией по действию (автопереход или необходимость клика).
Константа BATTLE_TUTORIAL_STEPS
Пример набора шагов для боевого туториала, состоящий из трёх этапов:
Выбор локации
Выбор элемента
Выбор элементаля
Каждый шаг содержит все необходимые данные для корректного отображения и логики перехода.
Пример использования
import StepByStepTutorial, { BATTLE_TUTORIAL_STEPS } from './StepByStepTutorial';
function BattleTutorialContainer() {
const [isTutorialActive, setTutorialActive] = React.useState(true);
const [phase, setPhase] = React.useState('menu');
const handleComplete = () => {
console.log('Tutorial completed');
setTutorialActive(false);
};
const handleSkip = () => {
console.log('Tutorial skipped');
setTutorialActive(false);
};
return (
<StepByStepTutorial
isActive={isTutorialActive}
currentPhase={phase}
steps={BATTLE_TUTORIAL_STEPS}
onComplete={handleComplete}
onSkip={handleSkip}
/>
);
}
Взаимодействие с остальной системой
Компонент получает из внешнего состояния (например, игрового состояния или родительского компонента) текущую фазу игры и активность туториала.
Управляет визуальным выделением элементов интерфейса, взаимодействует с DOM напрямую для поиска и подсветки.
Вызовы
onCompleteиonSkipпозволяют интегрировать поведение туториала с логикой приложения (например, разблокировать UI, переключить режимы).Используется в рамках модуля "Обучение и поддержка игрока" совместно с другими компонентами, такими как уведомления о достижениях и раздел настроек.
Важные детали реализации
Для динамических элементов (например, выбор элементаля) реализован механизм периодического поиска с ограничением по количеству попыток.
Позиционирование подсказок учитывает размеры viewport и предотвращает наложения и выходы за границы.
Использование
ResizeObserverиrequestAnimationFrameобеспечивает плавное и точное обновление позиции без излишних перерисовок.Классы CSS
tutorial-activeиtutorial-targetуправляют визуальными эффектами затенения и подсветки.Обработка кликов с временным снятием блокировки интерфейса гарантирует корректное выполнение действия пользователя.
Диаграмма структуры компонента
classDiagram
class StepByStepTutorial {
- currentStepIndex: number
- targetElement: HTMLElement | null
- tooltipPosition: {top: number, left: number}
- highlightPosition: {top: number, left: number, width: number, height: number, borderRadius: string}
+ isActive: boolean
+ currentPhase: string
+ steps: TutorialStep[]
+ onComplete(): void
+ onSkip(): void
+ updateAllPositions(element: HTMLElement): void
+ nextStep(): void
+ render(): JSX.Element | null
}
class TutorialStep {
+ id: string
+ targetSelector: string
+ title: string
+ description: string
+ phase: string
+ position?: string
+ actionRequired?: boolean
+ triggerNext?: string
}
StepByStepTutorial "1" o-- "*" TutorialStep : steps
Заключение
Файл StepByStepTutorial.tsx содержит полнофункциональный компонент интерактивного пошагового туториала с выделением элементов интерфейса и подсказками, адаптированный для динамичного игрового UI. Его архитектура обеспечивает гибкость, удобство использования и плавный пользовательский опыт, что способствует эффективному обучению игроков и улучшению общего взаимодействия с приложением.