lunchboxjs 0.2.1001-beta.1 → 0.2.1001-beta.2
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.
- package/dist/lunchboxjs.js +160 -87
- package/dist/lunchboxjs.min.js +1 -1
- package/dist/lunchboxjs.module.js +159 -82
- package/extras/OrbitControlsWrapper.vue +2 -12
- package/package.json +4 -1
- package/src/components/LunchboxEventHandlers.tsx +29 -31
- package/src/components/LunchboxWrapper/LunchboxWrapper.tsx +42 -1
- package/src/core/index.ts +0 -1
- package/src/core/update.ts +30 -14
- package/src/core/updateObjectProp.ts +16 -2
- package/src/index.ts +55 -33
- package/src/utils/index.ts +10 -0
- package/src/core/ensure.ts +0 -16
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isRef, isVNode,
|
|
1
|
+
import { isRef, isVNode, toRaw, defineComponent, createVNode, resolveComponent, ref, onBeforeUnmount, watch, reactive, onMounted, computed, Fragment, mergeProps, h, inject, createRenderer } from 'vue';
|
|
2
2
|
import * as THREE from 'three';
|
|
3
3
|
import { get, isNumber, set } from 'lodash';
|
|
4
4
|
|
|
@@ -102,34 +102,6 @@ function createNode(options = {}, props = {}) {
|
|
|
102
102
|
return node;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
const globalsInjectionKey = Symbol();
|
|
106
|
-
const updateGlobalsInjectionKey = Symbol();
|
|
107
|
-
const setCustomRenderKey = Symbol();
|
|
108
|
-
const clearCustomRenderKey = Symbol();
|
|
109
|
-
const beforeRenderKey = Symbol();
|
|
110
|
-
const onBeforeRenderKey = Symbol();
|
|
111
|
-
const offBeforeRenderKey = Symbol();
|
|
112
|
-
const afterRenderKey = Symbol();
|
|
113
|
-
const onAfterRenderKey = Symbol();
|
|
114
|
-
const offAfterRenderKey = Symbol();
|
|
115
|
-
const frameIdKey = Symbol();
|
|
116
|
-
const watchStopHandleKey = Symbol();
|
|
117
|
-
const appRootNodeKey = Symbol();
|
|
118
|
-
const appKey = Symbol();
|
|
119
|
-
const appRenderersKey = Symbol();
|
|
120
|
-
const appSceneKey = Symbol();
|
|
121
|
-
const appCameraKey = Symbol();
|
|
122
|
-
const lunchboxInteractables = Symbol();
|
|
123
|
-
const startCallbackKey = Symbol();
|
|
124
|
-
|
|
125
|
-
const ensuredCamera = () => inject(appCameraKey); // ENSURE RENDERER
|
|
126
|
-
// ====================
|
|
127
|
-
|
|
128
|
-
const ensureRenderer = () => inject(appRenderersKey); // ENSURE SCENE
|
|
129
|
-
// ====================
|
|
130
|
-
|
|
131
|
-
const ensuredScene = () => inject(appSceneKey);
|
|
132
|
-
|
|
133
105
|
/** Add an event listener to the given node. Also creates the event teardown function and any necessary raycaster/interaction dictionary updates. */
|
|
134
106
|
function addEventListener({
|
|
135
107
|
node,
|
|
@@ -260,8 +232,6 @@ const LunchboxEventHandlers = defineComponent({
|
|
|
260
232
|
|
|
261
233
|
setup() {
|
|
262
234
|
const interactables = useLunchboxInteractables();
|
|
263
|
-
const camera = useCamera();
|
|
264
|
-
const renderer = useRenderer();
|
|
265
235
|
const globals = useGlobals();
|
|
266
236
|
const mousePos = ref({
|
|
267
237
|
x: Infinity,
|
|
@@ -289,7 +259,7 @@ const LunchboxEventHandlers = defineComponent({
|
|
|
289
259
|
}; // add mouse listener to renderer DOM element when the element is ready
|
|
290
260
|
|
|
291
261
|
|
|
292
|
-
|
|
262
|
+
onRendererReady(v => {
|
|
293
263
|
if (!v?.domElement) return; // we have a DOM element, so let's add mouse listeners
|
|
294
264
|
|
|
295
265
|
const {
|
|
@@ -310,16 +280,14 @@ const LunchboxEventHandlers = defineComponent({
|
|
|
310
280
|
|
|
311
281
|
domElement.addEventListener('pointermove', mouseMoveListener);
|
|
312
282
|
domElement.addEventListener('pointerdown', mouseDownListener);
|
|
313
|
-
domElement.addEventListener('pointerup', mouseUpListener);
|
|
314
|
-
|
|
315
|
-
stopWatch();
|
|
316
|
-
}, {
|
|
317
|
-
immediate: true
|
|
283
|
+
domElement.addEventListener('pointerup', mouseUpListener);
|
|
318
284
|
});
|
|
285
|
+
const camera = useCamera();
|
|
319
286
|
|
|
320
287
|
const update = () => {
|
|
321
288
|
const c = camera.value;
|
|
322
|
-
if (!c) return;
|
|
289
|
+
if (!c) return; // console.log(camera.value)
|
|
290
|
+
|
|
323
291
|
raycaster.setFromCamera(mousePos.value, c);
|
|
324
292
|
const intersections = raycaster.intersectObjects(interactables?.value.map(v => v.instance) ?? []);
|
|
325
293
|
let leaveValues = [],
|
|
@@ -410,10 +378,13 @@ const LunchboxEventHandlers = defineComponent({
|
|
|
410
378
|
onBeforeUnmount(teardown);
|
|
411
379
|
const clickEventKeys = ['onClick', 'onPointerDown', 'onPointerUp'];
|
|
412
380
|
watch(inputActive, isDown => {
|
|
413
|
-
//
|
|
381
|
+
// run raycaster on click (necessary when `update` is not automatically called,
|
|
382
|
+
// for example in `updateSource` functions)
|
|
383
|
+
update(); // meshes with multiple intersections receive multiple callbacks by default -
|
|
414
384
|
// let's make it so they only receive one callback of each type per frame.
|
|
415
385
|
// (ie usually when you click on a mesh, you expect only one click event to fire, even
|
|
416
386
|
// if there are technically multiple intersections with that mesh)
|
|
387
|
+
|
|
417
388
|
const uuidsInteractedWithThisFrame = [];
|
|
418
389
|
currentIntersections.forEach(v => {
|
|
419
390
|
clickEventKeys.forEach(key => {
|
|
@@ -615,7 +586,37 @@ const LunchboxWrapper = defineComponent({
|
|
|
615
586
|
// ====================
|
|
616
587
|
|
|
617
588
|
const containerFillStyle = props.sizePolicy === 'container' ? 'static' : 'absolute';
|
|
618
|
-
const canvasFillStyle = props.sizePolicy === 'container' ? 'static' : 'fixed';
|
|
589
|
+
const canvasFillStyle = props.sizePolicy === 'container' ? 'static' : 'fixed'; // REACTIVE CUSTOM CAMERAS
|
|
590
|
+
// ====================
|
|
591
|
+
// find first camera with `type.name` property
|
|
592
|
+
// (which indicates a Lunch.Node)
|
|
593
|
+
|
|
594
|
+
const activeCamera = computed(() => {
|
|
595
|
+
const output = context.slots?.camera?.().find(c => c.type?.name);
|
|
596
|
+
|
|
597
|
+
if (output) {
|
|
598
|
+
return output;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
return output;
|
|
602
|
+
}); // TODO: make custom cameras reactive
|
|
603
|
+
|
|
604
|
+
watch(activeCamera, async (newVal, oldVal) => {
|
|
605
|
+
// console.log('got camera', newVal)
|
|
606
|
+
if (newVal && newVal?.props?.key !== oldVal?.props?.key) {
|
|
607
|
+
// TODO: remove cast
|
|
608
|
+
camera.value = newVal; // TODO: why isn't this updating app camera?
|
|
609
|
+
// const el = await waitFor(() => newVal.el)
|
|
610
|
+
// console.log(el)
|
|
611
|
+
// camera.value = el
|
|
612
|
+
// console.log(newVal.uuid)
|
|
613
|
+
// updateGlobals?.({ camera: el })
|
|
614
|
+
}
|
|
615
|
+
}, {
|
|
616
|
+
immediate: true
|
|
617
|
+
}); // RENDER FUNCTION
|
|
618
|
+
// ====================
|
|
619
|
+
|
|
619
620
|
return () => createVNode(Fragment, null, [context.slots?.renderer?.()?.length ? // TODO: remove `as any` cast
|
|
620
621
|
renderer.value = context.slots?.renderer?.()[0] : // ...otherwise, add canvas...
|
|
621
622
|
createVNode(Fragment, null, [createVNode("div", {
|
|
@@ -645,7 +646,7 @@ const LunchboxWrapper = defineComponent({
|
|
|
645
646
|
}, {
|
|
646
647
|
default: () => [context.slots?.default?.()]
|
|
647
648
|
}), context.slots?.camera?.()?.length ? // TODO: remove `any` cast
|
|
648
|
-
camera.value
|
|
649
|
+
camera.value : props.ortho || props.orthographic ? createVNode(resolveComponent("orthographicCamera"), mergeProps({
|
|
649
650
|
"ref": camera,
|
|
650
651
|
"args": props.cameraArgs ?? []
|
|
651
652
|
}, consolidatedCameraProperties), null) : createVNode(resolveComponent("perspectiveCamera"), mergeProps({
|
|
@@ -1113,6 +1114,26 @@ function isMinidomNode(item) {
|
|
|
1113
1114
|
return item?.minidomType === 'RendererNode';
|
|
1114
1115
|
}
|
|
1115
1116
|
|
|
1117
|
+
const globalsInjectionKey = Symbol();
|
|
1118
|
+
const updateGlobalsInjectionKey = Symbol();
|
|
1119
|
+
const setCustomRenderKey = Symbol();
|
|
1120
|
+
const clearCustomRenderKey = Symbol();
|
|
1121
|
+
const beforeRenderKey = Symbol();
|
|
1122
|
+
const onBeforeRenderKey = Symbol();
|
|
1123
|
+
const offBeforeRenderKey = Symbol();
|
|
1124
|
+
const afterRenderKey = Symbol();
|
|
1125
|
+
const onAfterRenderKey = Symbol();
|
|
1126
|
+
const offAfterRenderKey = Symbol();
|
|
1127
|
+
const frameIdKey = Symbol();
|
|
1128
|
+
const watchStopHandleKey = Symbol();
|
|
1129
|
+
const appRootNodeKey = Symbol();
|
|
1130
|
+
const appKey = Symbol();
|
|
1131
|
+
const appRenderersKey = Symbol();
|
|
1132
|
+
const appSceneKey = Symbol();
|
|
1133
|
+
const appCameraKey = Symbol();
|
|
1134
|
+
const lunchboxInteractables = Symbol();
|
|
1135
|
+
const startCallbackKey = Symbol();
|
|
1136
|
+
|
|
1116
1137
|
const requestUpdate = opts => {
|
|
1117
1138
|
if (typeof opts.app.config.globalProperties.lunchbox.frameId === 'number') {
|
|
1118
1139
|
cancelAnimationFrame(opts.app.config.globalProperties.lunchbox.frameId);
|
|
@@ -1146,20 +1167,19 @@ const update = opts => {
|
|
|
1146
1167
|
const {
|
|
1147
1168
|
app,
|
|
1148
1169
|
renderer,
|
|
1149
|
-
scene
|
|
1150
|
-
camera
|
|
1170
|
+
scene
|
|
1151
1171
|
} = opts; // BEFORE RENDER
|
|
1152
1172
|
|
|
1153
1173
|
app.config.globalProperties.lunchbox.beforeRender.forEach(cb => {
|
|
1154
1174
|
cb?.(opts);
|
|
1155
1175
|
}); // RENDER
|
|
1156
1176
|
|
|
1157
|
-
if (renderer && scene && camera) {
|
|
1177
|
+
if (renderer && scene && opts.app.config.globalProperties.lunchbox.camera) {
|
|
1158
1178
|
if (app.customRender) {
|
|
1159
1179
|
app.customRender(opts);
|
|
1160
1180
|
} else {
|
|
1161
|
-
renderer.render(toRaw(scene),
|
|
1162
|
-
|
|
1181
|
+
renderer.render(toRaw(scene), opts.app.config.globalProperties.lunchbox.camera // toRaw(camera)
|
|
1182
|
+
);
|
|
1163
1183
|
}
|
|
1164
1184
|
} // AFTER RENDER
|
|
1165
1185
|
|
|
@@ -1169,55 +1189,81 @@ const update = opts => {
|
|
|
1169
1189
|
});
|
|
1170
1190
|
}; // before render
|
|
1171
1191
|
// ====================
|
|
1172
|
-
|
|
1192
|
+
|
|
1193
|
+
/** Obtain callback methods for `onBeforeRender` and `offBeforeRender`. Usually used internally by Lunchbox. */
|
|
1173
1194
|
|
|
1174
1195
|
const useBeforeRender = () => {
|
|
1175
1196
|
return {
|
|
1176
1197
|
onBeforeRender: inject(onBeforeRenderKey),
|
|
1177
1198
|
offBeforeRender: inject(offBeforeRenderKey)
|
|
1178
1199
|
};
|
|
1179
|
-
};
|
|
1200
|
+
};
|
|
1201
|
+
/** Run a function before every render.
|
|
1202
|
+
*
|
|
1203
|
+
* Note that if `updateSource` is set in the Lunchbox wrapper component, this will **only** run
|
|
1204
|
+
* before a render triggered by that `updateSource`. Normally, the function should run every frame.
|
|
1205
|
+
*/
|
|
1180
1206
|
|
|
1181
1207
|
const onBeforeRender = (cb, index = Infinity) => {
|
|
1182
1208
|
useBeforeRender().onBeforeRender?.(cb, index);
|
|
1183
|
-
};
|
|
1209
|
+
};
|
|
1210
|
+
/** Remove a function from the `beforeRender` callback list. Useful for tearing down functions added
|
|
1211
|
+
* by `onBeforeRender`.
|
|
1212
|
+
*/
|
|
1184
1213
|
|
|
1185
1214
|
const offBeforeRender = cb => {
|
|
1186
1215
|
useBeforeRender().offBeforeRender?.(cb);
|
|
1187
1216
|
}; // after render
|
|
1188
1217
|
// ====================
|
|
1189
|
-
|
|
1218
|
+
|
|
1219
|
+
/** Obtain callback methods for `onAfterRender` and `offAfterRender`. Usually used internally by Lunchbox. */
|
|
1190
1220
|
|
|
1191
1221
|
const useAfterRender = () => {
|
|
1192
1222
|
return {
|
|
1193
1223
|
onAfterRender: inject(onBeforeRenderKey),
|
|
1194
1224
|
offAfterRender: inject(offBeforeRenderKey)
|
|
1195
1225
|
};
|
|
1196
|
-
};
|
|
1226
|
+
};
|
|
1227
|
+
/** Run a function after every render.
|
|
1228
|
+
*
|
|
1229
|
+
* Note that if `updateSource` is set in the Lunchbox wrapper component, this will **only** run
|
|
1230
|
+
* after a render triggered by that `updateSource`. Normally, the function should run every frame.
|
|
1231
|
+
*/
|
|
1197
1232
|
|
|
1198
1233
|
const onAfterRender = (cb, index = Infinity) => {
|
|
1199
1234
|
useBeforeRender().onBeforeRender?.(cb, index);
|
|
1200
|
-
};
|
|
1235
|
+
};
|
|
1236
|
+
/** Remove a function from the `afterRender` callback list. Useful for tearing down functions added
|
|
1237
|
+
* by `onAfterRender`.
|
|
1238
|
+
*/
|
|
1201
1239
|
|
|
1202
1240
|
const offAfterRender = cb => {
|
|
1203
1241
|
useBeforeRender().offBeforeRender?.(cb);
|
|
1204
|
-
};
|
|
1242
|
+
};
|
|
1243
|
+
/** Obtain a function used to cancel the current update frame. Use `cancelUpdate` if you wish
|
|
1244
|
+
* to immediately invoke the cancellation function. Usually used internally by Lunchbox.
|
|
1245
|
+
*/
|
|
1205
1246
|
|
|
1206
1247
|
const useCancelUpdate = () => {
|
|
1207
1248
|
const frameId = inject(frameIdKey);
|
|
1208
1249
|
return () => {
|
|
1209
1250
|
if (frameId !== undefined) cancelAnimationFrame(frameId);
|
|
1210
1251
|
};
|
|
1211
|
-
};
|
|
1252
|
+
};
|
|
1253
|
+
/** Cancel the current update frame. Usually used internally by Lunchbox. */
|
|
1212
1254
|
|
|
1213
1255
|
const cancelUpdate = () => {
|
|
1214
1256
|
useCancelUpdate()?.();
|
|
1215
|
-
};
|
|
1257
|
+
};
|
|
1258
|
+
/** Obtain a function used to cancel an update source. Use `cancelUpdateSource` if you wish to
|
|
1259
|
+
* immediately invoke the cancellation function. Usually used internally by Lunchbox.
|
|
1260
|
+
*/
|
|
1216
1261
|
|
|
1217
1262
|
const useCancelUpdateSource = () => {
|
|
1218
1263
|
const cancel = inject(watchStopHandleKey);
|
|
1219
1264
|
return () => cancel?.();
|
|
1220
|
-
};
|
|
1265
|
+
};
|
|
1266
|
+
/** Cancel an update source. Usually used internally by Lunchbox. */
|
|
1221
1267
|
|
|
1222
1268
|
const cancelUpdateSource = () => {
|
|
1223
1269
|
useCancelUpdateSource()?.();
|
|
@@ -1268,6 +1314,7 @@ function updateObjectProp({
|
|
|
1268
1314
|
const fullPath = [nestedProperty, finalKey].filter(Boolean).join('.');
|
|
1269
1315
|
liveProperty = liveProperty = get(target, fullPath);
|
|
1270
1316
|
} // change property
|
|
1317
|
+
// first, save as array in case we need to spread it
|
|
1271
1318
|
|
|
1272
1319
|
|
|
1273
1320
|
if (liveProperty && isNumber(value) && liveProperty.setScalar) {
|
|
@@ -1278,14 +1325,24 @@ function updateObjectProp({
|
|
|
1278
1325
|
const nextValueAsArray = Array.isArray(value) ? value : [value];
|
|
1279
1326
|
target[finalKey].set(...nextValueAsArray);
|
|
1280
1327
|
} else if (typeof liveProperty === 'function') {
|
|
1281
|
-
//
|
|
1282
|
-
|
|
1328
|
+
// some function properties are set rather than called, so let's handle them
|
|
1329
|
+
if (finalKey.toLowerCase() === 'onbeforerender' || finalKey.toLowerCase() === 'onafterrender') {
|
|
1330
|
+
target[finalKey] = value;
|
|
1331
|
+
} else {
|
|
1332
|
+
if (!Array.isArray(value)) {
|
|
1333
|
+
throw new Error('Arguments on a declarative method must be wrapped in an array.\nWorks:\n<example :methodCall="[256]" />\nDoesn\'t work:\n<example :methodCall="256" />');
|
|
1334
|
+
} // if property is a function, let's try calling it
|
|
1335
|
+
|
|
1336
|
+
|
|
1337
|
+
liveProperty.bind(node.instance)(...value);
|
|
1338
|
+
} // pass the result to the parent
|
|
1283
1339
|
// const parent = node.parentNode
|
|
1284
1340
|
// if (parent) {
|
|
1285
1341
|
// const parentAsLunchboxNode = parent as Lunchbox.Node
|
|
1286
1342
|
// parentAsLunchboxNode.attached[finalKey] = result
|
|
1287
1343
|
// ; (parentAsLunchboxNode.instance as any)[finalKey] = result
|
|
1288
1344
|
// }
|
|
1345
|
+
|
|
1289
1346
|
} else if (get(target, finalKey, undefined) !== undefined) {
|
|
1290
1347
|
// blank strings evaluate to `true`
|
|
1291
1348
|
// <mesh castShadow receiveShadow /> will work the same as
|
|
@@ -1575,33 +1632,51 @@ const createNodeOps = () => {
|
|
|
1575
1632
|
};
|
|
1576
1633
|
};
|
|
1577
1634
|
|
|
1578
|
-
/** The current camera
|
|
1579
|
-
// TODO: update docs
|
|
1635
|
+
/** The current camera as a computed value. */
|
|
1580
1636
|
|
|
1581
|
-
const
|
|
1637
|
+
const useCamera = () => inject(appCameraKey);
|
|
1638
|
+
/** Run a function using the current camera when it's present. */
|
|
1582
1639
|
|
|
1583
|
-
const
|
|
1584
|
-
|
|
1640
|
+
const onCameraReady = cb => {
|
|
1641
|
+
const stopWatch = watch(useCamera(), newVal => {
|
|
1642
|
+
if (newVal) {
|
|
1643
|
+
cb(newVal);
|
|
1644
|
+
stopWatch();
|
|
1645
|
+
}
|
|
1646
|
+
}, {
|
|
1647
|
+
immediate: true
|
|
1648
|
+
});
|
|
1649
|
+
};
|
|
1650
|
+
/** The current renderer as a computed value. */
|
|
1585
1651
|
|
|
1586
|
-
const
|
|
1652
|
+
const useRenderer = () => inject(appRenderersKey);
|
|
1587
1653
|
/** Run a function using the current renderer when it's present. */
|
|
1588
1654
|
|
|
1589
|
-
const
|
|
1590
|
-
|
|
1591
|
-
|
|
1655
|
+
const onRendererReady = cb => {
|
|
1656
|
+
const stopWatch = watch(useRenderer(), newVal => {
|
|
1657
|
+
if (newVal) {
|
|
1658
|
+
cb(newVal);
|
|
1659
|
+
stopWatch();
|
|
1660
|
+
}
|
|
1661
|
+
}, {
|
|
1662
|
+
immediate: true
|
|
1663
|
+
});
|
|
1664
|
+
};
|
|
1665
|
+
/** The current scene as a computed value. */
|
|
1592
1666
|
|
|
1593
|
-
const
|
|
1667
|
+
const useScene = () => inject(appSceneKey);
|
|
1594
1668
|
/** Run a function using the current scene when it's present. */
|
|
1595
|
-
// TODO: update docs
|
|
1596
1669
|
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
if (
|
|
1600
|
-
|
|
1670
|
+
const onSceneReady = cb => {
|
|
1671
|
+
const stopWatch = watch(useScene(), newVal => {
|
|
1672
|
+
if (newVal) {
|
|
1673
|
+
cb(newVal);
|
|
1674
|
+
stopWatch();
|
|
1675
|
+
}
|
|
1601
1676
|
}, {
|
|
1602
1677
|
immediate: true
|
|
1603
1678
|
});
|
|
1604
|
-
} // CUSTOM RENDER SUPPORT
|
|
1679
|
+
}; // CUSTOM RENDER SUPPORT
|
|
1605
1680
|
// ====================
|
|
1606
1681
|
|
|
1607
1682
|
/** Set a custom render function, overriding the Lunchbox app's default render function.
|
|
@@ -1658,14 +1733,15 @@ const useUpdateGlobals = () => inject(updateGlobalsInjectionKey);
|
|
|
1658
1733
|
|
|
1659
1734
|
const updateGlobals = newValue => {
|
|
1660
1735
|
useUpdateGlobals()?.(newValue);
|
|
1661
|
-
};
|
|
1662
|
-
|
|
1663
|
-
const useRootNode = () => inject(appRootNodeKey); // TODO: document
|
|
1736
|
+
};
|
|
1737
|
+
/** Use the current Lunchbox app. Usually used internally by Lunchbox. */
|
|
1664
1738
|
|
|
1665
|
-
const useApp = () => inject(appKey);
|
|
1739
|
+
const useApp = () => inject(appKey);
|
|
1740
|
+
/** Obtain a list of the start callback functions. Usually used internally by Lunchbox. */
|
|
1666
1741
|
|
|
1667
|
-
const useStartCallbacks = () => inject(startCallbackKey);
|
|
1668
|
-
|
|
1742
|
+
const useStartCallbacks = () => inject(startCallbackKey);
|
|
1743
|
+
/** Run a given callback once when the Lunchbox app starts. Include an index to
|
|
1744
|
+
* splice the callback at that index in the callback queue. */
|
|
1669
1745
|
|
|
1670
1746
|
const onStart = (cb, index = Infinity) => {
|
|
1671
1747
|
const callbacks = useStartCallbacks();
|
|
@@ -1675,7 +1751,8 @@ const onStart = (cb, index = Infinity) => {
|
|
|
1675
1751
|
} else {
|
|
1676
1752
|
callbacks?.splice(index, 0, cb);
|
|
1677
1753
|
}
|
|
1678
|
-
};
|
|
1754
|
+
};
|
|
1755
|
+
/** Obtain a list of interactable objects (registered via onClick, onHover, etc events). Usually used internally by Lunchbox. */
|
|
1679
1756
|
|
|
1680
1757
|
const useLunchboxInteractables = () => inject(lunchboxInteractables); // CREATE APP
|
|
1681
1758
|
// ====================
|
|
@@ -1838,4 +1915,4 @@ const createApp = root => {
|
|
|
1838
1915
|
return app;
|
|
1839
1916
|
};
|
|
1840
1917
|
|
|
1841
|
-
export { MiniDom, addEventListener, afterRenderKey, appCameraKey, appKey, appRenderersKey, appRootNodeKey, appSceneKey, beforeRenderKey,
|
|
1918
|
+
export { MiniDom, addEventListener, afterRenderKey, appCameraKey, appKey, appRenderersKey, appRootNodeKey, appSceneKey, beforeRenderKey, cancelUpdate, cancelUpdateSource, clearCustomRender, clearCustomRenderKey, createApp, createCommentNode, createDomNode, createNode, createTextNode, extend, find, frameIdKey, globalsInjectionKey, instantiateThreeObject, isMinidomNode, lunchboxInteractables, nestedPropertiesToCheck, offAfterRender, offAfterRenderKey, offBeforeRender, offBeforeRenderKey, onAfterRender, onAfterRenderKey, onBeforeRender, onBeforeRenderKey, onCameraReady, onRendererReady, onSceneReady, onStart, setCustomRender, setCustomRenderKey, startCallbackKey, update, updateGlobals, updateGlobalsInjectionKey, updateObjectProp, useAfterRender, useApp, useBeforeRender, useCamera, useCancelUpdate, useCancelUpdateSource, useCustomRender, useGlobals, useLunchboxInteractables, useRenderer, useScene, useStartCallbacks, useUpdateGlobals, watchStopHandleKey };
|
|
@@ -11,15 +11,8 @@
|
|
|
11
11
|
</template>
|
|
12
12
|
|
|
13
13
|
<script lang="ts" setup>
|
|
14
|
-
import { computed, ref
|
|
15
|
-
import {
|
|
16
|
-
onBeforeRender,
|
|
17
|
-
globals,
|
|
18
|
-
Lunch,
|
|
19
|
-
// camera,
|
|
20
|
-
useRenderer,
|
|
21
|
-
useCamera,
|
|
22
|
-
} from '../src'
|
|
14
|
+
import { computed, ref } from 'vue'
|
|
15
|
+
import { onBeforeRender, Lunch, useCamera, useRenderer } from '../src'
|
|
23
16
|
|
|
24
17
|
// props
|
|
25
18
|
const props = defineProps<{
|
|
@@ -33,9 +26,6 @@ const ready = computed(() => {
|
|
|
33
26
|
const camera = useCamera()
|
|
34
27
|
const renderer = useRenderer()
|
|
35
28
|
const orbitArgs = computed(() => [camera.value, renderer.value?.domElement])
|
|
36
|
-
// watch(() => orbitArgs.value, console.log, { immediate: true })
|
|
37
|
-
// console.log(renderer)
|
|
38
|
-
watch(camera, console.log)
|
|
39
29
|
|
|
40
30
|
// update
|
|
41
31
|
const controls = ref<Lunch.LunchboxComponent>()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lunchboxjs",
|
|
3
|
-
"version": "0.2.1001-beta.
|
|
3
|
+
"version": "0.2.1001-beta.2",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "vite -c utils/vite.config.ts",
|
|
6
6
|
"build": "vue-tsc --noEmit && vite build -c utils/vite.config.ts",
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
"docs:serve": "vitepress serve docs",
|
|
15
15
|
"demo:create": "node utils/createExample"
|
|
16
16
|
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"npm": ">=16.0.0"
|
|
19
|
+
},
|
|
17
20
|
"dependencies": {
|
|
18
21
|
"uuid": "8.3.2",
|
|
19
22
|
"vue": "^3.2.16"
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { defineComponent, onBeforeUnmount, ref, watch } from 'vue'
|
|
2
2
|
import {
|
|
3
|
-
Lunch,
|
|
4
3
|
useCamera,
|
|
4
|
+
Lunch,
|
|
5
5
|
useGlobals,
|
|
6
6
|
useLunchboxInteractables,
|
|
7
|
-
|
|
7
|
+
onRendererReady,
|
|
8
8
|
} from '..'
|
|
9
9
|
import * as THREE from 'three'
|
|
10
10
|
import { offBeforeRender, onBeforeRender } from '../core'
|
|
@@ -13,8 +13,6 @@ export const LunchboxEventHandlers = defineComponent({
|
|
|
13
13
|
name: 'LunchboxEventHandlers',
|
|
14
14
|
setup() {
|
|
15
15
|
const interactables = useLunchboxInteractables()
|
|
16
|
-
const camera = useCamera()
|
|
17
|
-
const renderer = useRenderer()
|
|
18
16
|
const globals = useGlobals()
|
|
19
17
|
const mousePos = ref({ x: Infinity, y: Infinity })
|
|
20
18
|
const inputActive = ref(false)
|
|
@@ -49,38 +47,34 @@ export const LunchboxEventHandlers = defineComponent({
|
|
|
49
47
|
}
|
|
50
48
|
|
|
51
49
|
// add mouse listener to renderer DOM element when the element is ready
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
domElement.addEventListener('pointerup', mouseUpListener)
|
|
73
|
-
|
|
74
|
-
// stop the watcher
|
|
75
|
-
stopWatch()
|
|
76
|
-
},
|
|
77
|
-
{ immediate: true }
|
|
78
|
-
)
|
|
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
|
+
})
|
|
79
70
|
|
|
71
|
+
const camera = useCamera()
|
|
80
72
|
const update = () => {
|
|
81
73
|
const c = camera.value
|
|
82
74
|
if (!c) return
|
|
83
75
|
|
|
76
|
+
// console.log(camera.value)
|
|
77
|
+
|
|
84
78
|
raycaster.setFromCamera(mousePos.value, c)
|
|
85
79
|
const intersections = raycaster.intersectObjects(
|
|
86
80
|
interactables?.value.map(
|
|
@@ -202,6 +196,10 @@ export const LunchboxEventHandlers = defineComponent({
|
|
|
202
196
|
'onPointerUp',
|
|
203
197
|
]
|
|
204
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
|
+
|
|
205
203
|
// meshes with multiple intersections receive multiple callbacks by default -
|
|
206
204
|
// let's make it so they only receive one callback of each type per frame.
|
|
207
205
|
// (ie usually when you click on a mesh, you expect only one click event to fire, even
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
+
computed,
|
|
2
3
|
defineComponent,
|
|
3
4
|
onBeforeUnmount,
|
|
4
5
|
onMounted,
|
|
5
6
|
PropType,
|
|
6
7
|
reactive,
|
|
7
8
|
ref,
|
|
9
|
+
watch,
|
|
8
10
|
WatchSource,
|
|
9
11
|
} from 'vue'
|
|
10
12
|
import { cancelUpdate, cancelUpdateSource, MiniDom, update } from '../../core'
|
|
@@ -14,6 +16,8 @@ import { prepCanvas } from './prepCanvas'
|
|
|
14
16
|
import { useUpdateGlobals, useStartCallbacks } from '../..'
|
|
15
17
|
import { LunchboxScene } from './LunchboxScene'
|
|
16
18
|
import { LunchboxEventHandlers } from '../LunchboxEventHandlers'
|
|
19
|
+
import * as Keys from '../../keys'
|
|
20
|
+
import { waitFor } from '../../utils'
|
|
17
21
|
|
|
18
22
|
/** fixed & fill styling for container */
|
|
19
23
|
const fillStyle = (position: string) => {
|
|
@@ -212,6 +216,43 @@ export const LunchboxWrapper = defineComponent({
|
|
|
212
216
|
const canvasFillStyle =
|
|
213
217
|
props.sizePolicy === 'container' ? 'static' : 'fixed'
|
|
214
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
|
+
// ====================
|
|
215
256
|
return () => (
|
|
216
257
|
<>
|
|
217
258
|
{/* use renderer slot if provided... */}
|
|
@@ -270,7 +311,7 @@ export const LunchboxWrapper = defineComponent({
|
|
|
270
311
|
{/* use camera slot if provided... */}
|
|
271
312
|
{context.slots?.camera?.()?.length ? (
|
|
272
313
|
// TODO: remove `any` cast
|
|
273
|
-
|
|
314
|
+
camera.value
|
|
274
315
|
) : props.ortho || props.orthographic ? (
|
|
275
316
|
<orthographicCamera
|
|
276
317
|
ref={camera}
|