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/three@0.162.0/build/three.module.js",
"three/examples/jsm": "https://unpkg.com/three@0.162.0/examples/jsm/",
"@three.ez/main": "https://unpkg.com/@three.ez/main@0.5.1/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.
Like it?
If you find this project helpful, I would greatly appreciate it if you could leave a star on this repository!
This helps me know that you appreciate my work and encourages me to continue improving it.
Thank you so much for your support!