Als het gaat om realtime 3D-graphics in de browser, is Three.js de eerste bibliotheek die in je opkomt; in dit artikel bouwen we Three.js-animatie-logica vanaf nul om een levende scène te maken die draait, licht opvangt en reageert op interactie van de gebruiker. Het doel is niet alleen een kubus laten draaien, maar begrijpen hoe de renderlus werkt, waarom de tijd tussen frames belangrijk is en hoe je de scène performant houdt. Aan het eind heb je een startsjabloon dat in één HTML-bestand draait en nooit frames laat vallen.
De drie bouwstenen van een Three.js-scène
Elk Three.js-project draait om drie objecten: de Scene, de Camera en de Renderer. De scène is de container die alles bevat wat zichtbaar is. De camera bepaalt vanuit welke hoek we naar die scène kijken. De renderer gebruikt WebGL om het resultaat op een <canvas> te tekenen.
- Scene: de wortel van de grafische boom die objecten, lichten en camera's bevat.
- PerspectiveCamera: een camera die, net als het menselijk oog, verre objecten kleiner laat lijken en een gevoel van diepte geeft.
- WebGLRenderer: draait op de GPU en hertekent de scène bij elk frame.
De bibliotheek als module importeren is de schoonste aanpak. Moderne browsers kunnen een ES-module rechtstreeks van een CDN ophalen met een import map.
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
</script>
De eerste scène opzetten
Laten we nu de scène, de camera en de renderer maken. De cameraparameters zijn, in volgorde: gezichtsveld (fov, in graden), beeldverhouding, nabij-clipvlak en ver-clipvlak. Om de renderer aan de pagina toe te voegen, plaatsen we de eigenschap domElement in de DOM.
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0b1020);
const camera = new THREE.PerspectiveCamera(
60, window.innerWidth / window.innerHeight, 0.1, 100
);
camera.position.z = 4;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
setPixelRatio op 2 begrenzen is belangrijk: op schermen met een hoge dichtheid houdt het beperken van de pixelverhouding de resolutie redelijk, ontlast het de GPU en vermindert het de warmteontwikkeling op mobiel.
Object, materiaal en licht
In Three.js is een zichtbaar object (mesh) de combinatie van een geometrie (vorm) en een materiaal (oppervlakteweergave). MeshStandardMaterial is een fysisch gebaseerd materiaal dat licht nodig heeft om realistisch te ogen; daarom blijft het scherm pikzwart tot je minstens één licht aan de scène toevoegt.
const geometry = new THREE.IcosahedronGeometry(1, 0);
const material = new THREE.MeshStandardMaterial({
color: 0x4f9dde,
roughness: 0.3,
metalness: 0.6,
flatShading: true,
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
const keyLight = new THREE.DirectionalLight(0xffffff, 2);
keyLight.position.set(3, 4, 5);
scene.add(keyLight);
scene.add(new THREE.AmbientLight(0x404060, 1.5));
Hier gebruiken we een gericht licht (als de zon, met een duidelijke richting en harde schaduwen) samen met een omgevingslicht (gelijk uit alle richtingen, een opvulling die schaduwen verzacht). Dit duo geeft een eenvoudige maar evenwichtige belichting.
De renderlus en tijdgebaseerde animatie
Het hart van animatie is de renderlus. requestAnimationFrame zegt tegen de browser: "roep me aan wanneer je klaar bent voor de volgende tekening"; dit gebeurt meestal 60 keer per seconde, synchroon met de verversingssnelheid van het scherm. De naïeve aanpak voegt elk frame een vaste waarde toe, maar dat is fout: het draait traag op een 60 Hz-scherm en snel op een 144 Hz-scherm. De juiste manier is vermenigvuldigen met de verstreken tijd (delta).
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta(); // verstreken tijd in seconden
mesh.rotation.y += delta * 0.8; // 0,8 radiaal per seconde
mesh.rotation.x += delta * 0.3;
renderer.render(scene, camera);
}
animate();
THREE.Clock geeft de werkelijke tijd die tussen frames verstrijkt. Door de rotatiesnelheid met deze waarde te vermenigvuldigen, loopt de animatie op elk apparaat even snel, onafhankelijk van de verversingssnelheid van het scherm. Dit is een van de meest zichtbare verschillen tussen professioneel en amateurwerk.
Aanpassen aan vensterformaat en interactie
Zodat het beeld niet vervormt wanneer de gebruiker het venster vergroot of verkleint, moeten we de camera en de renderer bijwerken. Wanneer de beeldverhouding van de camera verandert, is een aanroep van updateProjectionMatrix verplicht.
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
Voor interactie is de snelste weg de officiële OrbitControls-add-on, waarmee je de scène met de muis kunt draaien en zoomen. Voeg het toe aan de import map en zet controls.update() in de lus. Wil je een lichter gevoel, dan kun je de muispositie koppelen aan een subtiele cameraverschuiving (een parallaxeffect).
Tips voor prestaties en opruimen
- Pauzeer wanneer het tabblad op de achtergrond staat:
requestAnimationFramevertraagt al wanneer de pagina verborgen is, maar voor zware scènes spaart het volledig stoppen van de lus met eendocument.hidden-controle de batterij. - Hergebruik geometrie en materiaal: als je honderden kopieën van dezelfde vorm nodig hebt, tekent
InstancedMeshze allemaal in één draw call. - Let op geheugenlekken: wanneer je een object permanent verwijdert, roep dan
geometry.dispose()enmaterial.dispose()aan; GPU-geheugen wordt niet automatisch vrijgegeven. - Gebruik schaduwen spaarzaam: realtime schaduwkaarten zijn duur; houd ze uit als je ze niet nodig hebt.
Veelgestelde vragen
Moet ik WebGL kennen om Three.js te leren?
Nee. Three.js abstraheert de complexe shader- en bufferdetails van WebGL weg; om te beginnen zijn JavaScript en de basis 3D-concepten (coördinaten, vectoren, camera's) voldoende. WebGL-kennis wordt later een voordeel wanneer je eigen shaders wilt schrijven, maar het is niet vereist.
Waarom draait mijn animatie zo snel op sommige computers?
Hoogstwaarschijnlijk verhoog je de rotatie met een vast getal. Op schermen met een hoge verversingssnelheid (120/144 Hz) loopt de lus vaker en versnelt de beweging. De oplossing is de beweging te vermenigvuldigen met clock.getDelta(), dus tijdgebaseerde animatie te gebruiken.
Werkt Three.js goed op mobiele apparaten?
Ja, maar je moet voorzichtig zijn. Begrens setPixelRatio, houd het aantal polygonen laag, vermijd zware post-processing-effecten en beperk schaduwen. Met deze maatregelen kun je vloeiende 3D-scènes draaien, zelfs op middenklasse-telefoons.
Droom je van een levende 3D-ervaring in de browser? Van productconfigurators tot interactieve achtergronden: ik ontwerp Three.js-gebaseerde scènes en breng ze met aandacht voor prestaties tot leven. Om over je project te praten, neem contact met me op.