-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(typescript): Basic TypeScript support #59
Changes from 13 commits
fc533f8
f4334fd
45d5e6f
ed817db
89c04bf
04a96f9
c1739a6
6773b52
eba50d5
b714712
eb2f85b
56bf9a1
887943b
4ea3947
43215c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,3 +11,4 @@ $RECYCLE.BIN/ | |
package-lock.json | ||
coverage/ | ||
.idea | ||
yarn-error.log |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,21 @@ | ||
{ | ||
"dist/index.js": { | ||
"bundled": 25173, | ||
"minified": 11956, | ||
"gzipped": 4347, | ||
"bundled": 25479, | ||
"minified": 12132, | ||
"gzipped": 4371, | ||
"treeshaked": { | ||
"rollup": { | ||
"code": 9745, | ||
"import_statements": 615 | ||
"code": 9895, | ||
"import_statements": 660 | ||
}, | ||
"webpack": { | ||
"code": 11288 | ||
"code": 11489 | ||
} | ||
} | ||
}, | ||
"dist/index.cjs.js": { | ||
"bundled": 28214, | ||
"minified": 13691, | ||
"gzipped": 4561 | ||
"bundled": 28437, | ||
"minified": 13760, | ||
"gzipped": 4571 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"compilerOptions": { | ||
"target": "es6", | ||
"module": "commonjs", | ||
"moduleResolution": "node", | ||
"jsx": "react", | ||
"pretty": true, | ||
"skipLibCheck": true, | ||
"allowJs": true | ||
}, | ||
"include": ["./src/**/*"], | ||
"exclude": ["./node_modules/**/*"] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,93 @@ | ||
import * as THREE from 'three' | ||
import React, { useRef, useEffect, useMemo, useState, useCallback, useContext } from 'react' | ||
import * as React from 'react' | ||
import { useRef, useEffect, useMemo, useState, useCallback } from 'react' | ||
import ResizeObserver from 'resize-observer-polyfill' | ||
import { invalidate, applyProps, render, unmountComponentAtNode } from './reconciler' | ||
|
||
export const stateContext = React.createContext() | ||
export type CanvasContext = { | ||
canvas?: React.MutableRefObject<any> | ||
subscribers: Array<Function> | ||
frames: 0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should these two be numbers? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. subscribers is an array of callbacks. frames is an int. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I meant to comment on the line below, as the two 0s will be Numeric Literal Types and thus only allow actual 0 as value. |
||
aspect: 0 | ||
gl?: THREE.WebGLRenderer | ||
camera?: THREE.Camera | ||
scene?: THREE.Scene | ||
canvasRect?: DOMRectReadOnly | ||
viewport?: { width: number; height: number } | ||
size?: { left: number; top: number; width: number; height: number } | ||
ready: boolean | ||
manual: boolean | ||
active: boolean | ||
captured: boolean | ||
invalidateFrameloop: boolean | ||
subscribe?: (callback: Function, main: any) => () => any | ||
setManual: (takeOverRenderloop: boolean) => any | ||
setDefaultCamera: (camera: THREE.Camera) => any | ||
invalidate: () => any | ||
} | ||
|
||
export type CanvasProps = { | ||
children: React.ReactNode | ||
gl: THREE.WebGLRenderer | ||
orthographic: THREE.OrthographicCamera | THREE.PerspectiveCamera | ||
raycaster: THREE.Raycaster | ||
camera?: THREE.Camera | ||
style?: React.CSSProperties | ||
pixelRatio?: number | ||
invalidateFrameloop?: boolean | ||
onCreated: Function | ||
} | ||
|
||
export type Measure = [ | ||
{ ref: React.MutableRefObject<any> }, | ||
{ left: number; top: number; width: number; height: number } | ||
] | ||
|
||
export type IntersectObject = Event & | ||
THREE.Intersection & { | ||
ray: THREE.Raycaster | ||
stopped: { current: boolean } | ||
uuid: string | ||
transform: { | ||
x: Function | ||
y: Function | ||
} | ||
} | ||
|
||
const defaultRef = { | ||
ready: false, | ||
subscribers: [], | ||
manual: false, | ||
active: true, | ||
canvas: undefined, | ||
gl: undefined, | ||
camera: undefined, | ||
scene: undefined, | ||
size: undefined, | ||
canvasRect: undefined, | ||
frames: 0, | ||
aspect: 0, | ||
viewport: undefined, | ||
captured: undefined, | ||
invalidateFrameloop: false, | ||
subscribe: (fn, main) => () => {}, | ||
setManual: takeOverRenderloop => {}, | ||
setDefaultCamera: cam => {}, | ||
invalidate: () => {}, | ||
} | ||
|
||
function useMeasure() { | ||
export const stateContext = React.createContext(defaultRef) | ||
|
||
function useMeasure(): Measure { | ||
const ref = useRef() | ||
|
||
const [bounds, set] = useState({ left: 0, top: 0, width: 0, height: 0 }) | ||
const [ro] = useState(() => new ResizeObserver(([entry]) => set(entry.contentRect))) | ||
useEffect(() => { | ||
if (ref.current) ro.observe(ref.current) | ||
return () => ro.disconnect() | ||
}, [ref.current]) | ||
|
||
return [{ ref }, bounds] | ||
} | ||
|
||
|
@@ -28,7 +103,7 @@ export const Canvas = React.memo( | |
invalidateFrameloop = false, | ||
onCreated, | ||
...rest | ||
}) => { | ||
}: CanvasProps) => { | ||
// Local, reactive state | ||
const canvas = useRef() | ||
const [ready, setReady] = useState(false) | ||
|
@@ -50,19 +125,7 @@ export const Canvas = React.memo( | |
|
||
// Public state | ||
const state = useRef({ | ||
ready: false, | ||
subscribers: [], | ||
manual: false, | ||
active: true, | ||
canvas: undefined, | ||
gl: undefined, | ||
camera: undefined, | ||
scene: undefined, | ||
size: undefined, | ||
canvasRect: undefined, | ||
frames: 0, | ||
viewport: undefined, | ||
captured: undefined, | ||
...defaultRef, | ||
subscribe: (fn, main) => { | ||
state.current.subscribers.push(fn) | ||
return () => (state.current.subscribers = state.current.subscribers.filter(s => s !== fn)) | ||
|
@@ -186,10 +249,10 @@ export const Canvas = React.memo( | |
const intersect = useCallback((event, prepare = true) => { | ||
if (prepare) prepareRay(event) | ||
return defaultRaycaster.intersectObjects(state.current.scene.__interaction, true).filter(h => h.object.__handlers) | ||
}) | ||
}, []) | ||
|
||
/** Handles intersections by forwarding them to handlers */ | ||
const handleIntersects = useCallback((event, fn) => { | ||
const handleIntersects = useCallback((event: React.PointerEvent<any>, fn) => { | ||
prepareRay(event) | ||
// If the interaction is captured, take the last known hit instead of raycasting again | ||
const hits = | ||
|
@@ -206,6 +269,7 @@ export const Canvas = React.memo( | |
|
||
for (let hit of hits) { | ||
let stopped = { current: false } | ||
|
||
fn({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given my current knowledge of |
||
...Object.assign({}, event), | ||
...hit, | ||
|
@@ -215,6 +279,7 @@ export const Canvas = React.memo( | |
// Hijack stopPropagation, which just sets a flag | ||
stopPropagation: () => (stopped.current = true), | ||
}) | ||
|
||
if (stopped.current === true) break | ||
} | ||
} | ||
|
@@ -234,7 +299,7 @@ export const Canvas = React.memo( | |
) | ||
|
||
const hovered = useRef({}) | ||
const handlePointerMove = useCallback(event => { | ||
const handlePointerMove = useCallback((event: React.PointerEvent<any>) => { | ||
if (!state.current.ready) return | ||
const hits = handleIntersects(event, data => { | ||
const object = data.object | ||
|
@@ -266,7 +331,7 @@ export const Canvas = React.memo( | |
handlePointerCancel(event, hits) | ||
}, []) | ||
|
||
const handlePointerCancel = useCallback((event, hits) => { | ||
const handlePointerCancel = useCallback((event: React.PointerEvent<any>, hits?: []) => { | ||
if (!hits) hits = handleIntersects(event, () => null) | ||
Object.values(hovered.current).forEach(data => { | ||
if (!hits.length || !hits.find(i => i.object === data.object)) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe enable strict?
Will result in more errors initially, but worth for the long run.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ksjogo just fixed in #86!