Prawdziwe pytanie
Gdy zaczynasz nową symulację, wybór między Canvas 2D a Three.js rzadko sprowadza się do pytania „co jest lepsze". Chodzi o to, czego naprawdę potrzebuje sama symulacja. Dwuwymiarowe API Canvas 2D jest zaskakująco wydajne, a wątek renderowania pozostaje na CPU — a to czasem jest dokładnie to, czego potrzebujesz, gdy Twoja fizyka też działa na CPU i wymagasz ścisłej synchroniczności.
Three.js przekazuje renderowanie do GPU poprzez WebGL. To właściwy wybór, gdy masz dziesiątki tysięcy cząstek, efekty wolumetryczne lub geometrię 3D w czasie rzeczywistym. Ale to ma swoją cenę: więcej kodu, większą zależność i wyższe koszty debugowania.
Porównanie bezpośrednie
| Kryterium | Canvas 2D | Three.js (WebGL) |
|---|---|---|
| Złożoność konfiguracji | 3 linijki kodu | minimum ~20 linijek |
| Liczba cząstek (60 FPS) | ~2 000–5 000 | 50 000–1 000 000+ |
| Rozmiar zależności | 0 KB (wbudowane w przeglądarkę) | ~150 KB (rdzeń Three.js) |
| Własne shadery (GLSL) | Niedostępne | Pełne wsparcie GLSL |
| Renderowanie 3D | Ręczna matematyka projekcji | Natywny graf sceny 3D |
| ImageData / operacje na pikselach | Szybki, bezpośredni dostęp | Wymaga odczytu render targetu |
| Renderowanie tekstu | fillText() — trywialne | TextGeometry / Sprites — złożone |
| Interaktywność (klik/przeciąganie) | Prosta matematyka współrzędnych | Wymaga raycastingu |
| Debugowanie | DevTools, breakpointy działają wszędzie | Błędy GLSL są trudne do odczytania |
| Wydajność na urządzeniach mobilnych | Akceptowalna dla małego N | GPU dobrze radzi sobie z dużym N |
| Postprocessing (bloom, blur) | Ręcznie, wolno | EffectComposer + wbudowane passy |
Kiedy wygrywa Canvas 2D
Wybierz Canvas 2D, gdy:
- Symulacja jest z natury dwuwymiarowa. Automaty komórkowe, modele agentowe, dwuwymiarowa dynamika płynów, grafy, wykresy, drzewa decyzyjne — żadne z nich nie zyskują na grafie sceny 3D. Dodanie Three.js byłoby czystym narzutem.
-
Potrzebujesz kontroli na poziomie pikseli.
Wzory reakcji-dyfuzji, mapy cieplne i każda symulacja piszącą do
siatki pikseli są najłatwiejsze do zrealizowania przy pomocy
ImageData. AktualizowanieUint8ClampedArrayna CPU i wywołanieputImageData()jest prostsze niż zarządzanie teksturami WebGL. - Liczba cząstek jest poniżej ~5 000. Canvas 2D komfortowo renderuje 2 000–5 000 kółek lub linii przy 60 FPS na sprzęcie średniej klasy. Jeśli Twoja symulacja nigdy nie przekracza tej granicy, nie dodawaj złożoności WebGL.
- Strona musi ładować się szybko. Żadnego tagu skryptu z CDN, żadnej inicjalizacji kontekstu WebGL. Symulacja na Canvas 2D może być w pełni interaktywna już w ciągu 50 ms od załadowania strony.
- Piszesz szybki prototyp. Canvas 2D pozwala iterować w kilka minut. Najpierw zweryfikuj algorytm, potem ulepszaj renderer.
Kiedy wygrywa Three.js
Przejdź na Three.js, gdy:
-
Liczba cząstek przekracza ~5 000. Użyj
PointszBufferGeometryi własnymShaderMaterial. Przesuwanie miliona cząstek na klatkę na GPU jest trywialne; Canvas 2D spadłby do jednocyfrowego FPS. - Scena jest naprawdę trójwymiarowa. Symulacje galaktyk, mechanika orbitalna, wizualizacje molekularne i inżynieria strukturalna potrzebują właściwej kamery 3D, projekcji perspektywicznej i bufora głębi.
- Potrzebujesz własnych efektów na poziomie fragmentu. Fale Gerstnera w oceanie, odbicia Fresnela, ray marching oparty na SDF, chmury wolumetryczne — wszystko to wymaga fragment shaderów GLSL, które po prostu nie mogą działać w Canvas 2D.
-
Wizualizacja wymaga oświetlenia lub cieni w czasie
rzeczywistym.
MeshStandardMateriali mapy cieni w Three.js działają od razu, „z pudełka". Odtworzenie nawet podstawowego oświetlenia Phonga w Canvas 2D to już samo w sobie projekt badawczy.
Prawdziwe przykłady z tej strony
Spośród 225 symulacji na tej stronie mniej więcej połowa korzysta z Canvas 2D, a połowa z Three.js. Oto podział:
- Canvas 2D: wszystkie automaty komórkowe (Gra w Życie, Pożar lasu, Reakcja-dyfuzja), wszystkie dwuwymiarowe modele agentowe (Boids 2D, Mrówki, Epidemia SIR), algorytmy sortowania, drzewa decyzyjne, filtry cyfrowe, wykresy finansowe, modele ruchu drogowego i większość edukacyjnych demonstracji fizycznych.
- Three.js: Galaktyka (80 000 cząstek gwiazd), shader oceanu (Gerstner + Fresnel), płyty tektoniczne (mapa przemieszczeń), dynamika płynów SPH (100 000 cząstek), path tracing, ray marching oraz wszystkie prawdziwe symulacje mechaniczne 3D.
Dobra heurystyka: zaczynaj każdą symulację od Canvas 2D. Jeśli po działającym prototypie napotykasz barierę liczby klatek albo potrzebujesz widoku 3D — przejdź na Three.js. Dzięki temu dodasz dodatkową złożoność tylko wtedy, gdy jest naprawdę potrzebna — i zrozumiesz algorytm, zanim zaczniesz walczyć z rendererem.
Zaczynanie od Canvas 2D, migracja później
Ścieżka migracji z Canvas 2D do Three.js jest zwykle prosta, jeśli od samego początku oddzielasz logikę symulacji od renderowania. Trzymaj aktualizację fizyki w czystej funkcji, która przyjmuje stan i zwraca nowy stan. Renderer jest jedynie konsumentem tego stanu.
// Dobra struktura — renderer można podmienić
function updatePhysics(state, dt) { /* czysta logika */ return newState; }
function renderCanvas2D(ctx, state) { /* rysuje na canvasie */ }
function renderThreeJS(scene, state) { /* aktualizuje obiekty Three.js */ }
// W pętli rAF:
state = updatePhysics(state, dt);
renderCanvas2D(ctx, state); // podmień na renderThreeJS, gdy będziesz gotowy
Pułapka, której należy unikać: mieszanie obliczeń fizycznych z
wywołaniami rysowania. Gdy tylko zrobisz
ctx.moveTo(particle.x += vx * dt, ...) wewnątrz pętli
rysowania, migracja zamienia się w pełne przepisanie zamiast
prostej podmiany renderera.
Lista kontrolna decyzji
- ⬜ Symulacja jest z natury dwuwymiarowa? → Canvas 2D
- ⬜ Liczba cząstek < 5 000? → Canvas 2D
-
⬜ Potrzebujesz dostępu do pikseli przez
ImageData? → Canvas 2D - ⬜ Budujesz najpierw prototyp? → Canvas 2D
- ⬜ Scena jest naprawdę 3D? → Three.js
- ⬜ Liczba cząstek > 10 000? → Three.js
- ⬜ Potrzebujesz własnych shaderów GLSL? → Three.js
- ⬜ Potrzebujesz oświetlenia / cieni w czasie rzeczywistym? → Three.js
- ⬜ Potrzebujesz postprocessingu (bloom, SSAO)? → Three.js