Skip to content

Commit 8ccc62b

Browse files
authored
feat: replace global T for createT factory-function, rename Primitive to Entity and export useProps
2 parents 279ff79 + c406615 commit 8ccc62b

22 files changed

+439
-515
lines changed

README.md

Lines changed: 70 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -6,55 +6,40 @@
66

77
`solid-three` is a port of [react-three-fiber](https://github.com/pmndrs/react-three-fiber) to [solid-js](https://www.solidjs.com/). It allows you to declaratively construct a [Three.js](https://threejs.org/) scene, with reactive primitives, just as you would construct a DOM tree in `solid-js`.
88

9+
- **Declarative `three.js` Components**: Utilize `three.js` objects as JSX components.
10+
- **Reactive Prop Updates**: Properties of 3D objects update reactively, promoting efficient re-renders.
11+
- **Integrated Animation Loop**: `useFrame` hook allows for easy animations.
12+
- **Comprehensive Event System**: Enhanced event handling with support for `three.js` pointer and mouse events.
13+
- **Extensible and Customizable**: Easily extendable with additional `three.js` entities or custom behaviors.
14+
- **Optimized for `solid-js`**: Leverages `solid-js`' fine-grained reactivity for optimal performance.
15+
916
## Table of Contents
1017

11-
1. [Features](#features)
12-
2. [Differences from React-Three-Fiber](#differences-from-react-three-fiber)
13-
3. [Installation](#installation)
14-
4. [Setup with extend()](#setup-with-extend)
15-
5. [Basic Usage](#basic-usage)
16-
6. [Components](#components)
18+
1. [Installation](#installation)
19+
2. [Basic Usage](#basic-usage)
20+
3. [Components](#components)
1721
- [Canvas](#canvas)
18-
- [Primitive Components (`<T/>`)](#primitive-components-t)
22+
- [T Namespace (`<T/>`)](#t-namespace-t)
1923
- [Portal](#portal)
2024
- [Primitive](#primitive)
21-
7. [Hooks](#hooks)
25+
4. [Hooks](#hooks)
2226
- [useThree](#usethree)
2327
- [useFrame](#useframe)
2428
- [useLoader](#useloader)
25-
8. [Event Handling](#event-handling)
29+
5. [Event Handling](#event-handling)
2630
- [Controlling Raycasting with pointerEvents](#controlling-raycasting-with-pointerevents)
2731
- [Supported Events](#supported-events)
2832
- [Event Object](#event-object)
2933
- [Event Propagation](#event-propagation)
3034
- [Missed Events](#missed-events)
3135
- [Hover Events](#hover-events)
32-
9. [TypeScript Support](#typescript-support)
33-
10. [Performance Optimization](#performance-optimization)
34-
11. [Testing](#testing)
35-
12. [API Reference](#api-reference)
36-
13. [Contributing](#contributing)
37-
14. [License](#license)
38-
39-
## Features
40-
41-
- **Declarative `three.js` Components**: Utilize `three.js` objects as JSX components.
42-
- **Reactive Prop Updates**: Properties of 3D objects update reactively, promoting efficient re-renders.
43-
- **Integrated Animation Loop**: `useFrame` hook allows for easy animations.
44-
- **Comprehensive Event System**: Enhanced event handling with support for `three.js` pointer and mouse events.
45-
- **Extensible and Customizable**: Easily extendable with additional `three.js` entities or custom behaviors.
46-
- **Optimized for `solid-js`**: Leverages `solid-js`' fine-grained reactivity for optimal performance.
47-
48-
## Differences from React-Three-Fiber
49-
50-
While `solid-three` is heavily inspired by– and initially shared a lot of code with– react-three-fiber, there are currently several key differences:
51-
52-
- **No `performance` Prop**: The `Canvas` component does not support a `performance` prop as optimization is handled differently in `solid-js`.
53-
- **Simplified Event Objects**: The event object provided to event handlers is more minimalistic.
54-
- **Minimal `useThree` Hook**: Returns a more concise context object, focusing on essential properties.
55-
- **Missed Events**: Implements `onClickMissed`, `onDoubleClickMissed`, and `onContextMenuMissed` for handling events on empty space or stopped propagation.
56-
- **TODO**:
57-
- **No Pointer Capture**: Pointer events do not support pointer capture management.
36+
6. [TypeScript Support](#typescript-support)
37+
7. [Performance Optimization](#performance-optimization)
38+
8. [Testing](#testing)
39+
9. [API Reference](#api-reference)
40+
10. [Differences from React-Three-Fiber](#differences-from-react-three-fiber)
41+
11. [Contributing](#contributing)
42+
12. [License](#license)
5843

5944
## Installation
6045

@@ -72,43 +57,20 @@ yarn add solid-three three
7257

7358
Ensure that `solid-js` is installed in your environment, as it is a peer dependency of `solid-three`.
7459

75-
## Setup with `extend()`
76-
77-
Before using solid-three components, you must register THREE.js objects with the `extend()` function:
78-
79-
```tsx
80-
import { extend } from "solid-three"
81-
import * as THREE from "three"
82-
83-
// Register all THREE.js objects (required step)
84-
extend(THREE)
85-
```
86-
87-
This registration step is required and must be done before rendering any `<T.*>` components. You can also register specific objects, allowing for treeshaking:
88-
89-
```tsx
90-
import { extend } from "solid-three"
91-
import { Mesh, BoxGeometry, MeshBasicMaterial } from "three"
92-
93-
// Register only specific objects
94-
extend({ Mesh, BoxGeometry, MeshBasicMaterial })
95-
```
96-
9760
## Basic Usage
9861

9962
Here's a simple example to get you started:
10063

10164
```tsx
10265
import { Component, createSignal } from "solid-js"
103-
import { Canvas, T, extend, useFrame } from "solid-three"
104-
import { Mesh } from "three"
66+
import { Canvas, createT, useFrame } from "solid-three"
10567
import * as THREE from "three"
10668

107-
// Required: Register THREE.js objects before using them
108-
extend(THREE)
69+
// Create the T namespace with THREE.js objects
70+
const T = createT(THREE)
10971

11072
const Box = () => {
111-
let mesh: Mesh | undefined
73+
let mesh: THREE.Mesh | undefined
11274
const [hovered, setHovered] = createSignal(false)
11375

11476
useFrame(() => (mesh!.rotation.y += 0.01))
@@ -183,19 +145,36 @@ Example with all props:
183145
</Canvas>
184146
```
185147

186-
### Primitive Components (`<T/>`)
148+
### T Namespace (`<T/>`)
187149

188-
`<T/>` components are wrappers around `three.js` objects, allowing you to insert these objects into your scene declaratively. These components are created dynamically based on the `three.js` classes you've registered with `extend()`.
189-
190-
Example:
150+
The `T` namespace contains components that wrap `three.js` objects, allowing you to insert them into your scene declaratively. Before using these components, you must create the namespace using the `createT()` function:
191151

192152
```tsx
153+
import { createT } from "solid-three"
154+
import * as THREE from "three"
155+
156+
// Create T namespace with all THREE.js objects
157+
const T = createT(THREE)
158+
159+
// Now you can use T components
193160
<T.Mesh>
194161
<T.BoxGeometry args={[1, 1, 1]} />
195162
<T.MeshBasicMaterial color="hotpink" />
196163
</T.Mesh>
197164
```
198165

166+
You can also create a namespace with specific objects for better tree-shaking:
167+
168+
```tsx
169+
import { createT } from "solid-three"
170+
import { Mesh, BoxGeometry, MeshBasicMaterial } from "three"
171+
172+
// Create T namespace with only specific objects
173+
const T = createT({ Mesh, BoxGeometry, MeshBasicMaterial })
174+
```
175+
176+
The `T` namespace can be created once and imported throughout your application, or created locally in each file as needed. The components are typed based on the objects you pass to `createT()`, providing full TypeScript support and autocomplete.
177+
199178
#### Advanced Prop Patterns
200179

201180
`solid-three` supports advanced prop attachment patterns for precise control:
@@ -231,7 +210,6 @@ const AdvancedProps = () => {
231210

232211
These patterns automatically trigger `needsUpdate` flags on materials and geometries when necessary.
233212

234-
235213
### Portal
236214

237215
The `Portal` component allows you to place children outside the regular scene graph while maintaining reactive updates. This is useful for rendering objects into different scenes or bypassing the normal parent-child relationships.
@@ -394,7 +372,7 @@ function useLoader<TLoader, TArgs>(
394372

395373
```tsx
396374
import { createSignal } from "solid-js"
397-
import { Canvas, T, useLoader } from "solid-three"
375+
import { Canvas, useLoader } from "solid-three"
398376
import { TextureLoader } from "three"
399377
400378
const TexturedSphere = () => {
@@ -421,7 +399,7 @@ export const App = () => {
421399
### Multiple textures
422400

423401
```tsx
424-
import { Canvas, T, useLoader } from "solid-three"
402+
import { Canvas, useLoader } from "solid-three"
425403
import { TextureLoader } from "three"
426404
427405
const TexturedPlanes = () => {
@@ -716,13 +694,14 @@ solid-three's hover event handling differs from react-three-fiber in several key
716694

717695
```tsx
718696
import type { S3 } from "solid-three"
697+
import type * as THREE from "three"
719698
720699
// Component types
721-
type MeshComponent = S3.Component<Mesh>
722-
type BoxProps = S3.ClassProps<BoxGeometry>
700+
type MeshComponent = S3.Component<THREE.Mesh>
701+
type BoxProps = S3.Props<THREE.BoxGeometry>
723702
724703
// Instance types - `three.js` objects augmented with solid-three metadata
725-
type AugmentedMesh = S3.Instance<Mesh>
704+
type AugmentedMesh = S3.Instance<THREE.Mesh>
726705
727706
// Generic Three instance
728707
type AnyInstance = S3.ThreeInstance
@@ -734,9 +713,11 @@ type Camera = S3.CameraType // PerspectiveCamera | OrthographicCamera
734713
### Props Types
735714

736715
```tsx
716+
import type { S3 } from "solid-three"
717+
737718
// Get props type for a specific `three.js` class
738-
type MeshProps = S3.Props<"Mesh">
739-
type MaterialProps = S3.Props<"MeshStandardMaterial">
719+
type MeshProps = S3.Props<THREE.Mesh>
720+
type MaterialProps = S3.Props<THREE.MeshStandardMaterial>
740721
741722
// Using in components
742723
const MyMesh = (props: MeshProps) => {
@@ -1003,7 +984,6 @@ const SharedResource = () => {
1003984
```tsx
1004985
import { test, TestCanvas } from "solid-three/testing"
1005986
import { render } from "@solidjs/testing-library"
1006-
import { T } from "solid-three"
1007987
1008988
test("renders a mesh", () => {
1009989
const { canvas, scene, unmount, waitTillNextFrame } = test(() => (
@@ -1050,6 +1030,7 @@ import { WebGL2RenderingContext } from "solid-three/testing"
10501030

10511031
```tsx
10521032
import { fireEvent } from "@solidjs/testing-library"
1033+
import { test } from "solid-three/testing"
10531034
10541035
test("handles click events", () => {
10551036
let clicked = false
@@ -1104,6 +1085,9 @@ test("useThree returns context", () => {
11041085
### Testing Animations
11051086

11061087
```tsx
1088+
import { test } from "solid-three/testing"
1089+
import { useFrame } from "solid-three"
1090+
11071091
test("animates on frame", async () => {
11081092
let rotation = 0
11091093
@@ -1124,6 +1108,17 @@ test("animates on frame", async () => {
11241108
})
11251109
```
11261110

1111+
## Differences from React-Three-Fiber
1112+
1113+
While `solid-three` is heavily inspired byand initially shared a lot of code withreact-three-fiber, there are currently several key differences:
1114+
1115+
- **No `performance` Prop**: The `Canvas` component does not support a `performance` prop as optimization is handled differently in `solid-js`.
1116+
- **Simplified Event Objects**: The event object provided to event handlers is more minimalistic.
1117+
- **Minimal `useThree` Hook**: Returns a more concise context object, focusing on essential properties.
1118+
- **Missed Events**: Implements `onClickMissed`, `onDoubleClickMissed`, and `onContextMenuMissed` for handling events on empty space or stopped propagation.
1119+
- **TODO**:
1120+
- **No Pointer Capture**: Pointer events do not support pointer capture management.
1121+
11271122
## Contributing
11281123

11291124
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTION.md) for details on how to get started.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "solid-three",
3-
"version": "0.3.0-next.4",
3+
"version": "0.3.0-next.5",
44
"description": "SolidJS bindings for ThreeJS",
55
"repository": {
66
"type": "git",

playground/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { A, Route, Router } from "@solidjs/router"
22
import type { ParentProps } from "solid-js"
33
import * as THREE from "three"
4-
import { Canvas, extend, T } from "../src/index.ts"
4+
import { Canvas, createT } from "../src/index.ts"
55
import { SimpleSolar } from "./examples/SimpleSolar.tsx"
66
import "./index.css"
77

8-
extend(THREE)
8+
const T = createT(THREE)
99

1010
function Layout(props: ParentProps) {
1111
return (

playground/controls/OrbitControls.tsx

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import { createEffect, createMemo, onCleanup, type Ref } from "solid-js"
22
import type { Event } from "three"
33
import { OrbitControls as ThreeOrbitControls } from "three-stdlib"
44
import { useFrame, useThree, type S3 } from "../../src/index.ts"
5-
import { manageProps } from "../../src/props.ts"
5+
import { useProps } from "../../src/props.ts"
66
import { whenEffect } from "../../src/utils/conditionals.ts"
77
import { processProps } from "./process-props.ts"
88

9-
export type OrbitControlsProps = S3.ClassProps<typeof ThreeOrbitControls> & {
9+
export interface OrbitControlsProps extends S3.Props<typeof ThreeOrbitControls> {
1010
ref?: Ref<ThreeOrbitControls>
1111
camera?: S3.CameraType
1212
domElement?: HTMLElement
@@ -70,15 +70,7 @@ export function OrbitControls(props: OrbitControlsProps) {
7070
onCleanup(() => _controls.removeEventListener("end", callback))
7171
})
7272

73-
manageProps(controls, rest)
73+
useProps(controls, rest)
7474

7575
return null!
7676
}
77-
78-
function createEventListener() {
79-
const callback = config.onStart
80-
if (!callback) return
81-
const _controls = controls()
82-
_controls.addEventListener("start", callback)
83-
onCleanup(() => _controls.removeEventListener("start", callback))
84-
}

playground/controls/default-props.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { MergeProps } from "solid-js"
22
import { mergeProps } from "solid-js"
3-
import type { KeyOfOptionals } from "./type-utils.ts"
3+
import type { KeyOfOptionals } from "./type-utils"
44

55
export function defaultProps<T, K extends KeyOfOptionals<T>>(
66
props: T,

playground/examples/SimpleSolar.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { createEffect, createMemo, createSignal, Show, type ParentProps, type Ref } from "solid-js"
22
import type { Instance } from "src/types.ts"
33
import * as THREE from "three"
4-
import { Canvas, T, useFrame } from "../../src/index.ts"
4+
import { Canvas, createT, Entity, useFrame } from "../../src/index.ts"
55
import { OrbitControls } from "../controls/OrbitControls.tsx"
66

7+
const T = createT(THREE)
8+
79
function OrbitPath(
810
props: ParentProps<{
911
ref?: Ref<THREE.Line>
@@ -45,7 +47,7 @@ function OrbitPath(
4547
}}
4648
>
4749
<T.LineBasicMaterial color="#ffffff" />
48-
<T.Primitive object={curve()} />
50+
<Entity from={curve()} />
4951
{props.children}
5052
</T.Line>
5153
)

src/canvas.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@ import {
66
onCleanup,
77
onMount,
88
} from "solid-js"
9-
import { Camera, OrthographicCamera, Raycaster, Scene, WebGLRenderer } from "three"
9+
import {
10+
Camera,
11+
OrthographicCamera,
12+
PerspectiveCamera,
13+
Raycaster,
14+
Scene,
15+
WebGLRenderer,
16+
} from "three"
1017
import { createThree } from "./create-three.tsx"
1118
import type { EventHandlers, Props } from "./types.ts"
1219

@@ -16,21 +23,21 @@ import type { EventHandlers, Props } from "./types.ts"
1623
export interface CanvasProps extends ParentProps<Partial<EventHandlers>> {
1724
class?: string
1825
/** Configuration for the camera used in the scene. */
19-
camera?: Partial<Props<"PerspectiveCamera"> | Props<"OrthographicCamera">> | Camera
26+
camera?: Partial<Props<PerspectiveCamera> | Props<OrthographicCamera>> | Camera
2027
/** Element to render while the main content is loading asynchronously. */
2128
fallback?: JSX.Element
2229
/** Options for the WebGLRenderer or a function returning a customized renderer. */
2330
gl?:
24-
| Partial<Props<"WebGLRenderer">>
31+
| Partial<Props<WebGLRenderer>>
2532
| ((canvas: HTMLCanvasElement) => WebGLRenderer)
2633
| WebGLRenderer
2734
/** Toggles between Orthographic and Perspective camera. */
2835
orthographic?: boolean
2936
/** Configuration for the Raycaster used for mouse and pointer events. */
30-
raycaster?: Partial<Props<"Raycaster">> | Raycaster
37+
raycaster?: Partial<Props<Raycaster>> | Raycaster
3138
ref?: Ref<HTMLDivElement>
3239
/** Configuration for the Scene instance. */
33-
scene?: Partial<Props<"Scene">> | Scene
40+
scene?: Partial<Props<Scene>> | Scene
3441
/** Custom CSS styles for the canvas container. */
3542
style?: JSX.CSSProperties
3643
/** Enables and configures shadows in the scene. */

0 commit comments

Comments
 (0)