three.ez
Simplify your three.js application development with three.ez!
Extend the functionalities of Object3D
and Scene
classes, making their usage more straightforward, and introduce utility classes.
import { Scene, Mesh, BoxGeometry, MeshNormalMaterial } from 'three';
import { Main, PerspectiveCameraAuto } from '@three.ez/main';
const box = new Mesh(new BoxGeometry(), new MeshNormalMaterial());
box.draggable = true; // make it draggable
box.on('animate', (e) => box.rotateX(e.delta).rotateY(e.delta * 2)); // animate it every frame
box.on(['pointerover', 'pointerout'], (e) => box.scale.setScalar(e.type === 'pointerover' ? 1.5 : 1));
const scene = new Scene().add(box);
const main = new Main(); // init inside the renderer, and handle events, resize, etc
main.createView({ scene, camera: new PerspectiveCameraAuto(70).translateZ(1) }); // create the view to be rendered
This library has only one dependency: three.js r151+
.
✅ Why three.ez?
- Program the logic of your Object3D more quickly and intuitively
- Less code and cleaner
- Streamlined rendering
- Declarative and imperative programming
- Compatible with your three.js code and external libraries
- Easy to learn
- High performance
🔑 Key Features
Event Programming
✨Add interactions to Object3D
through programmable events, similar to DOM events
, including a propagation system.
See events list here: Interaction, Miscellaneous, Update.
const box = new Mesh(geometry, material);
box.on('click', (e) => e.stopPropagation());
box.on('animate', (e) => console.log('animate'));
box.on('positionchange', () => console.log('position changed'));
🔥 Drag and Drop
Integrate drag and drop functionality. The drag is cancelled by pressing ESC.
const box = new Mesh(geometry, material);
box.draggable = true;
box.findDropTarget = true;
box.on('drag', (e) => console.log(`new position: ${e.position}`));
const plane = new Mesh(geometry, material);
plane.on('drop', (e) => console.log(`obj dropped on this: ${e.relatedTarget}`));
🚀 Focus and Blur
Enhance interactivity with focus and blur events.
const box = new Mesh(geometry, material);
box.focusable = true; // default is true
box.on('focus', (e) => console.log('focused'));
box.on('blur', (e) => console.log('focus lost'));
Object3D Property Binding
🏅Streamline the management of Object3D
properties.
const box = new Mesh(geometry, material);
box.bindProperty('visible', () => box.parent?.enabled);
✂️ Automatic Resize Handling
Automatically resizes the Renderer
, Camera
, and EffectComposer
.
Utilize the viewportResize
event to easily set the resolution for custom shaders.
const line = new Line2(geometry, material);
line.on('viewportresize', (e) => material.resolution.set(e.width, e.height));
Smart Rendering
💡Optimize performance by rendering frames only when necessary, reducing computational overhead.
Automatically identifies changes in position, scale, rotation, visibility, focus, blurring and addition or removal of objects.
const scene = new Scene();
scene.activeSmartRendering();
const box = new Mesh(new BoxGeometry(), new MeshLambertMaterial({ color: 'green' }));
box.draggable = true; // if you drag the frame, it automatically detects changes and renders the frame
box.material.color.set('yellow');
box.needsRender = true; // necessary because color change cannot be automatically detected
Simplified Multiple Rendering
🏆Effortlessly manage rendering for multiple scenes or viewports within a single canvas.
const main = new Main();
main.createView({ scene, camera, viewport: { left: 0, bottom: 0, width: 0.5, height: 1 } });
main.createView({ scene, camera, viewport: { left: 0.5, bottom: 0, width: 0.5, height: 1 } });
Asset Management
🛠️Efficiently load and preload the assets for your 3D projects.
load:
const audioBuffer = await Asset.load(AudioLoader, 'audio.mp3', onProgressCallback, onErrorCallback);
preload:
// soldier.js
Asset.preload(GLTFLoader, 'https://threejs.org/examples/models/gltf/Soldier.glb');
export class Soldier extends Group {
constructor() {
super();
const gltf = Asset.get('https://threejs.org/examples/models/gltf/Soldier.glb');
this.add(...gltf.scene.children);
}
}
// main.js
await Asset.preloadAllPending({ onProgress: (e) => console.log(e * 100 + '%'), onError: (e) => console.error(e) });
const main = new Main();
const soldier = new Soldier();
🎥 Tweening
Create smooth animations effortlessly with built-in tweening.
box.tween().by(1000, { position: new Vector3(0, 0.5, 0) }, { easing: 'easeInOutBack' }).yoyoForever().start();
new Tween(box)
.by(2000, { scale: 1, rotation: new Euler(Math.PI * 2, Math.PI, 0) }, { easing: 'easeOutElastic' })
.delay(200)
.to(1000, { scale: 1 }, { easing: 'easeOutBounce' })
.start();
⚙️ Raycasting Customisable
Choose between continuous or mouse movement-based raycasting, optimizing intersection operations.
Set which objects to intersect from the main raycasting.
const scene = new Scene();
scene.continuousRaycasting = true; // default is false
const box = new Mesh(geometry, material);
box.interceptByRaycaster = false; // default is true
🎯 Hitbox Functionality
Leverage hitboxes for customized intersections or simplified calculations.
const ring = new Mesh(new RingGeometry(1, 1.5), new MeshBasicMaterial());
ring.hitboxes = [new Hitbox(new CircleGeometry(1.5))]; // intercept also inside the ring
💯 Simplified InstancedMesh
Manage InstancedMesh
instances with the ease of working with Object3D
, simplifying creation and manipulation, including frustum culling.
const myInstancedMesh = new InstancedMesh2(geometry, material, count, (obj, index) => {
obj.position.x += index;
obj.scale.setScalar(2);
obj.quaternion.random();
obj.forceUpdateMatrix();
});
// How to handle instances
myInstancedMesh.instances[0].visible = false;
myInstancedMesh.instances[1].draggable = true;
myInstancedMesh.instances[2].rotateOnWorldAxis(xAxis, Math.PI);
myInstancedMesh.instances[2].updateMatrix();
🔍 Query
Find and select Object3D
using powerful query selectors.
scene.querySelectorAll('Mesh'); // Selects all the meshes in the scene.
scene.querySelectorAll('[name=box]'); // Selects all Object3D that have 'box' as their name.
scene.querySelectorAll('[name*=box]'); // Selects all Object3D that have 'box' anywhere in their name.
scene.querySelectorAll('Mesh.even'); // Selects meshes with both 'Mesh' type and 'even' tag.
scene.querySelectorAll('Group .even'); // Selects all Object3D with 'even' tag that are children of a 'Group'.
scene.querySelectorAll('Group > .even'); // Selects all direct children with 'even' tag under a 'Group'.
scene.querySelectorAll('Mesh, SkinnedMesh'); // Selects all meshes and skinned meshes in the scene.
⬇️ Installation
You can install it via npm using the following command:
npm install @three.ez/main
Or can import it from CDN:
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/[email protected]/build/three.module.js",
"three/examples/jsm": "https://unpkg.com/[email protected]/examples/jsm/",
"@three.ez/main": "https://unpkg.com/@three.ez/[email protected]/bundle.js"
}
}
</script>
🧑💻 Live Examples
These examples use vite
, and some mobile devices may run out of memory. However, there is one example without it.
- Template — Template Extended — Template No Vite
- Smart Rendering
- Multiple Views — Multiple Views Wireframe — Multiple Views Switch
- Asset Management
- Binding — Binding Collisions
- Events — Click On Scene To Add Box
- Focus — Focus Outline (post-processing)
- Drag & Drop — LOD Draggable — Drag Limits
- Continuous Raycasting
- Hitbox
- Tweening — Tweening Custom Progress
- InstancedMeshEntity — InstancedMeshEntity Performance
- Draggable Box OrbitControls
- Textbox (troika-three-text)
- Bubble Refraction
📚 Documentation
The tutorial is available here (work in progress).
The API documentation is available here.
🤝 Contributing
Any help is highly appreciated. If you would like to contribute to this package or report problems, feel free to open a bug or pull request.
❔ Questions?
If you have questions or need assistance, you can ask on our discord server.