Motion Lab / Galleries / r3f island / minimal
Een low-poly icosahedron in wireframe, traag roterend op een papieren achtergrond. R3F-equivalent in de Mechanisme-tab.
// Mechanisme: gallery-r3f-island-minimal
// Live demo gebruikt vanilla Three.js (lichter dan r3f-bundle).
// React Three Fiber referentie (educatief):
import { Canvas, useFrame } from '@react-three/fiber';
import { useRef } from 'react';
function Shape() {
const ref = useRef();
useFrame((_, dt) => { ref.current.rotation.y += dt * 0.25; });
return (
<mesh ref={ref}>
<icosahedronGeometry args={[1.4, 0]} />
<meshBasicMaterial color="#0A0A0A" wireframe />
</mesh>
);
}
export default () => (
<Canvas camera={{ position:[0,0,5], fov:50 }} dpr={[1,2]}>
<Shape />
</Canvas>
);
// --- VANILLA THREE.JS (LIVE) ---
import * as THREE from 'https://esm.sh/three@0.160.0';
const host = document.querySelector('.r3f-island-mount');
const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
const renderer = new THREE.WebGLRenderer({ antialias:true, alpha:true });
renderer.setPixelRatio(Math.min(devicePixelRatio,2));
host.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 100);
camera.position.z = 5;
const wire = new THREE.LineSegments(
new THREE.WireframeGeometry(new THREE.IcosahedronGeometry(1.6, 0)),
new THREE.LineBasicMaterial({ color: 0x0A0A0A })
);
scene.add(wire);
function fit(){ const r = host.getBoundingClientRect(); renderer.setSize(r.width,r.height,false); camera.aspect = r.width/r.height; camera.updateProjectionMatrix(); }
new ResizeObserver(fit).observe(host); fit();
function tick(){ wire.rotation.x += 0.004; wire.rotation.y += 0.006; renderer.render(scene,camera); requestAnimationFrame(tick); }
if (reduce) renderer.render(scene,camera); else tick(); <!-- Skeleton: gallery-r3f-island-minimal -->
<section class="r3f-island">
<p class="kicker">Motion Lab / Galleries / r3f island / minimal</p>
<div class="r3f-island-mount" data-canvas-host>
<div class="r3f-island-fallback" hidden>
<!-- SVG-fallback alleen bij WebGL-failure -->
</div>
</div>
<h1 class="title">Restrained motion. One object. One rotation.</h1>
</section> /* Styling: gallery-r3f-island-minimal */
:root {
--block-bg: #F4F1EB;
--block-fg: #0A0A0A;
--block-accent: rgba(10,10,10,.4);
}
.r3f-island { min-height: 80vh; background: var(--block-bg); display: grid; place-items: center; }
.r3f-island-mount { position: relative; width: 100%; min-height: 80vh; }
.r3f-island-mount canvas { display: block; width: 100% !important; height: 100% !important; }
.r3f-island-fallback { position: absolute; inset: 0; display: grid; place-items: center; }
.kicker { font-family: 'JetBrains Mono', monospace; font-size: .7rem; letter-spacing: .15em; text-transform: uppercase; color: var(--block-accent); }
.title { font-family: 'Fraunces', Georgia, serif; font-weight: 300; color: var(--block-fg); }