lunchboxjs 0.2.1020 → 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/lunchboxjs.cjs +46 -0
  2. package/dist/lunchboxjs.d.ts +1 -0
  3. package/dist/lunchboxjs.js +1632 -1962
  4. package/dist/lunchboxjs.umd.cjs +46 -0
  5. package/package.json +36 -81
  6. package/LICENSE.md +0 -7
  7. package/README.md +0 -17
  8. package/dist/lunchboxjs.es.d.ts +0 -1
  9. package/dist/lunchboxjs.min.js +0 -1
  10. package/dist/lunchboxjs.module.js +0 -1924
  11. package/dist/lunchboxjs.umd.d.ts +0 -1
  12. package/src/components/LunchboxEventHandlers.tsx +0 -237
  13. package/src/components/LunchboxWrapper/LunchboxScene.tsx +0 -8
  14. package/src/components/LunchboxWrapper/LunchboxWrapper.tsx +0 -341
  15. package/src/components/LunchboxWrapper/prepCanvas.ts +0 -55
  16. package/src/components/LunchboxWrapper/resizeCanvas.ts +0 -41
  17. package/src/components/autoGeneratedComponents.ts +0 -175
  18. package/src/components/index.ts +0 -31
  19. package/src/core/createNode.ts +0 -71
  20. package/src/core/extend.ts +0 -25
  21. package/src/core/index.ts +0 -7
  22. package/src/core/instantiateThreeObject/index.ts +0 -37
  23. package/src/core/instantiateThreeObject/processProps.ts +0 -40
  24. package/src/core/interaction.ts +0 -55
  25. package/src/core/minidom.ts +0 -256
  26. package/src/core/update.ts +0 -149
  27. package/src/core/updateObjectProp.ts +0 -153
  28. package/src/index.ts +0 -400
  29. package/src/keys.ts +0 -31
  30. package/src/nodeOps/createElement.ts +0 -34
  31. package/src/nodeOps/index.ts +0 -83
  32. package/src/nodeOps/insert.ts +0 -165
  33. package/src/nodeOps/remove.ts +0 -32
  34. package/src/plugins/bridge/BridgeComponent.tsx +0 -60
  35. package/src/plugins/bridge/bridge.ts +0 -9
  36. package/src/types.ts +0 -186
  37. package/src/utils/find.ts +0 -24
  38. package/src/utils/get.ts +0 -18
  39. package/src/utils/index.ts +0 -60
  40. package/src/utils/isNumber.ts +0 -87
  41. package/src/utils/set.ts +0 -14
@@ -1 +0,0 @@
1
- export * from "../src/index"
@@ -1,237 +0,0 @@
1
- import { defineComponent, onBeforeUnmount, ref, watch } from 'vue'
2
- import {
3
- useCamera,
4
- Lunch,
5
- useGlobals,
6
- useLunchboxInteractables,
7
- onRendererReady,
8
- } from '..'
9
- import * as THREE from 'three'
10
- import { offBeforeRender, onBeforeRender } from '../core'
11
-
12
- export const LunchboxEventHandlers = defineComponent({
13
- name: 'LunchboxEventHandlers',
14
- setup() {
15
- const interactables = useLunchboxInteractables()
16
- const globals = useGlobals()
17
- const mousePos = ref({ x: Infinity, y: Infinity })
18
- const inputActive = ref(false)
19
-
20
- let currentIntersections: Array<{
21
- element: Lunch.Node
22
- intersection: THREE.Intersection<THREE.Object3D>
23
- }> = []
24
-
25
- const raycaster = new THREE.Raycaster(
26
- new THREE.Vector3(),
27
- new THREE.Vector3(0, 0, -1)
28
- )
29
-
30
- const fireEventsFromIntersections = ({
31
- element,
32
- eventKeys,
33
- intersection,
34
- }: {
35
- element: Lunch.Node
36
- eventKeys: Array<Lunch.EventKey>
37
- intersection: THREE.Intersection<THREE.Object3D>
38
- }) => {
39
- if (!element) return
40
- eventKeys.forEach((eventKey) => {
41
- if (element.eventListeners[eventKey]) {
42
- element.eventListeners[eventKey].forEach((cb) => {
43
- cb({ intersection })
44
- })
45
- }
46
- })
47
- }
48
-
49
- // add mouse listener to renderer DOM element when the element is ready
50
- onRendererReady((v) => {
51
- if (!v?.domElement) return
52
-
53
- // we have a DOM element, so let's add mouse listeners
54
- const { domElement } = v
55
-
56
- const mouseMoveListener = (evt: PointerEvent) => {
57
- const screenWidth = (domElement.width ?? 1) / globals.dpr
58
- const screenHeight = (domElement.height ?? 1) / globals.dpr
59
- mousePos.value.x = (evt.offsetX / screenWidth) * 2 - 1
60
- mousePos.value.y = -(evt.offsetY / screenHeight) * 2 + 1
61
- }
62
- const mouseDownListener = () => (inputActive.value = true)
63
- const mouseUpListener = () => (inputActive.value = false)
64
-
65
- // add mouse events
66
- domElement.addEventListener('pointermove', mouseMoveListener)
67
- domElement.addEventListener('pointerdown', mouseDownListener)
68
- domElement.addEventListener('pointerup', mouseUpListener)
69
- })
70
-
71
- const camera = useCamera()
72
- const update = () => {
73
- const c = camera.value
74
- if (!c) return
75
-
76
- // console.log(camera.value)
77
-
78
- raycaster.setFromCamera(mousePos.value, c)
79
- const intersections = raycaster.intersectObjects(
80
- interactables?.value.map(
81
- (v) => v.instance as any as THREE.Object3D
82
- ) ?? []
83
- )
84
-
85
- let enterValues: Array<THREE.Intersection<THREE.Object3D>> = [],
86
- sameValues: Array<THREE.Intersection<THREE.Object3D>> = [],
87
- leaveValues: Array<THREE.Intersection<THREE.Object3D>> = [],
88
- entering: Array<{
89
- element: Lunch.Node
90
- intersection: THREE.Intersection<THREE.Object3D>
91
- }> = [],
92
- staying: Array<{
93
- element: Lunch.Node
94
- intersection: THREE.Intersection<THREE.Object3D>
95
- }> = []
96
-
97
- // intersection arrays
98
- leaveValues = currentIntersections.map((v) => v.intersection)
99
-
100
- // element arrays
101
- intersections?.forEach((intersection) => {
102
- const currentIdx = currentIntersections.findIndex(
103
- (v) => v.intersection.object === intersection.object
104
- )
105
- if (currentIdx === -1) {
106
- // new intersection
107
- enterValues.push(intersection)
108
-
109
- const found = interactables?.value.find(
110
- (v) => v.instance?.uuid === intersection.object.uuid
111
- )
112
- if (found) {
113
- entering.push({ element: found, intersection })
114
- }
115
- } else {
116
- // existing intersection
117
- sameValues.push(intersection)
118
-
119
- const found = interactables?.value.find(
120
- (v) => v.instance?.uuid === intersection.object.uuid
121
- )
122
- if (found) {
123
- staying.push({ element: found, intersection })
124
- }
125
- }
126
- // this is a current intersection, so it won't be in our `leave` array
127
- const leaveIdx = leaveValues.findIndex(
128
- (v) => v.object.uuid === intersection.object.uuid
129
- )
130
- if (leaveIdx !== -1) {
131
- leaveValues.splice(leaveIdx, 1)
132
- }
133
- })
134
-
135
- const leaving: Array<{
136
- element: Lunch.Node
137
- intersection: THREE.Intersection<THREE.Object3D>
138
- }> = leaveValues.map((intersection) => {
139
- return {
140
- element: interactables?.value.find(
141
- (interactable) =>
142
- interactable.instance?.uuid ===
143
- intersection.object.uuid
144
- ) as any as Lunch.Node,
145
- intersection,
146
- }
147
- })
148
-
149
- // new interactions
150
- entering.forEach(({ element, intersection }) => {
151
- fireEventsFromIntersections({
152
- element,
153
- eventKeys: ['onPointerEnter'],
154
- intersection,
155
- })
156
- })
157
-
158
- // unchanged interactions
159
- staying.forEach(({ element, intersection }) => {
160
- const eventKeys: Array<Lunch.EventKey> = [
161
- 'onPointerOver',
162
- 'onPointerMove',
163
- ]
164
- fireEventsFromIntersections({
165
- element,
166
- eventKeys,
167
- intersection,
168
- })
169
- })
170
-
171
- // exited interactions
172
- leaving.forEach(({ element, intersection }) => {
173
- const eventKeys: Array<Lunch.EventKey> = [
174
- 'onPointerLeave',
175
- 'onPointerOut',
176
- ]
177
- fireEventsFromIntersections({
178
- element,
179
- eventKeys,
180
- intersection,
181
- })
182
- })
183
-
184
- currentIntersections = ([] as any).concat(entering, staying)
185
- }
186
-
187
- // update function
188
- onBeforeRender(update)
189
-
190
- const teardown = () => offBeforeRender(update)
191
- onBeforeUnmount(teardown)
192
-
193
- const clickEventKeys: Lunch.EventKey[] = [
194
- 'onClick',
195
- 'onPointerDown',
196
- 'onPointerUp',
197
- ]
198
- watch(inputActive, (isDown) => {
199
- // run raycaster on click (necessary when `update` is not automatically called,
200
- // for example in `updateSource` functions)
201
- update()
202
-
203
- // meshes with multiple intersections receive multiple callbacks by default -
204
- // let's make it so they only receive one callback of each type per frame.
205
- // (ie usually when you click on a mesh, you expect only one click event to fire, even
206
- // if there are technically multiple intersections with that mesh)
207
- const uuidsInteractedWithThisFrame: string[] = []
208
- currentIntersections.forEach((v) => {
209
- clickEventKeys.forEach((key) => {
210
- const id = v.element.uuid + key
211
- if (
212
- isDown &&
213
- (key === 'onClick' || key === 'onPointerDown')
214
- ) {
215
- if (!uuidsInteractedWithThisFrame.includes(id)) {
216
- v.element.eventListeners[key]?.forEach((cb) =>
217
- cb({ intersection: v.intersection })
218
- )
219
- uuidsInteractedWithThisFrame.push(id)
220
- }
221
- } else if (!isDown && key === 'onPointerUp') {
222
- if (!uuidsInteractedWithThisFrame.includes(id)) {
223
- v.element.eventListeners[key]?.forEach((cb) =>
224
- cb({ intersection: v.intersection })
225
- )
226
- uuidsInteractedWithThisFrame.push(id)
227
- }
228
- }
229
- })
230
- })
231
- })
232
-
233
- // return arbitrary object to ensure instantiation
234
- // TODO: why can't we return a <raycaster/> here?
235
- return () => <object3D />
236
- },
237
- })
@@ -1,8 +0,0 @@
1
- import { defineComponent } from 'vue'
2
-
3
- export const LunchboxScene = defineComponent({
4
- name: 'LunchboxScene',
5
- setup(props, { slots }) {
6
- return () => <scene>{slots.default?.()}</scene>
7
- },
8
- })
@@ -1,341 +0,0 @@
1
- import {
2
- computed,
3
- defineComponent,
4
- onBeforeUnmount,
5
- onMounted,
6
- PropType,
7
- reactive,
8
- ref,
9
- watch,
10
- WatchSource,
11
- } from 'vue'
12
- import { cancelUpdate, cancelUpdateSource, MiniDom, update } from '../../core'
13
- import { Lunch, useApp, useGlobals, useLunchboxInteractables } from '../..'
14
- import * as THREE from 'three'
15
- import { prepCanvas } from './prepCanvas'
16
- import { useUpdateGlobals, useStartCallbacks } from '../..'
17
- import { LunchboxScene } from './LunchboxScene'
18
- import { LunchboxEventHandlers } from '../LunchboxEventHandlers'
19
- import * as Keys from '../../keys'
20
- import { waitFor } from '../../utils'
21
-
22
- /** fixed & fill styling for container */
23
- const fillStyle = (position: string) => {
24
- return {
25
- position,
26
- top: 0,
27
- right: 0,
28
- bottom: 0,
29
- left: 0,
30
- width: '100%',
31
- height: '100%',
32
- display: 'block',
33
- }
34
- }
35
-
36
- export const LunchboxWrapper = defineComponent({
37
- name: 'Lunchbox',
38
- props: {
39
- // These should match the Lunchbox.WrapperProps interface
40
- background: String,
41
- cameraArgs: Array,
42
- cameraLook: Array as unknown as PropType<Lunch.Vector3AsArray>,
43
- cameraLookAt: Array as unknown as PropType<Lunch.Vector3AsArray>,
44
- cameraPosition: Array as unknown as PropType<Lunch.Vector3AsArray>,
45
- dpr: Number,
46
- ortho: Boolean,
47
- orthographic: Boolean,
48
- r3f: Boolean,
49
- rendererArguments: Object,
50
- rendererProperties: Object,
51
- sizePolicy: String as PropType<Lunch.SizePolicy>,
52
- shadow: [Boolean, Object],
53
- transparent: Boolean,
54
- zoom: Number,
55
- updateSource: Object as PropType<WatchSource>,
56
- },
57
- setup(props, context) {
58
- const canvas = ref<MiniDom.RendererDomNode>()
59
- let dpr = props.dpr ?? -1
60
- const container = ref<MiniDom.RendererDomNode>()
61
- const renderer = ref<Lunch.LunchboxComponent<THREE.Renderer>>()
62
- const camera = ref<Lunch.LunchboxComponent<THREE.Camera>>()
63
- const scene = ref<Lunch.LunchboxComponent<THREE.Scene>>()
64
- const globals = useGlobals()
65
- const updateGlobals = useUpdateGlobals()
66
- const app = useApp()
67
- const consolidatedCameraProperties: Record<string, any> = reactive({})
68
- const startCallbacks = useStartCallbacks()
69
-
70
- // https://threejs.org/docs/index.html#manual/en/introduction/Color-management
71
- if (props.r3f && (THREE as any)?.ColorManagement) {
72
- ;(THREE as any).ColorManagement.legacyMode = false
73
- }
74
-
75
- const interactables = useLunchboxInteractables()
76
-
77
- // MOUNT
78
- // ====================
79
- onMounted(async () => {
80
- // canvas needs to exist (or user needs to handle it on their own)
81
- if (!canvas.value && !context.slots?.renderer?.()?.length)
82
- throw new Error('missing canvas')
83
-
84
- // no camera provided, so let's create one
85
- if (!context.slots?.camera?.()?.length) {
86
- if (props.cameraPosition) {
87
- consolidatedCameraProperties.position = props.cameraPosition
88
- }
89
- if (props.cameraLook || props.cameraLookAt) {
90
- consolidatedCameraProperties.lookAt =
91
- props.cameraLook || props.cameraLookAt
92
- }
93
- if (props.zoom !== undefined) {
94
- consolidatedCameraProperties.zoom = props.zoom
95
- }
96
- }
97
-
98
- // SCENE
99
- // ====================
100
- // set background color
101
- if (scene.value?.$el?.instance && props.background) {
102
- scene.value.$el.instance.background = new THREE.Color(
103
- props.background
104
- )
105
- }
106
-
107
- // MISC PROPERTIES
108
- // ====================
109
- if (dpr === -1) {
110
- dpr = window.devicePixelRatio
111
- }
112
- updateGlobals?.({ dpr })
113
-
114
- while (
115
- !renderer.value?.$el?.instance &&
116
- // TODO: remove `as any`
117
- !(renderer.value as any)?.component?.ctx.$el?.instance
118
- ) {
119
- await new Promise((r) => requestAnimationFrame(r))
120
- }
121
-
122
- while (
123
- !scene.value?.$el?.instance &&
124
- // TODO: remove `as any`
125
- !(scene.value as any)?.component?.ctx.$el?.instance
126
- ) {
127
- await new Promise((r) => requestAnimationFrame(r))
128
- }
129
-
130
- const normalizedRenderer = (renderer.value?.$el?.instance ??
131
- (renderer.value as any)?.component?.ctx.$el
132
- ?.instance) as THREE.WebGLRenderer
133
-
134
- normalizedRenderer.setPixelRatio(globals.dpr)
135
-
136
- const normalizedScene = (scene.value?.$el?.instance ??
137
- (scene.value as any)?.component?.ctx.$el
138
- ?.instance) as THREE.Scene
139
-
140
- const normalizedCamera = (camera.value?.$el?.instance ??
141
- (camera.value as any)?.component?.ctx.$el
142
- ?.instance) as THREE.Camera
143
-
144
- // TODO: update DPR on monitor switch
145
- // prep canvas (sizing, observe, unmount, etc)
146
- // (only run if no custom renderer)
147
- if (!context.slots?.renderer?.()?.length) {
148
- // TODO: use dispose
149
- const { dispose } = prepCanvas(
150
- container,
151
- normalizedCamera,
152
- normalizedRenderer,
153
- normalizedScene,
154
- props.sizePolicy
155
- )
156
-
157
- if (props.r3f) {
158
- normalizedRenderer.outputEncoding = THREE.sRGBEncoding
159
- normalizedRenderer.toneMapping = THREE.ACESFilmicToneMapping
160
- }
161
-
162
- // update render sugar
163
- const sugar = {
164
- shadow: props.shadow,
165
- }
166
- if (sugar?.shadow) {
167
- normalizedRenderer.shadowMap.enabled = true
168
- if (typeof sugar.shadow === 'object') {
169
- normalizedRenderer.shadowMap.type = sugar.shadow.type
170
- }
171
- }
172
- }
173
-
174
- // START
175
- // ====================
176
- if (!app) {
177
- throw new Error('error creating app')
178
- }
179
-
180
- // save renderer, scene, camera
181
- app.config.globalProperties.lunchbox.camera = normalizedCamera
182
- app.config.globalProperties.lunchbox.renderer = normalizedRenderer
183
- app.config.globalProperties.lunchbox.scene = normalizedScene
184
-
185
- for (let startCallback of startCallbacks ?? []) {
186
- startCallback({
187
- app,
188
- camera: normalizedCamera,
189
- renderer: normalizedRenderer,
190
- scene: normalizedScene,
191
- })
192
- }
193
-
194
- // KICK UPDATE
195
- // ====================
196
- update({
197
- app,
198
- camera: normalizedCamera,
199
- renderer: normalizedRenderer,
200
- scene: normalizedScene,
201
- updateSource: props.updateSource,
202
- })
203
- })
204
-
205
- // UNMOUNT
206
- // ====================
207
- onBeforeUnmount(() => {
208
- cancelUpdate()
209
- cancelUpdateSource()
210
- })
211
-
212
- // RENDER FUNCTION
213
- // ====================
214
- const containerFillStyle =
215
- props.sizePolicy === 'container' ? 'static' : 'absolute'
216
- const canvasFillStyle =
217
- props.sizePolicy === 'container' ? 'static' : 'fixed'
218
-
219
- // REACTIVE CUSTOM CAMERAS
220
- // ====================
221
- // find first camera with `type.name` property
222
- // (which indicates a Lunch.Node)
223
- const activeCamera = computed(() => {
224
- const output = context.slots
225
- ?.camera?.()
226
- .find((c) => (c.type as any)?.name)
227
- if (output) {
228
- return output
229
- }
230
-
231
- return output
232
- })
233
-
234
- // TODO: make custom cameras reactive
235
- watch(
236
- activeCamera,
237
- async (newVal, oldVal) => {
238
- // console.log('got camera', newVal)
239
- if (newVal && newVal?.props?.key !== oldVal?.props?.key) {
240
- // TODO: remove cast
241
- camera.value = newVal as any
242
-
243
- // TODO: why isn't this updating app camera?
244
- // const el = await waitFor(() => newVal.el)
245
- // console.log(el)
246
- // camera.value = el
247
- // console.log(newVal.uuid)
248
- // updateGlobals?.({ camera: el })
249
- }
250
- },
251
- { immediate: true }
252
- )
253
-
254
- // RENDER FUNCTION
255
- // ====================
256
- return () => (
257
- <>
258
- {/* use renderer slot if provided... */}
259
- {context.slots?.renderer?.()?.length ? (
260
- // TODO: remove `as any` cast
261
- (renderer.value = context.slots?.renderer?.()[0] as any)
262
- ) : (
263
- // ...otherwise, add canvas...
264
- <>
265
- <div
266
- class="lunchbox-container"
267
- style={fillStyle(containerFillStyle) as any}
268
- ref={container}
269
- data-lunchbox="true"
270
- >
271
- <canvas
272
- ref={canvas}
273
- class="lunchbox-canvas"
274
- style={fillStyle(canvasFillStyle) as any}
275
- data-lunchbox="true"
276
- ></canvas>
277
- </div>
278
- {/* ...and create default renderer once canvas is present */}
279
- {canvas.value?.domElement && (
280
- <webGLRenderer
281
- {...(props.rendererProperties ?? {})}
282
- ref={renderer}
283
- args={[
284
- {
285
- alpha: props.transparent,
286
- antialias: true,
287
- canvas: canvas.value?.domElement,
288
- powerPreference: !!props.r3f
289
- ? 'high-performance'
290
- : 'default',
291
- ...(props.rendererArguments ?? {}),
292
- },
293
- ]}
294
- />
295
- )}
296
- </>
297
- )}
298
-
299
- {/* use scene slot if provided... */}
300
- {context.slots?.scene?.()?.length ? (
301
- // TODO: remove `as any` cast
302
- (scene.value = context.slots?.scene?.()[0] as any)
303
- ) : (
304
- // ...otherwise, add default scene
305
- // TODO: why does this need to be a separate component? <scene> throws an error
306
- <LunchboxScene ref={scene}>
307
- {context.slots?.default?.()}
308
- </LunchboxScene>
309
- )}
310
-
311
- {/* use camera slot if provided... */}
312
- {context.slots?.camera?.()?.length ? (
313
- // TODO: remove `any` cast
314
- camera.value
315
- ) : props.ortho || props.orthographic ? (
316
- <orthographicCamera
317
- ref={camera}
318
- args={props.cameraArgs ?? []}
319
- {...consolidatedCameraProperties}
320
- />
321
- ) : (
322
- <perspectiveCamera
323
- ref={camera}
324
- args={
325
- props.cameraArgs ?? [
326
- props.r3f ? 75 : 45,
327
- 0.5625,
328
- 1,
329
- 1000,
330
- ]
331
- }
332
- {...consolidatedCameraProperties}
333
- />
334
- )}
335
-
336
- {/* Lunchbox interaction handlers */}
337
- {interactables?.value.length && <LunchboxEventHandlers />}
338
- </>
339
- )
340
- },
341
- })
@@ -1,55 +0,0 @@
1
- import { MiniDom } from '../../core'
2
- import { Lunch } from '../../types'
3
- import { Ref } from 'vue'
4
- import { resizeCanvas } from './resizeCanvas'
5
-
6
- const getInnerDimensions = (node: Element) => {
7
- const computedStyle = getComputedStyle(node)
8
- const width =
9
- node.clientWidth -
10
- parseFloat(computedStyle.paddingLeft) -
11
- parseFloat(computedStyle.paddingRight)
12
- const height =
13
- node.clientHeight -
14
- parseFloat(computedStyle.paddingTop) -
15
- parseFloat(computedStyle.paddingBottom)
16
- return { width, height }
17
- }
18
-
19
- export const prepCanvas = (
20
- container: Ref<MiniDom.RendererDomNode | undefined>,
21
- camera: THREE.Camera,
22
- renderer: THREE.Renderer,
23
- scene: THREE.Scene,
24
- sizePolicy?: Lunch.SizePolicy
25
- ) => {
26
- const containerElement = container.value?.domElement
27
- if (!containerElement) throw new Error('missing container')
28
-
29
- // save and size element
30
- const resizeCanvasByPolicy = () => {
31
- if (sizePolicy === 'container') {
32
- const dims = getInnerDimensions(containerElement)
33
- resizeCanvas(camera, renderer, scene, dims.width, dims.height)
34
- } else resizeCanvas(camera, renderer, scene)
35
- }
36
- resizeCanvasByPolicy()
37
-
38
- // attach listeners
39
- let observer = new ResizeObserver(() => {
40
- resizeCanvasByPolicy()
41
- })
42
- // window.addEventListener('resize', resizeCanvas)
43
- if (containerElement) {
44
- observer.observe(containerElement)
45
- }
46
-
47
- // cleanup
48
- return {
49
- dispose() {
50
- if (containerElement) {
51
- observer.unobserve(containerElement)
52
- }
53
- },
54
- }
55
- }
@@ -1,41 +0,0 @@
1
- import { toRaw } from 'vue'
2
-
3
- export const resizeCanvas = (
4
- camera: THREE.Camera,
5
- renderer: THREE.Renderer,
6
- scene: THREE.Scene,
7
- width?: number,
8
- height?: number
9
- ) => {
10
- // ignore if no element
11
- if (!renderer?.domElement || !scene || !camera) return
12
-
13
- width = width ?? window.innerWidth
14
- height = height ?? window.innerHeight
15
-
16
- // update camera
17
- const aspect = width / height
18
- if (camera.type?.toLowerCase() === 'perspectivecamera') {
19
- const perspectiveCamera = camera as THREE.PerspectiveCamera
20
- perspectiveCamera.aspect = aspect
21
- perspectiveCamera.updateProjectionMatrix()
22
- } else if (camera.type?.toLowerCase() === 'orthographiccamera') {
23
- // TODO: ortho camera update
24
- const orthoCamera = camera as THREE.OrthographicCamera
25
- const heightInTermsOfWidth = height / width
26
- orthoCamera.top = heightInTermsOfWidth * 10
27
- orthoCamera.bottom = -heightInTermsOfWidth * 10
28
- orthoCamera.right = 10
29
- orthoCamera.left = -10
30
- orthoCamera.updateProjectionMatrix()
31
- } else {
32
- // TODO: non-ortho or perspective camera
33
- }
34
-
35
- // update canvas
36
- renderer.setSize(width, height)
37
- // render immediately so there's no flicker
38
- if (scene && camera) {
39
- renderer.render(toRaw(scene), toRaw(camera))
40
- }
41
- }