Files
wow-design-wiki/motion/procedural-patterns.md
neo 76c4c5b4fe design-systems: add 74 brand DESIGN.md files (VoltAgent/awesome-design-md)
- 74 brands: airbnb, stripe, apple, figma, linear, vercel, etc.
- 147 files: DESIGN.md + README.md per brand (~2.1 MB)
- Each DESIGN.md contains structured brand analysis (colors, typography, components)
- Updated 00-overview.md with new design-systems directory
2026-06-21 20:40:35 +05:00

230 lines
9.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: motion/procedural-patterns
title: Procedural Pattern Generator
description: Canvas-based procedural generation of animated patterns around title elements. Uses occupancy grid, BFS proximity map, and seeded RNG for deterministic layout.
tags: [motion, procedural, pattern, canvas, generation, animation, title-card]
source: github.com/halfof8/patterngen-oss
---
# Procedural Pattern Generator
## Что это
Инструментарий для автоматического создания анимированных заставок с процедурно размещёнными паттернами вокруг заголовков. Состоит из трёх компонентов:
1. **Web App** (React + Canvas 2D) — основной генератор
2. **Figma Plugin** — импорт дизайна из Figma
3. **Blender Add-on** — импорт PNG-последовательностей как emissive planes
Пайплайн: Figma → Web App (генерация) → PNG-последовательности → Blender (композиция).
## Алгоритм размещения
### 1. Occupancy Grid
Сетка 20px на 1920×1080 (96×54 ячеек). Title-элементы занимают ячейки, помечая их как занятые.
```typescript
class GridOccupancy {
private cells: boolean[][]; // GRID_ROWS × GRID_COLS
markRect(x, y, w, h) { /* occupy cells */ }
isBlockFree(col, row, spanCols, spanRows) { /* collision check */ }
findFreeBlocks(spanCols, spanRows, align) { /* find valid positions */ }
}
```
### 2. BFS Proximity Map
BFS от всех title-элементов к каждой ячейке сетки. Определяет «зону влияния» — где паттерны могут появляться относительно заголовка.
```typescript
function buildProximityMap(titles): number[][] {
const dist: number[][] = fill(Infinity);
// BFS from all title edges
while (queue.length > 0) {
const [cx, cy] = queue.shift();
for (const [dx, dy] of neighbors) {
const nd = dist[cy+dy][cx+dx];
if (nd > dist[cy][cx] + 1) {
dist[cy+dy][cx+dx] = dist[cy][cx] + 1;
queue.push([cx+dx, cy+dy]);
}
}
}
}
```
### 3. Seeded Placement
Deterministic RNG (mulberry32). Same seed = identical layout.
```typescript
function mulberry32(seed) {
let state = seed >>> 0;
return () => {
state = (state + 0x6D2B79F5) >>> 0;
let t = state;
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
```
## 7 типов анимации паттернов
| Тип | Эффект | CSS/Canvas |
|-----|--------|------------|
| `dots` | Поэлементный fade-in со stagger | `globalAlpha` per shape |
| `capsule` | Рост от центра по длинной оси | Clip rect grow |
| `circle` | Pie-fill по часовой | Arc + clip |
| `arrow` | Slide up снизу | `translateY` offset |
| `square` | Scale 1.6× → 1× | `scale()` transform |
| `stripes` | Полосы slide-in staggered | Y-offset per stripe |
| `cross` | Scale 1.6× → 1× | `scale()` transform |
## Timeline: 3 фазы
```
0──────────→ durationMs ──────────→ 2×durationMs ──────────→ 4×durationMs (loop)
│ ФАЗА 1 │ ФАЗА 2 │ ФАЗА 3 │
│ Title reveal │ Pattern reveal │ Dots pulse │
│ half-reveal из центра │ clip-side reveal │ sin wave │
```
```typescript
// Фазовая логика timeline
function evaluate(elapsedMs, durationMs, titles, patterns, dots, easing) {
// Phase 1: Title reveal (0→1×)
const titleT = Math.min(1, elapsedMs / durationMs);
const titleEased = applyEasing(titleT, easing); // half-reveal
// Phase 2: Pattern reveal (1×→2×)
const cycleMs = afterTitle % (4 * durationMs);
// Phase 3: Dots pulse (2×→4×)
const holdMs = cycleMs - durationMs;
const pulse = 0.15 + 0.85 * (0.5 + 0.5 * Math.sin(2π * speed * t + phase));
}
```
## Color System
```typescript
function getPairsForColor(primary: string): ColorPair[] {
const lum = luminance(r, g, b);
const dark = mixRGB(primary, #000000, 0.45);
const light = mixRGB(primary, #FFFFFF, 0.35);
if (lum > 0.5) {
return [
{ bg: primary, fg: '#000000' },
{ bg: dark, fg: primary },
{ bg: primary, fg: dark },
];
}
// inverted for dark colors
}
```
Конвенция SVG-паттернов (40×40): `fill="white"` = фон (перекрашивается runtime), `fill="black"` / `stroke="black"` = foreground.
## Figma Plugin
Экспортирует фреймы 1920×1080 в JSON. Каждый слой экспортируется как 2× PNG, цвета автодетектятся.
```javascript
// code.js — экспорт слоя
bytes = await child.exportAsync({
format: 'PNG',
constraint: { type: 'SCALE', value: 2 },
});
// Автодетект цветов
collectUsedColors(node, usedColors); // BFS по иерархии
```
## Blender Add-on
Импорт ZIP с 3 PNG-последовательностями (titles, patterns, dots) как emissive planes:
```python
# Создаёт plane с aspect-ratio из разрешения PNG
plane.scale = (6.0, 6.0, 6.0)
plane.rotation_euler = (90°, 0, 0)
# Emissive shader node graph
tex.image.source = 'SEQUENCE'
tex.image_user.use_cyclic = cyclic # dots = True
nt.links.new(tex.outputs['Color'], bsdf.inputs['Emission'])
nt.links.new(tex.outputs['Alpha'], bsdf.inputs['Alpha'])
```
## Ключевые параметры
| Параметр | Диапазон | Описание |
|----------|----------|----------|
| density | 1–20 | Количество паттернов (формула: `3 + 40 × (d/10)²`) |
| proximity | 1–20 | Зона влияния от title (maxRadius = `2 + proximity × 2.5`) |
| stagger | 0–5 | Stagger window: `(stagger/5) × 0.8` |
| durationMs | 100–20000 | Длительность фазы |
| EXPORT_SCALE | 4 | Рендер в 4× разрешении |
| FPS | 24 | Частота кадров |
## Anti-patterns
| ❌ Плохо | ✅ Хорошо |
|----------|-----------|
| Паттерны накладываются на title | BFS proximity map ограничивает placement |
| Без collision detection | Occupancy grid предотвращает наложения |
| Рандомная расстановка | Seeded RNG — детерминизм для итераций |
| Паттерны без анимации | 7 типов анимации с clip-side |
| Фиксированные цвета | Алгоритмическая генерация color pairs |
| Рендер без bg/fg разделения | white→bg, black→fg runtime colorize |
| 1 кадр = 1 экспорт | Layer export: titles/patterns/dots separately |
## Где используется
- **patterngen-oss** (github.com/halfof8/patterngen-oss) — open-source референс
- Title cards в motion design
- Анимированные фоны лендингов
- Procedural patterns для социальных сетей
- Генерация фоновых текстур для 3D-композиций в Blender
## CV Service Validation
Процедурные паттерны рендерятся на Canvas 2D — это клиентская генерация, не серверная. Для валидации дизайна страницы с pattern-генератором:
```bash
curl -X POST http://localhost:8001/design/full-analysis \
-H "Content-Type: application/json" \
-d '{"html": "<HTML>", "full_analysis": true}'
```
**Особенности:** Canvas-элементы не оцениваются по semantic HTML — паттерны — это визуальный слой. Оценивается обёртка (header, sidebar, controls).
## Технические детали
```
Структура проекта:
├── src/store.ts # Zustand store — 373 строки, вся бизнес-логика
├── src/core/
│ ├── patterns.ts # Load SVG, colorize (white→bg, black→fg)
│ ├── colors.ts # getPairsForColor, luminance-based
│ ├── grid.ts # GridOccupancy + snapToGrid(20px)
│ ├── placement.ts # generatePlacement — BFS + occupancy
│ └── rng.ts # mulberry32 seeded RNG
├── src/engine/
│ ├── animations.ts # 7 анимаций (drawAnimatedPattern)
│ ├── easing.ts # 7 easing функций
│ └── timeline.ts # evaluate() — фазовая логика
├── src/export/
│ ├── renderer.ts # renderFrame() — canvas layer compositing
│ └── exporter.ts # exportAll() → ZIP with 3 layers × 24fps
├── figma-plugin/
│ ├── code.js # Figma sandbox export
│ └── ui.html # Plugin UI
└── blender-addon/
└── patterngen_importer.py # Blender 3.4+ operator
```
**Зависимости:** React 18, Zustand 5, JSZip, FileSaver, Vite