Canvas 2D проти Three.js — Коли обирати кожен із них

Не кожна симуляція потребує WebGL. Ось повна схема прийняття рішень, якою я користуюся, коли починаю нову симуляцію — і чому приблизно половина з 225 симуляцій цього сайту виконана на чистому Canvas 2D.

Справжнє питання

Коли ви починаєте нову симуляцію, вибір між Canvas 2D і Three.js рідко зводиться до питання «що краще». Йдеться про те, що насправді потрібно самій симуляції. 2D-API Canvas 2D напрочуд потужний, а потік рендерингу залишається на CPU — а це саме те, що часом потрібно, коли ваша фізика теж працює на CPU і вам потрібна тісна синхронність.

Three.js передає рендеринг на GPU через WebGL. Це правильний вибір, коли у вас десятки тисяч частинок, об'ємні ефекти або 3D-геометрія в реальному часі. Але це має свою ціну: більше коду, більша залежність і вищі витрати на налагодження.

Пряме порівняння

Критерій Canvas 2D Three.js (WebGL)
Складність налаштування 3 рядки коду щонайменше ~20 рядків
Кількість частинок (60 FPS) ~2 000–5 000 50 000–1 000 000+
Розмір залежності 0 КБ (вбудовано в браузер) ~150 КБ (ядро Three.js)
Власні шейдери (GLSL) Недоступно Повна підтримка GLSL
3D-рендеринг Ручна проєкційна математика Нативний 3D-граф сцени
ImageData / робота з пікселями Швидкий, прямий доступ Потрібне зчитування render target
Рендеринг тексту fillText() — тривіально TextGeometry / Sprites — складно
Інтерактивність (клік/перетягування) Проста координатна математика Потрібен рейкастинг
Налагодження DevTools, брейкпоінти працюють скрізь Помилки GLSL важко читати
Продуктивність на мобільних Прийнятно для малого N GPU добре справляється з великим N
Постобробка (bloom, blur) Вручну, повільно EffectComposer + вбудовані проходи

Коли перемагає Canvas 2D

Обирайте Canvas 2D, коли:

Коли перемагає Three.js

Переходьте на Three.js, коли:

Реальні приклади з цього сайту

З 225 симуляцій на цьому сайті приблизно половина використовує Canvas 2D, а половина — Three.js. Ось як розподілено:

Хороша евристика: починайте кожну симуляцію з Canvas 2D. Якщо після робочого прототипу ви впираєтеся в стелю частоти кадрів або потребуєте 3D-вигляду — мігруйте на Three.js. Так ви додасте зайву складність лише тоді, коли вона справді потрібна — і зрозумієте алгоритм, перш ніж боротися з рендерером.

Починаємо з Canvas 2D, мігруємо пізніше

Шлях міграції з Canvas 2D на Three.js зазвичай простий, якщо ви від самого початку відокремлюєте логіку симуляції від рендерингу. Тримайте оновлення фізики в чистій функції, що приймає стан і повертає новий стан. Рендерер — лише споживач цього стану.

// Хороша структура — рендерер можна замінити
function updatePhysics(state, dt) { /* чиста логіка */ return newState; }
function renderCanvas2D(ctx, state) { /* малює на канвасі */ }
function renderThreeJS(scene, state) { /* оновлює об'єкти Three.js */ }

// У циклі rAF:
state = updatePhysics(state, dt);
renderCanvas2D(ctx, state);  // заміните на renderThreeJS, коли будете готові

Пастка, якої слід уникати: змішування фізичних розрахунків із викликами малювання. Щойно ви робите ctx.moveTo(particle.x += vx * dt, ...) всередині циклу малювання, міграція перетворюється на повне переписування замість простої заміни рендерера.

Чек-лист для рішення