# Анимированные скролл-сайты с видео: практическое руководство

> Как создавать кинематографичные сайты с покадровой прокруткой видео (эффект Apple): нарезка кадров в WebP, сглаживание LERP на Canvas, dwell-remap и дизайн-система Precision Editorial.

Animated Video Scroll — это техника создания кинематографичных одностраничных сайтов, где прокрутка страницы напрямую управляет покадровым воспроизведением видео. Когда пользователь прокручивает страницу колесом мыши или свайпом на смартфоне, видеоряд плавно меняется кадр за кадром, синхронизируясь с ритмом чтения и появлением текста.

Этот визуальный прием стал фирменным знаком презентаций продуктов Apple, Tesla, Porsche и ведущих мировых дизайн-студий.

> [!IMPORTANT]
> Эта техника **не является встраиванием обычного видеоплеера** и не использует тяжелые GIF-файлы. В ее основе лежит покадровая последовательность оптимизированных WebP-изображений, аппаратно отрисовываемых на элементе `<canvas>` через WebGL или 2D Context с нулевой задержкой отклика на скролл.

---

## 1. Что такое скролл-сайт с видео и его преимущества

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

Сайт с управлением видео через скролл решает эти проблемы:

- **Полный контроль внимания:** Пользователь сам определяет темп изучения продукта. Остановка на абзаце фиксирует ракурс камеры в нужной точке.
- **Мгновенный отклик без буферизации:** Видео заранее разбивается на оптимизированные кадры WebP, избавляя браузер от тяжелого декодирования контейнеров MP4 при быстром скролле.
- **Синхронизация повествования:** Заголовки, спецификации и числовые метрики появляются строго в момент демонстрации соответствующего узла или детали.

### Матрица применимости

| Тип продукта | Почему подходит скролл-видео | Ключевой акцент в кадре |
| :--- | :--- | :--- |
| **Автомобили и техника** | Подчеркивает аэродинамику, шасси и отделку салона | Плавное движение камеры вдоль кузова, смена ракурсов |
| **Премиальная электроника** | Демонстрирует прецизионную сборку и обработку материалов | Разборка на компоненты (exploded view), макросъемка |
| **Архитектура и девелопмент** | Передает объем, масштаб и естественное освещение | Пролет через фасад во внутренние интерьеры |
| **Fashion и ювелирные изделия** | Раскрывает текстуру тканей и игру света на металле | Тонкое скольжение света по граням изделия |

---

## 2. Архитектура и структура файлов навыка

Пайплайн разработки построен как модульная инженерная система. Каждый файл папки навыка отвечает за свой изолированный этап:

```text
animated-sites-engine/
├── SKILL.md            # Сценарная инструкция: от анализа видео до верстки
├── visual-system.md    # Дизайн-система Precision Editorial и правила сетки
├── extract_frames.py   # Python-утилита нарезки и сжатия кадров в WebP
└── evals.json          # Бенчмарки и критерии автоматической проверки качества
```

### 1. SKILL.md — главное руководство и сценарный алгоритм
Регламентирует сквозной цикл разработки: от анализа исходного ролика через `ffprobe` до деления на смысловые главы, расчета высоты скролла и сборки готового шаблона.

### 2. visual-system.md — справочник системы Precision Editorial
Устанавливает строгие эстетические стандарты: отказ от клише (неоновых подсветок, размытых стеклянных плашек, шрифта Playfair), готовая палитра «Trackline» для инженерных тем и 5 схем компоновки контента.

### 3. extract_frames.py — скрипт экстракции и сжатия кадров
Консольная утилита на Python. Автоматически считывает метаданные видео через `ffprobe`, рассчитывает оптимальное число кадров, нарезает отдельные потоки для Desktop и Mobile в формате WebP и формирует `manifest.json`.

### 4. evals.json — сценарии верификации качества
Содержит эталонный тестовый сценарий (на примере ролика с Porsche GT3), проверяющий плавность LERP, поведение canvas при ресайзе, доступность с клавиатуры и общий вес ассетов.

---

## 3. Механика воспроизведения: Canvas, LERP и Dwell-Remap

В основе работы лежит элемент `<canvas>`, зафиксированный на весь экран (`position: fixed`). Прокрутка страницы пересчитывается в относительный прогресс от 0.0 до 1.0, определяющий отображаемый номер кадра.

```text
Позиция скролла (0.0 — 1.0)
          │
          ▼
┌───────────────────────┐
│     Dwell-Remap       │ ──► Искусственное замедление у центров глав
└──────────┬────────────┘
           │
           ▼
┌───────────────────────┐
│     LERP-фильтр       │ ──► targetFrame плавно догоняет текущий скролл
└──────────┬────────────┘
           │
           ▼
┌───────────────────────┐
│   HTML5 2D Canvas     │ ──► ctx.drawImage(loadedImages[frameIndex], ...)
└───────────────────────┘
```

### 1. Сглаживание через LERP (Linear Interpolation)

Прямое переключение кадров по событиям скролла приводит к дерганой анимации на колесах мыши с дискретным шагом. 

Для кинематографичной плавности применяется линейная интерполяция: отрисовываемый кадр (`currentFrame`) плавно догоняет целевой (`targetFrame`).

```javascript
let currentFrame = 0;
let targetFrame = 0;
const lerpFactor = 0.075; // Коэффициент сглаживания (0.05 — 0.1)

function renderLoop() {
  // Плавно приближаем текущий кадр к целевому
  currentFrame += (targetFrame - currentFrame) * lerpFactor;
  
  const frameToDraw = Math.round(currentFrame);
  drawCanvasFrame(frameToDraw);

  requestAnimationFrame(renderLoop);
}

window.addEventListener("scroll", () => {
  const maxScroll = document.documentElement.scrollHeight - window.innerHeight;
  const progress = Math.min(Math.max(window.scrollY / maxScroll, 0), 1);
  targetFrame = progress * (totalFrames - 1);
}, { passive: true });
```

### 2. Замедление у глав (Dwell-Remap)

Чтобы посетитель успевал прочитать заголовок и ключевую мысль без необходимости ювелирно удерживать колесо мыши, применяется **Dwell-Remap**. Скорость смены кадров искусственно снижается около центров глав, сохраняя нормальную скорость на переходах.

```javascript
function applyDwellRemap(rawProgress, chapterPoints = [0.2, 0.5, 0.8], dwellWidth = 0.08, strength = 0.45) {
  let remapped = rawProgress;
  
  for (const center of chapterPoints) {
    const distance = Math.abs(rawProgress - center);
    if (distance < dwellWidth) {
      // Плавное косинусоидальное замедление
      const factor = Math.cos((distance / dwellWidth) * (Math.PI / 2));
      const delta = (rawProgress - center) * (1 - factor * strength);
      remapped = center + delta;
      break;
    }
  }
  return Math.min(Math.max(remapped, 0), 1);
}
```

### 3. Прогрессивная загрузка кадров

Чтобы исключить белые вспышки canvas и не заставлять пользователя ждать загрузки всех 120+ кадров, применяется двухступенчатая схема:

1. **Критический приоритет:** Мгновенно подгружаются первые 5 кадров и опорные кадры каждой главы.
2. **Фоновая очередь:** Остальные кадры догружаются небольшими пачками через `requestIdleCallback`. Если запрошенный кадр еще в пути, canvas показывает ближайший доступный соседний кадр.

---

## 4. Пошаговый процесс разработки: от видео к коду

Производственный пайплайн состоит из семи последовательных шагов:

### Шаг 1. Анализ ролика через ffprobe

Перед началом верстки извлекаются технические параметры исходного файла:

```bash
ffprobe -v error -select_streams v:0 \
  -show_entries stream=width,height,duration,r_frame_rate,codec_name \
  -of default=noprint_wrappers=1 input.mp4
```

Формируется контактный лист из 6 опорных кадров для анализа движения камеры, положения объекта и наличия свободного пространства (negative space) под текст.

### Шаг 2. Формулирование дизайн-концепции

Фиксируется четкий план:
- **Главный герой:** Что находится в фокусе внимания.
- **Целевая эмоция:** Инженерная строгость, спокойная роскошь, скорость или минимализм.
- **Шрифтовая пара:** Характерный заголовочный гротеск и моноширинный акцент.
- **Фирменный прием:** Телеметрия, переменная ширина начертания, счетчик микрокадров.

### Шаг 3. Нарезка и оптимизация кадров

Количество кадров и высота прокрутки страницы определяются хронометражем исходного ролика:

| Длительность видео | Количество кадров | Рекомендуемая высота скролла | Целевой вес ассетов (WebP) |
| :--- | :--- | :--- | :--- |
| **0–5 секунд** | 60–80 кадров | 450–550vh | 4–7 МБ |
| **5–15 секунд** | 90–130 кадров | 600–750vh | 8–12 МБ |
| **15–30 секунд** | 130–180 кадров | 750–900vh | 10–15 МБ |
| **30+ секунд** | 180–200 кадров | 900–1000vh | 12–16 МБ |

Запуск нарезки через Python-утилиту:

```bash
python3 extract_frames.py \
  --input ./raw/porsche-gt3.mp4 \
  --output ./public/frames \
  --desktop-frames 120 \
  --mobile-frames 80 \
  --quality 62
```

### Шаг 4. Сценарий по главам

Материал делится на 4–6 смысловых глав:
1. **Глава 1: Введение (0–15%)** — Название модели, силуэт в контровом свете.
2. **Глава 2: Аэродинамика (20–40%)** — Геометрия антикрыла и диффузора.
3. **Глава 3: Силовая установка (45–65%)** — Шасси и показатели мощности.
4. **Глава 4: Финал и действие (75–100%)** — Полный ракурс и кнопка тест-драйва.

### Шаг 5. Композиция слоев вокруг видео

Текст не должен перекрывать движущийся объект. Положение блоков динамически смещается от левой к правой колонке в зависимости от траектории камеры.

### Шаг 6. Сборка на основе стартового шаблона

В готовый HTML5-шаблон переносятся токены стилей, сгенерированный манифест кадров и текстовые главы.

### Шаг 7. Проверка и арт-дирекшн

> [!TIP]
> Всегда проверяйте результат через локальный сервер (`python3 -m http.server`), а не открытием `file:///`. Политика CORS в браузерах блокирует асинхронную загрузку WebP-кадров при прямом открытии файлов с диска.

---

## 5. Визуальная система: типографика и палитра Trackline

Стиль **Precision Editorial** требует отказа от декоративного мусора в пользу строгой швейцарской сетки и приборной эстетики.

### Типографические пары

:::tabs
=== Archivo + IBM Plex Mono (Спортивно-технический)
- **Заголовки:** Archivo (Black, плотный трекинг `-0.03em`)
- **Основной текст:** Archivo (Regular, 15px)
- **Телеметрия и шильдики:** IBM Plex Mono (Medium, 11px, Uppercase)
- **Подходит для:** Автомобили, авиация, тяжелая инженерия, часы.
=== Roboto Flex + Roboto Mono (Современный инженерный)
- **Заголовки:** Roboto Flex (Variable width, Bold)
- **Основной текст:** Roboto (Regular, 16px)
- **Телеметрия:** Roboto Mono (12px, tabular-nums)
- **Подходит для:** Корпоративный софт, робототехника, лабораторные приборы.
=== Barlow Condensed + Manrope (Кинематографичный драйв)
- **Заголовки:** Barlow Condensed (Extra Bold, Uppercase)
- **Основной текст:** Manrope (Medium, 15px)
- **Телеметрия:** Space Mono (11px)
- **Подходит для:** Экстремальный спорт, киберспорт, streetwear-дропы.
=== Anybody + Source Code Pro (Индустриальный акцент)
- **Заголовки:** Anybody (Bold, Ultra-expanded)
- **Основной текст:** Inter (Regular, 15px)
- **Телеметрия:** Source Code Pro (11px)
- **Подходит для:** Архитектурные студии, концептуальные электрокары.
:::

### Цветовая палитра «Trackline»

Палитра вдохновлена асфальтом гоночного трека, карбоном и контрольными лампами приборов:

```css
:root {
  /* Базовые поверхности */
  --color-carbon: #080a09;    /* Глубокий матовый черный (основной фон) */
  --color-paper: #f1f0e9;     /* Теплый матовый белый (основной текст) */
  --color-alloy: #a8aca8;     /* Анодированный алюминий (второстепенный текст) */
  --color-track: #414541;     /* Графитовый асфальт (линии сетки, разделители) */
  
  /* Сигнальные акценты */
  --color-petrol: #12372d;    /* Глубокий гоночный петролеум (фон карточек) */
  --color-signal: #d9381e;    /* Гоночный красный (активные состояния, алерты) */
  --color-heritage: #c5a059;  /* Золотистый оттенок (индикатор прогресса) */
}
```

> [!NOTE]
> Сигнальные цвета (`--color-signal` и `--color-heritage`) наносятся точечно — не более чем на 3% площади экрана.

---

## 6. Композиция слоев, движение и фирменные приемы

Страница формируется из трех наложенных друг на друга визуальных слоев:

```text
┌─────────────────────────────────────────────────────────────┐
│ 3. Слой интерфейса: прогресс-бар, статус главы, кнопки      │
├─────────────────────────────────────────────────────────────┤
│ 2. Информационный слой: заголовок, описание, телеметрия     │
├─────────────────────────────────────────────────────────────┤
│ 1. Canvas-слой: полноэкранное видео со сглаживанием LERP    │
└─────────────────────────────────────────────────────────────┘
```

### 5 схем компоновки контента

| Схема компоновки | Положение контента | Оптимальный сценарий |
| :--- | :--- | :--- |
| **Masthead Statement** | Крупный заголовок по центру экрана | Вводный экран в начале страницы |
| **Side Margin Annotation** | Узкая колонка (3–4 cols) слева или справа | Объект движется в противоположной части кадра |
| **Split Statement** | Заголовок слева, параметры справа | Объект стабильно зафиксирован по центру |
| **Telemetry Dock** | Горизонтальная плашка внизу экрана | Демонстрация характеристик, мощности и скорости |
| **Final Callout** | Карточка с единственным целевым действием | Финальный экран после завершения видеоряда |

### Принципы анимации DOM-элементов

Так как видео на canvas уже обеспечивает мощную динамику, оверлеи анимируются сдержанно:
- **Появление через маску:** Текст поднимается из контейнера с `overflow: hidden`.
- **Смещение:** Не более 16–24px по вертикали.
- **Тайминги:** 600–800ms с кривой `cubic-bezier(0.16, 1, 0.3, 1)`.
- **Никаких размытий:** Свойство `filter: blur()` запрещено — оно перегружает GPU и разрушает 60 FPS.

---

## 7. Антипаттерны: отказ от устаревших штампов

Большинство типовых видеолендингов страдают от одних и тех же дизайнерских ошибок. Избавьтесь от них:

| Устаревший штамп | Почему это плохо | Профессиональная альтернатива |
| :--- | :--- | :--- |
| **Playfair Display + тонкий гротеск** | Шаблон «псевдороскоши» | Прецизионные гротески: Archivo, Roboto Flex |
| **Glassmorphism (матовое стекло)** | Размывает видео, перегружает видеокарту | Контрастный чистый текст в свободном пространстве кадра |
| **Плавающие частицы и пыль** | Отвлекают внимание от реального продукта | Чистый canvas без искусственного визуального мусора |
| **Фиолетово-синий неоновый градиент** | Ассоциируется с шаблонными криптосайтами | Натуральные оттенки, взятые из кадров видео |
| **Кастомный курсор с задержкой** | Создает ощущение лага интерфейса | Системный курсор с понятными интерактивными ховерами |

---

## 8. Производительность, доступность (A11y) и адаптивность

### 1. Ограничение Device Pixel Ratio (DPR)
На мобильных экранах с DPR 3.0 рендеринг полноэкранного canvas перегревает устройство. Ограничьте DPR значением 2.0:

```javascript
// Ограничиваем DPR значением 2.0
const dpr = Math.min(window.devicePixelRatio || 1, 2);
canvas.width = window.innerWidth * dpr;
canvas.height = window.innerHeight * dpr;
ctx.scale(dpr, dpr);
```

### 2. Пауза рендеринга для скрытых вкладок
Останавливайте анимационный цикл при переключении вкладки:

```javascript
document.addEventListener("visibilitychange", () => {
  if (document.hidden) {
    cancelAnimationFrame(animationFrameId);
  } else {
    requestAnimationFrame(renderLoop);
  }
});
```

### 3. Поддержка режима уменьшенной анимации (prefers-reduced-motion)
> [!IMPORTANT]
> Если в операционной системе включен режим `prefers-reduced-motion: reduce`, замените непрерывную смену кадров на статичный постер высокого разрешения.

```css
@media (prefers-reduced-motion: reduce) {
  .scroll-container {
    height: auto !important;
  }
  canvas {
    display: none !important;
  }
  .static-hero-fallback {
    display: block !important;
  }
}
```

---

## 9. Практический воркшоп: создание и запуск проекта

### Шаг 1. Нарезка кадров

Поместите видео в рабочую папку и выполните команду:

```bash
mkdir my-scroll-site && cd my-scroll-site
python3 extract_frames.py --input sample.mp4 --output ./frames --desktop-frames 120
```

Скрипт создаст файл `frames/manifest.json`:

```json
{
  "totalFrames": 120,
  "desktop": {
    "pathPattern": "frames/desktop/frame-%04d.webp",
    "width": 1920,
    "height": 1080,
    "totalWeightMb": 9.4
  },
  "recommendedScrollVh": 700
}
```

### Шаг 2. Структура файлов готового проекта

```text
my-scroll-site/
├── index.html              # Разметка и контейнер canvas
├── styles.css              # Токены стилей и медиазапросы
├── app.js                  # Цикл LERP, dwell-remap и обработчики скролла
└── frames/
    ├── desktop/            # frame-0001.webp ... frame-0120.webp
    ├── mobile/             # frame-0001.webp ... frame-0080.webp
    └── manifest.json       # Метаданные и размеры
```

### Шаг 3. Запуск локального сервера

```bash
python3 -m http.server 3000
```

Откройте `http://localhost:3000` и проверьте синхронизацию глав с прокруткой.

---

## 10. Итоговый чек-лист готовности скролл-сайта

### Инженерия и производительность
- [x] Вес кадров для десктопа не превышает 12 МБ (мобильные — до 5 МБ).
- [x] Первые 5 кадров имеют директиву `<link rel="preload">`.
- [x] Коэффициент DPR ограничен значением `2.0`.
- [x] Цикл `requestAnimationFrame` засыпает при неактивной вкладке.

### UX и доступность
- [x] LERP обеспечивает плавный ход кадров без рывков.
- [x] Dwell-Remap позволяет комфортно прочесть текст каждой главы.
- [x] Реализован статичный постер для `prefers-reduced-motion`.
- [x] Кликабельные элементы имеют размер не менее 44×44px.

### Дизайн и эстетика
- [x] Выбрана строгая инженерная шрифтовая пара.
- [x] Текст не закрывает ключевой объект в кадре.
- [x] Палитра интерфейса сформирована из цветов самого видео.
- [x] В конце страницы размещен единственный ясный призыв к действию (CTA).