Components
Lunchbox 2 contains:
- Built-in components
- Several automatically-registered ThreeJS components
- The ability to add your own components via
extend
Built-in components
<three-lunchbox> wrapper
The <three-lunchbox> wrapper handles several common use cases for ThreeJS scenes. It automatically creates a scene, WebGLRenderer, and camera, and adds a canvas to the DOM that stretches to fit its container.
Available attributes are:
| Name | Default value | Notes |
|---|---|---|
background | null | Background color of the scene. Any ThreeJS color representation will work. <three-lunchbox background="blue"><three-lunchbox background="#0f0">(etc) |
camera | null | Options to pass to the default camera. Accepts an object that is parsed and whose values are sent to the camera. Nested values can be set by replacing dot notation with a dash. Set camera.position.z to 5:<three-renderer camera='{"position-z": 5}'>Set camera.position to 1, 2, 3:<three-renderer camera='{"position": [1, 2, 3]}'> |
camera-args | [] | Array of args to pass to the camera when it is instantiated. |
dpr | Infinity | Device pixel ratio. Will automatically adjust to the screen's DPR if set to Infinity. |
dispatch-after-render | false | Set to true to dispatch an afterrender CustomEvent after every render. See <three-lunchbox> events. |
dispatch-before-render | false | Set to true to dispatch a beforerender CustomEvent before every render. See <three-lunchbox> events. |
headless | false | Set to true to prevent automatic WebGLRenderer initialization. Useful in unit tests, for example. |
manual-render | false | Set to true to prevent automatic rendering. Note you'll need to call renderThree() yourself if this is the case. |
renderer | null | Options to pass to the default renderer. Accepts an object that is parsed and whose values are sent to the renderer. See camera for formatting. |
renderer-args | [] | Array of args to pass to the renderer when it is instantiated. |
scene | null | Options to pass to the default scene. Accepts an object that is parsed and whose values are sent to the scene. See camera for formatting. |
<three-lunchbox> Events
List of events:
| Name | Properties | Notes |
|---|---|---|
three-ready | { lunchbox: /** The three-lunchbox element */ } | Emitted when the scene, renderer, and camera are created |
Note that the events emitted by <three-lunchbox> are CustomEvents, so the properties listed below are contained in a detail property. For example:
const lunchbox = document.createElement('three-lunchbox') as ThreeLunchbox;
lunchbox.addEventListener('three-ready', (evt: CustomEvent<{ lunchbox: ThreeLunchbox }>) => {
console.log('lunchbox:', evt.detail.lunchbox);
});The three property of <three-lunchbox>
A three-lunchbox component contains a property called three with ThreeJS details. For example:
const lunchbox = document.querySelector('three-lunchbox');
console.log(lunchbox.three);Current methods/properties of three are:
| Property | Notes |
|---|---|
camera | The component's camera. |
renderer | The component's renderer. |
scene | The component's scene. |
In TypeScript, you can get type completion by querying the ThreeLunchbox type:
import { type ThreeLunchbox } from 'lunchboxjs';
const lunchbox = document.querySelector<ThreeLunchbox>('three-lunchbox');
console.log(lunchbox?.three);Customizing your lunchbox
See here for instructions on customizing Lunchbox's scene, camera, or renderer.
Other built-in <three-lunchbox> properties and methods
Other available properties and methods include:
| Property | Notes |
|---|---|
renderThree(scene?, camera?) | Manually render the default scene with the default camera. Optionally pass a custom override scene and camera. |
TypeScript example:
import { type ThreeLunchbox } from 'lunchboxjs';
const lunchbox = document.querySelector<ThreeLunchbox>('three-lunchbox');
// manually render
lunchbox?.renderThree();<html-anchor>
<html-anchor> makes the .position of its parent available as CSS vars (--left and --top, from the top left of the canvas).
For example, you can create this:
with this:
<three-lunchbox style="position: relative;">
<three-mesh position-z="-5">
<html-anchor>
<!-- `--left` and `--top` are set automatically by <html-anchor> -->
<div style="position: absolute; left: var(--left); top: var(--top);">
HTML Label
</div>
</html-anchor>
<torus-knot-geometry></torus-knot-geometry>
<mesh-normal-material></mesh-normal-material>
</three-mesh>
</three-lunchbox><html-anchor> will also have a class called in-frustum present if its 3D parent point is in the frustum.
Full list of CSS vars set on <html-anchor> (and therefore provided to children):
| CSS var name/class | Notes |
|---|---|
--distance-from-camera | Distance from object to camera. Lower value = closer, which corresponds to a higher z-index in CSS. When subtracted from a larger value and rounded, this can be used to correctly layer DOM elements based on their 3D parents. Example using CSS round():z-index: round(1000 - var(--distance-from-camera) * 100); |
--in-frustum | Whether the 3D parent's position is present in the main camera's frustum. 0 if not present or 1 if present.DOM elements will still appear onscreen if they're directly behind the camera, so you can use this var to determine if they should be visible and interactive or not. (Note you can also use the presence or absence of the class in-frustum on the <html-anchor> element.) |
in-frustum | Class name added to <html-anchor> when the target position is in the camera's frustum |
--left | Pixels from the left of the main canvas |
--top | Pixels from the top of the main canvas |
Auto-registered components
All ThreeJS classes listed here can be used out of the box with Lunchbox 2. Elements must be separated by a dash if the class name is two or more words:
<!-- right -->
<box-geometry></box-geometry>
<!-- wrong -->
<boxGeometry></boxGeometry>If the class name is one word, prepend three- to the element name:
<!-- right -->
<three-mesh></three-mesh>
<!-- wrong -->
<mesh></mesh>See core concepts for attribute notes.
Universal component attributes
All components registered or extended through Lunchbox 2 have the following attributes:
| Name | Default value | Notes |
|---|---|---|
try-add-once | By default, components poll for a suitable parent element (ie, one with an instance or three.scene property) every frame until they find a parent. Set this attribute to true to only try adding once on insertion into the DOM. |
The instance property
All auto-registered components and components created by extend (see below) contain an instance property that holds the underlying ThreeJS object. For example:
<box-geometry></box-geometry>
<script>
const boxGeometry = document.querySelector('box-geometry');
console.log(boxGeometry.instance); // logs the BoxGeometry held by the component
</script>In TypeScript, this you can get type completion with the Lunchbox generic type:
import { type Lunchbox } from 'lunchboxjs';
import * as THREE from 'three';
const boxGeometry = document.querySelector<Lunchbox<THREE.BoxGeometry>>('box-geometry');
console.log(boxGeometry?.instance); // logs the BoxGeometry held by the componentYou can do anything with an instance that you would do with a standard ThreeJS object - for example:
const mesh = document.querySelector('three-mesh');
// add a child to the selected mesh
mesh.instance.add(new THREE.Mesh(
new THREE.BoxGeometry(),
new THREE.MeshBasicMaterial(),
));This is a contrived example, since it would usually be easier to add a child via another <three-mesh> element; doing so also handles disposal automatically when removing the element from the DOM, while in the example above you would need to handle disposal manually.
Accessing instance is most useful when handling animations or large quantities of components/updates that would otherwise be expensive to add in the DOM.
Custom components via extend
You can add your own components via the extend command. For example, a common use case is to add OrbitControls:
import { extend } from 'lunchboxjs';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
extend('orbit-controls', OrbitControls);Now you can add an <orbit-controls> component:
<three-lunchbox camera="{ 'position-z': 5 }">
<three-mesh>
<torus-knot-geometry></torus-knot-geometry>
<mesh-normal-material></mesh-normal-material>
</three-mesh>
<orbit-controls args='["$camera", "$domElement"]'></orbit-controls>
</three-lunchbox>See here for notes on $camera and $renderer.