lunchboxjs 0.1.4015 → 0.1.4018
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 +78 -15
- package/dist/lunchboxjs.min.js +1 -1
- package/dist/lunchboxjs.module.js +79 -16
- package/package.json +1 -1
- package/src/components/LunchboxWrapper/LunchboxWrapper.ts +14 -3
- package/src/components/LunchboxWrapper/prepCanvas.ts +20 -3
- package/src/components/catalogue.ts +1 -1
- package/src/core/instantiateThreeObject/processProps.ts +23 -6
- package/src/core/interaction/input.ts +1 -1
- package/src/core/interaction/setupAutoRaycaster.ts +12 -2
- package/src/core/update.ts +29 -3
- package/src/core/updateObjectProp.ts +16 -2
- package/src/types.ts +6 -0
package/dist/lunchboxjs.js
CHANGED
|
@@ -64,16 +64,30 @@
|
|
|
64
64
|
}
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
const
|
|
67
|
+
const getInnerDimensions = (node) => {
|
|
68
|
+
const computedStyle = getComputedStyle(node);
|
|
69
|
+
const width = node.clientWidth - parseFloat(computedStyle.paddingLeft) - parseFloat(computedStyle.paddingRight);
|
|
70
|
+
const height = node.clientHeight - parseFloat(computedStyle.paddingTop) - parseFloat(computedStyle.paddingBottom);
|
|
71
|
+
return { width, height };
|
|
72
|
+
};
|
|
73
|
+
const prepCanvas = (container, canvasElement, onBeforeUnmount, sizePolicy) => {
|
|
68
74
|
const containerElement = container.value?.domElement;
|
|
69
75
|
if (!containerElement)
|
|
70
76
|
throw new Error('missing container');
|
|
71
77
|
// save...
|
|
72
78
|
// ...and size element
|
|
73
|
-
|
|
79
|
+
const resizeCanvasByPolicy = () => {
|
|
80
|
+
if (sizePolicy === "container") {
|
|
81
|
+
const dims = getInnerDimensions(containerElement);
|
|
82
|
+
resizeCanvas(dims.width, dims.height);
|
|
83
|
+
}
|
|
84
|
+
else
|
|
85
|
+
resizeCanvas();
|
|
86
|
+
};
|
|
87
|
+
resizeCanvasByPolicy();
|
|
74
88
|
// attach listeners
|
|
75
89
|
const observer = new ResizeObserver(([canvas]) => {
|
|
76
|
-
|
|
90
|
+
resizeCanvasByPolicy();
|
|
77
91
|
});
|
|
78
92
|
// window.addEventListener('resize', resizeCanvas)
|
|
79
93
|
if (containerElement) {
|
|
@@ -99,6 +113,7 @@
|
|
|
99
113
|
left: 0,
|
|
100
114
|
width: '100%',
|
|
101
115
|
height: '100%',
|
|
116
|
+
display: 'block',
|
|
102
117
|
};
|
|
103
118
|
};
|
|
104
119
|
const LunchboxWrapper = {
|
|
@@ -116,9 +131,11 @@
|
|
|
116
131
|
r3f: Boolean,
|
|
117
132
|
rendererArguments: Object,
|
|
118
133
|
rendererProperties: Object,
|
|
134
|
+
sizePolicy: String,
|
|
119
135
|
shadow: [Boolean, Object],
|
|
120
136
|
transparent: Boolean,
|
|
121
137
|
zoom: Number,
|
|
138
|
+
updateSource: Object,
|
|
122
139
|
},
|
|
123
140
|
setup(props, context) {
|
|
124
141
|
const canvas = vue.ref();
|
|
@@ -273,7 +290,7 @@
|
|
|
273
290
|
renderer.instance.setPixelRatio(dpr.value);
|
|
274
291
|
globals.dpr.value = dpr.value;
|
|
275
292
|
// prep canvas (sizing, observe, unmount, etc)
|
|
276
|
-
prepCanvas(container, renderer.instance.domElement, vue.onBeforeUnmount);
|
|
293
|
+
prepCanvas(container, renderer.instance.domElement, vue.onBeforeUnmount, props.sizePolicy);
|
|
277
294
|
}
|
|
278
295
|
else {
|
|
279
296
|
throw new Error('missing renderer');
|
|
@@ -298,24 +315,28 @@
|
|
|
298
315
|
camera: camera.instance,
|
|
299
316
|
renderer: renderer.instance,
|
|
300
317
|
scene: scene.instance,
|
|
318
|
+
updateSource: props.updateSource,
|
|
301
319
|
});
|
|
302
320
|
});
|
|
303
321
|
// UNMOUNT
|
|
304
322
|
// ====================
|
|
305
323
|
vue.onBeforeUnmount(() => {
|
|
306
324
|
cancelUpdate();
|
|
325
|
+
cancelUpdateSource();
|
|
307
326
|
});
|
|
308
327
|
// RENDER FUNCTION
|
|
309
328
|
// ====================
|
|
329
|
+
const containerFillStyle = props.sizePolicy === 'container' ? 'static' : 'absolute';
|
|
330
|
+
const canvasFillStyle = props.sizePolicy === 'container' ? 'static' : 'fixed';
|
|
310
331
|
return () => [
|
|
311
332
|
context.slots.default?.() ?? null,
|
|
312
333
|
vue.h('div', {
|
|
313
|
-
style: fillStyle(
|
|
334
|
+
style: fillStyle(containerFillStyle),
|
|
314
335
|
ref: container,
|
|
315
336
|
}, [
|
|
316
337
|
useFallbackRenderer.value
|
|
317
338
|
? vue.h('canvas', {
|
|
318
|
-
style: fillStyle(
|
|
339
|
+
style: fillStyle(canvasFillStyle),
|
|
319
340
|
class: 'lunchbox-canvas',
|
|
320
341
|
ref: canvas,
|
|
321
342
|
})
|
|
@@ -649,6 +670,7 @@
|
|
|
649
670
|
let mouseUpListener;
|
|
650
671
|
const mousePos = vue.ref({ x: Infinity, y: Infinity });
|
|
651
672
|
let autoRaycasterEventsInitialized = false;
|
|
673
|
+
let frameID$1;
|
|
652
674
|
const setupAutoRaycaster = (node) => {
|
|
653
675
|
const instance = node.instance;
|
|
654
676
|
if (!instance)
|
|
@@ -681,8 +703,14 @@
|
|
|
681
703
|
renderer.instance.domElement.addEventListener('mousedown', mouseDownListener);
|
|
682
704
|
renderer.instance.domElement.addEventListener('mouseup', mouseUpListener);
|
|
683
705
|
// TODO: add touch events
|
|
684
|
-
//
|
|
685
|
-
|
|
706
|
+
// process mouse events asynchronously, whenever the mouse state changes
|
|
707
|
+
vue.watch(() => [inputActive.value, mousePos.value.x, mousePos.value.y], () => {
|
|
708
|
+
if (frameID$1)
|
|
709
|
+
cancelAnimationFrame(frameID$1);
|
|
710
|
+
frameID$1 = requestAnimationFrame(() => {
|
|
711
|
+
autoRaycasterBeforeRender();
|
|
712
|
+
});
|
|
713
|
+
});
|
|
686
714
|
// mark complete
|
|
687
715
|
autoRaycasterEventsInitialized = true;
|
|
688
716
|
// cancel setup watcher
|
|
@@ -1011,7 +1039,7 @@
|
|
|
1011
1039
|
};
|
|
1012
1040
|
|
|
1013
1041
|
/** Process props into either themselves or the $attached value */
|
|
1014
|
-
function processProp({ node, prop }) {
|
|
1042
|
+
function processProp({ node, prop, }) {
|
|
1015
1043
|
// return $attachedArray value if needed
|
|
1016
1044
|
if (typeof prop === 'string' && prop.startsWith('$attachedArray')) {
|
|
1017
1045
|
return node.attachedArray[prop.replace('$attachedArray.', '')];
|
|
@@ -1023,10 +1051,12 @@
|
|
|
1023
1051
|
// otherwise, return plain value
|
|
1024
1052
|
return prop;
|
|
1025
1053
|
}
|
|
1026
|
-
function processPropAsArray({ node, prop }) {
|
|
1054
|
+
function processPropAsArray({ node, prop, }) {
|
|
1027
1055
|
const isAttachedArray = typeof prop === 'string' && prop.startsWith('$attachedArray');
|
|
1028
1056
|
const output = processProp({ node, prop });
|
|
1029
|
-
return Array.isArray(output) && isAttachedArray
|
|
1057
|
+
return Array.isArray(output) && isAttachedArray
|
|
1058
|
+
? output
|
|
1059
|
+
: [output];
|
|
1030
1060
|
}
|
|
1031
1061
|
|
|
1032
1062
|
function instantiateThreeObject(node) {
|
|
@@ -1325,16 +1355,34 @@
|
|
|
1325
1355
|
};
|
|
1326
1356
|
|
|
1327
1357
|
let frameID;
|
|
1358
|
+
let watchStopHandle;
|
|
1328
1359
|
const beforeRender = [];
|
|
1329
1360
|
const afterRender = [];
|
|
1330
|
-
const
|
|
1331
|
-
|
|
1361
|
+
const requestUpdate = (opts) => {
|
|
1362
|
+
cancelUpdate();
|
|
1332
1363
|
frameID = requestAnimationFrame(() => update({
|
|
1333
1364
|
app: opts.app,
|
|
1334
1365
|
renderer: ensureRenderer.value?.instance,
|
|
1335
1366
|
scene: ensuredScene.value.instance,
|
|
1336
1367
|
camera: ensuredCamera.value?.instance,
|
|
1368
|
+
updateSource: opts.updateSource,
|
|
1337
1369
|
}));
|
|
1370
|
+
};
|
|
1371
|
+
const update = (opts) => {
|
|
1372
|
+
if (opts.updateSource) {
|
|
1373
|
+
if (!watchStopHandle) {
|
|
1374
|
+
// request next frame only when state changes
|
|
1375
|
+
watchStopHandle = vue.watch(opts.updateSource, () => {
|
|
1376
|
+
requestUpdate(opts);
|
|
1377
|
+
}, {
|
|
1378
|
+
deep: true,
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
else {
|
|
1383
|
+
// request next frame on a continuous loop
|
|
1384
|
+
requestUpdate(opts);
|
|
1385
|
+
}
|
|
1338
1386
|
// prep options
|
|
1339
1387
|
const { app, renderer, scene, camera } = opts;
|
|
1340
1388
|
// BEFORE RENDER
|
|
@@ -1397,6 +1445,10 @@
|
|
|
1397
1445
|
if (frameID)
|
|
1398
1446
|
cancelAnimationFrame(frameID);
|
|
1399
1447
|
};
|
|
1448
|
+
const cancelUpdateSource = () => {
|
|
1449
|
+
if (watchStopHandle)
|
|
1450
|
+
watchStopHandle();
|
|
1451
|
+
};
|
|
1400
1452
|
|
|
1401
1453
|
/** Update a single prop on a given node. */
|
|
1402
1454
|
function updateObjectProp({ node, key, value, }) {
|
|
@@ -1434,6 +1486,7 @@
|
|
|
1434
1486
|
liveProperty = liveProperty = lodash.get(target, fullPath);
|
|
1435
1487
|
}
|
|
1436
1488
|
// change property
|
|
1489
|
+
// first, save as array in case we need to spread it
|
|
1437
1490
|
if (liveProperty && lodash.isNumber(value) && liveProperty.setScalar) {
|
|
1438
1491
|
// if value is a number and the property has a `setScalar` method, use that
|
|
1439
1492
|
liveProperty.setScalar(value);
|
|
@@ -1444,8 +1497,18 @@
|
|
|
1444
1497
|
target[finalKey].set(...nextValueAsArray);
|
|
1445
1498
|
}
|
|
1446
1499
|
else if (typeof liveProperty === 'function') {
|
|
1447
|
-
//
|
|
1448
|
-
|
|
1500
|
+
// some function properties are set rather than called, so let's handle them
|
|
1501
|
+
if (finalKey.toLowerCase() === 'onbeforerender' ||
|
|
1502
|
+
finalKey.toLowerCase() === 'onafterrender') {
|
|
1503
|
+
target[finalKey] = value;
|
|
1504
|
+
}
|
|
1505
|
+
else {
|
|
1506
|
+
if (!Array.isArray(value)) {
|
|
1507
|
+
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" />');
|
|
1508
|
+
}
|
|
1509
|
+
// if property is a function, let's try calling it
|
|
1510
|
+
liveProperty.bind(node.instance)(...value);
|
|
1511
|
+
}
|
|
1449
1512
|
// pass the result to the parent
|
|
1450
1513
|
// const parent = node.parentNode
|
|
1451
1514
|
// if (parent) {
|
package/dist/lunchboxjs.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vue"),require("three"),require("lodash")):"function"==typeof define&&define.amd?define(["exports","vue","three","lodash"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).LunchboxRenderer={},e.vue,e.three,e.lodash)}(this,(function(e,t,n,r){"use strict";function o(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}})),t.default=e,Object.freeze(t)}var a=o(n);const s=[],i=(e,n)=>{const r=W.value?.instance,o=I.value.instance,a=D.value;if(!r?.domElement||!o||!a)return;const s=(e=e??window.innerWidth)/(n=n??window.innerHeight);if("perspectivecamera"===a.type?.toLowerCase()){const e=a.instance;e.aspect=s,e.updateProjectionMatrix()}else if("orthographiccamera"===a.type?.toLowerCase()){const t=a.instance,r=n/e;t.top=10*r,t.bottom=10*-r,t.right=10,t.left=-10,t.updateProjectionMatrix()}else console.log("TODO: non-ortho or perspective camera");r.setSize(e,n),o&&a.instance&&r.render(t.toRaw(o),t.toRaw(a.instance))},c=e=>({position:e,top:0,right:0,bottom:0,left:0,width:"100%",height:"100%"}),d={name:"Lunchbox",props:{background:String,cameraArgs:Array,cameraLook:Array,cameraLookAt:Array,cameraPosition:Array,dpr:Number,ortho:Boolean,orthographic:Boolean,r3f:Boolean,rendererArguments:Object,rendererProperties:Object,shadow:[Boolean,Object],transparent:Boolean,zoom:Number},setup(e,n){const o=t.ref(),s=t.ref(!0),d=t.ref(e.dpr??-1),u=t.ref();let l,p,m;return e.r3f&&a?.ColorManagement&&(a.ColorManagement.legacyMode=!1),t.onMounted((()=>{if(!o.value)throw new Error("missing canvas");if(l=N(["WebGLRenderer"]),l)s.value=!1,U.value=!0;else{const t={alpha:e.transparent,antialias:!0,canvas:o.value.domElement,powerPreference:e.r3f?"high-performance":"default",...e.rendererArguments??{}};W.value=K({type:"WebGLRenderer",uuid:F,props:{args:[t]}}),U.value=!0;const n=W;e.r3f&&n.value.instance&&(n.value.instance.outputEncoding=a.sRGBEncoding,n.value.instance.toneMapping=a.ACESFilmicToneMapping);const s={shadow:e.shadow};n.value.instance&&s?.shadow&&(n.value.instance.shadowMap.enabled=!0,"object"==typeof s.shadow&&(n.value.instance.shadowMap.type=s.shadow.type)),e.rendererProperties&&Object.keys(e.rendererProperties).forEach((t=>{r.set(n.value,t,e.rendererProperties[t])})),l=n.value}if(p=N(["PerspectiveCamera","OrthographicCamera"]),p?S.value=!0:(e.ortho||e.orthographic?D.value=K({props:{args:e.cameraArgs??[]},type:"OrthographicCamera",uuid:j}):D.value=K({props:{args:e.cameraArgs??[e.r3f?75:45,.5625,1,1e3]},type:"PerspectiveCamera",uuid:j}),S.value=!0,p=D.value),!p.instance)throw new Error("Error creating camera.");if(p&&e.cameraPosition&&p.instance.position.set(...e.cameraPosition),p&&(e.cameraLookAt||e.cameraLook)){const t=e.cameraLookAt||e.cameraLook;p.instance.lookAt(...t)}if(p&&void 0!==e.zoom&&(p.instance.zoom=e.zoom),m=I.value,m&&m.instance&&e.background&&(m.instance.background=new a.Color(e.background)),-1===d.value&&(d.value=window.devicePixelRatio),!l?.instance)throw new Error("missing renderer");l.instance.setPixelRatio(d.value),he.dpr.value=d.value,((e,t,n)=>{const r=e.value?.domElement;if(!r)throw new Error("missing container");i();const o=new ResizeObserver((([e])=>{i()}));r&&o.observe(r),n((()=>{t&&o.unobserve(t)}))})(u,l.instance.domElement,t.onBeforeUnmount);const n=t.getCurrentInstance().appContext.app;for(let e of te)e({app:n,camera:p.instance,renderer:l.instance,scene:m.instance});ae({app:n,camera:p.instance,renderer:l.instance,scene:m.instance})})),t.onBeforeUnmount((()=>{ie()})),()=>[n.slots.default?.()??null,t.h("div",{style:c("absolute"),ref:u},[s.value?t.h("canvas",{style:c("fixed"),class:"lunchbox-canvas",ref:o}):null])]}},u={},l=["canvas","div","LunchboxWrapper"],p={...["mesh","instancedMesh","scene","sprite","object3D","instancedBufferGeometry","bufferGeometry","boxBufferGeometry","circleBufferGeometry","coneBufferGeometry","cylinderBufferGeometry","dodecahedronBufferGeometry","extrudeBufferGeometry","icosahedronBufferGeometry","latheBufferGeometry","octahedronBufferGeometry","parametricBufferGeometry","planeBufferGeometry","polyhedronBufferGeometry","ringBufferGeometry","shapeBufferGeometry","sphereBufferGeometry","tetrahedronBufferGeometry","textBufferGeometry","torusBufferGeometry","torusKnotBufferGeometry","tubeBufferGeometry","wireframeGeometry","parametricGeometry","tetrahedronGeometry","octahedronGeometry","icosahedronGeometry","dodecahedronGeometry","polyhedronGeometry","tubeGeometry","torusKnotGeometry","torusGeometry","sphereGeometry","ringGeometry","planeGeometry","latheGeometry","shapeGeometry","extrudeGeometry","edgesGeometry","coneGeometry","cylinderGeometry","circleGeometry","boxGeometry","material","shadowMaterial","spriteMaterial","rawShaderMaterial","shaderMaterial","pointsMaterial","meshPhysicalMaterial","meshStandardMaterial","meshPhongMaterial","meshToonMaterial","meshNormalMaterial","meshLambertMaterial","meshDepthMaterial","meshDistanceMaterial","meshBasicMaterial","meshMatcapMaterial","lineDashedMaterial","lineBasicMaterial","light","spotLightShadow","spotLight","pointLight","rectAreaLight","hemisphereLight","directionalLightShadow","directionalLight","ambientLight","lightShadow","ambientLightProbe","hemisphereLightProbe","lightProbe","texture","videoTexture","dataTexture","dataTexture3D","compressedTexture","cubeTexture","canvasTexture","depthTexture","textureLoader","group","catmullRomCurve3","points","cameraHelper","camera","perspectiveCamera","orthographicCamera","cubeCamera","arrayCamera","webGLRenderer"].map((e=>t.defineComponent({inheritAttrs:!1,name:e,setup:(n,r)=>()=>t.h(e,r.attrs,r.slots?.default?.()||[])}))).reduce(((e,t)=>(e[t.name]=t,e)),{}),Lunchbox:d};const m=e=>e?.$el&&e?.$el?.hasOwnProperty?.("instance"),h=e=>{if("domMeta"===e?.metaType)return!0;const t="string"==typeof e?e:e?.type;return l.includes(t??"")},f=e=>"standardMeta"===e?.metaType,y=e=>e.isLunchboxRootNode,v=[],g=t.ref(!1);function b({node:e,key:n,value:r}){var o;if(e.eventListeners[n]||(e.eventListeners[n]=[]),e.eventListenerRemoveFunctions[n]||(e.eventListenerRemoveFunctions[n]=[]),e.eventListeners[n].push(r),x.includes(n)&&(z.value,e.instance&&!v.includes(e)&&(o=e,v.push(o),e.eventListenerRemoveFunctions[n].push((()=>(e=>{const t=v.indexOf(e);-1!==t&&v.splice(t,1)})(e))))),"onClick"===n||"onPointerDown"===n||"onPointerUp"===n){const r=t.watch((()=>g.value),(t=>{const r=L.map((e=>e.element)).findIndex((t=>t.instance&&t.instance.uuid===e.instance?.uuid));-1!==r&&((!t||"onClick"!==n&&"onPointerDown"!==n)&&(t||"onPointerUp"!==n)||e.eventListeners[n].forEach((e=>{e({intersection:L[r].intersection})})))}));e.eventListenerRemoveFunctions[n].push(r)}return e}const x=["onClick","onPointerUp","onPointerDown","onPointerOver","onPointerOut","onPointerEnter","onPointerLeave","onPointerMove"];let R,w,A;const C=t.ref({x:1/0,y:1/0});let E=!1;let L=[];const P=()=>{const e=z.value?.instance,t=D.value?.instance;if(!e||!t)return;e.setFromCamera(he.mousePos.value,t);const n=e.intersectObjects(v.map((e=>e.instance)));let r=[],o=[],a=[];r=L.map((e=>e.intersection)),n?.forEach((e=>{if(-1===L.findIndex((t=>t.intersection.object===e.object))){const t=v.find((t=>t.instance?.uuid===e.object.uuid));t&&o.push({element:t,intersection:e})}else{const t=v.find((t=>t.instance?.uuid===e.object.uuid));t&&a.push({element:t,intersection:e})}const t=r.findIndex((t=>t.object.uuid===e.object.uuid));-1!==t&&r.splice(t,1)}));const s=r.map((e=>({element:v.find((t=>t.instance?.uuid===e.object.uuid)),intersection:e})));o.forEach((({element:e,intersection:t})=>{M({element:e,eventKeys:["onPointerEnter"],intersection:t})})),a.forEach((({element:e,intersection:t})=>{M({element:e,eventKeys:["onPointerOver","onPointerMove"],intersection:t})})),s.forEach((({element:e,intersection:t})=>{M({element:e,eventKeys:["onPointerLeave","onPointerOut"],intersection:t})})),L=[].concat(o,a)},M=({element:e,eventKeys:t,intersection:n})=>{e&&t.forEach((t=>{e.eventListeners[t]&&e.eventListeners[t].forEach((e=>{e({intersection:n})}))}))};function B(t={}){return e.lunchboxTree||(e.lunchboxTree=new J.RendererRootNode(t)),e.lunchboxTree}function N(e){Array.isArray(e)||(e=[e]);for(let t of e)if(T[t])return T[t];for(let n of e){const e=G[n]||s.find((e=>e.type?.toLowerCase()===n.toLowerCase()));if(!(t=e,"RendererNode"!==t?.minidomType||!1!==e.props["is-default"]&&!1!=!e.props.isDefault))return null;if(e){const t=e;return G[n]=t,t}}var t;return null}e.lunchboxTree=void 0;const G=t.reactive({}),T=t.reactive({});function O(e,n,r={},o=null){Array.isArray(e)||(e=[e]);for(let t of e)G[t]||(G[t]=null),T[t]||(T[t]=null);return t.computed({get(){const t=N(e);if(t)return t;const a=B(),s=K({type:e[0],uuid:n,props:r});return a.addChild(s),G[e[0]]=s,o&&o(s),s},set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);T[n]=e}})}const j="FALLBACK_CAMERA",k=O(["PerspectiveCamera","OrthographicCamera"],j,{args:[45,.5625,1,1e3]}),S=t.ref(!1),D=t.computed({get:()=>S.value?k.value:null,set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);T[n]=e}}),F="FALLBACK_RENDERER",$=O(["WebGLRenderer"],F,{}),U=t.ref(!1),W=t.computed({get:()=>U.value?$.value:null,set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);T[n]=e}}),I=O("Scene","FALLBACK_SCENE"),z=O("Raycaster","AUTO_RAYCASTER",{},(e=>(e=>{if(!e.instance)return;let n=null;n=t.watch((()=>W.value),(e=>{e?.instance&&(E||(R=t=>{const n=(e.instance.domElement.width??1)/he.dpr.value,r=(e.instance.domElement.height??1)/he.dpr.value;C.value.x=t.offsetX/n*2-1,C.value.y=-t.offsetY/r*2+1},w=()=>g.value=!0,A=()=>g.value=!1,e.instance.domElement.addEventListener("mousemove",R),e.instance.domElement.addEventListener("mousedown",w),e.instance.domElement.addEventListener("mouseup",A),se(P),E=!0),n&&n())}),{immediate:!0})})(e)));function K(e={},t={}){const n={attached:e.attached??[],attachedArray:e.attachedArray??{},instance:e.instance??null},r=new J.RendererStandardNode({...e,...n,metaType:"standardMeta"});return!r.type||y(r)||r.instance||(r.instance=function(e){if(!e.type)return null;const t=e.type[0].toUpperCase()+e.type.slice(1),n=u[e.type]||a[t];if(!n)throw`${t} is not part of the THREE namespace! Did you forget to extend? import {extend} from 'lunchbox'; extend({app, YourComponent, ...})`;const r=(e.props.args??[]).map((t=>function({node:e,prop:t}){const n="string"==typeof t&&t.startsWith("$attachedArray"),r=function({node:e,prop:t}){if("string"==typeof t&&t.startsWith("$attachedArray"))return e.attachedArray[t.replace("$attachedArray.","")];if("string"==typeof t&&t.startsWith("$attached"))return e.attached[t.replace("$attached.","")];return t}({node:e,prop:t});return Array.isArray(r)&&n?r:[r]}({node:e,prop:t})));let o=[];r.forEach((e=>{o=o.concat(e)}));return new n(...o)}({...r,props:{...r.props,...t}})),"scene"===r.type?.toLowerCase()?I.value=r:r.type?.toLowerCase().endsWith("camera")&&(D.value=r),r}const _=e=>t.defineComponent({inheritAttrs:!1,name:e,render(){return t.h(e,this.$attrs,this.$slots?.default?.()||[])}});var V,q=new Uint8Array(16);function H(){if(!V&&!(V="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return V(q)}var Y=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;function X(e){return"string"==typeof e&&Y.test(e)}for(var J,Q=[],Z=0;Z<256;++Z)Q.push((Z+256).toString(16).substr(1));function ee(e,t,n){var r=(e=e||{}).random||(e.rng||H)();if(r[6]=15&r[6]|64,r[8]=63&r[8]|128,t){n=n||0;for(var o=0;o<16;++o)t[n+o]=r[o];return t}return function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=(Q[e[t+0]]+Q[e[t+1]]+Q[e[t+2]]+Q[e[t+3]]+"-"+Q[e[t+4]]+Q[e[t+5]]+"-"+Q[e[t+6]]+Q[e[t+7]]+"-"+Q[e[t+8]]+Q[e[t+9]]+"-"+Q[e[t+10]]+Q[e[t+11]]+Q[e[t+12]]+Q[e[t+13]]+Q[e[t+14]]+Q[e[t+15]]).toLowerCase();if(!X(n))throw TypeError("Stringified UUID is invalid");return n}(r)}!function(e){e.BaseNode=class{constructor(e={},t){this.parentNode=e?.parentNode??t??null,this.minidomType="MinidomBaseNode",this.uuid=e?.uuid??ee(),s.push(this)}uuid;parentNode;get nextSibling(){if(!this.parentNode)return null;const e=this.parentNode.children.findIndex((e=>e.uuid===this.uuid));return-1!==e&&e<this.parentNode.children.length-1?this.parentNode.children[e+1]:null}insertBefore(e,t){e.removeAsChildFromAnyParents(),e.parentNode=this;const n=this.children.findIndex((e=>e.uuid===t?.uuid));-1!==n?this.children.splice(n,0,e):this.children.push(e)}removeChild(e){const t=this.children.findIndex((t=>t?.uuid===e?.uuid));-1!==t&&this.children.splice(t,1)}children=[];addChild(e){return e&&(e.removeAsChildFromAnyParents(),e.parentNode=this,this.insertBefore(e,null)),this}getPath(){const e=[];let t=this;for(;t;)e.unshift(t),t=t.parentNode;return e}drop(){this.parentNode=null,this.removeAsChildFromAnyParents()}walk(e){const t=[this,...this.children],n=[];let r=!0;for(;t.length&&r;){const o=t.shift();if(o){if(n.includes(o))continue;n.push(o),t.push(...o.children.filter((e=>!n.includes(e)))),r=e(o)}else r=!1}}minidomType;removeAsChildFromAnyParents(){s.forEach((e=>e.removeChild(this)))}};class t extends e.BaseNode{constructor(e={},t){super(e,t),this.minidomType="RendererNode",this.eventListeners={},this.eventListenerRemoveFunctions={},this.name=e.name??"",this.metaType=e.metaType??"standardMeta",this.props=e.props??[],this.type=e.type??""}eventListeners;eventListenerRemoveFunctions;name;metaType;props;type;drop(){super.drop(),Object.keys(this.eventListenerRemoveFunctions).forEach((e=>{this.eventListenerRemoveFunctions[e].forEach((e=>e()))}))}}e.RendererBaseNode=t;class n extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.domElement=e.domElement??document.createElement("div")}domElement;isLunchboxRootNode=!0}e.RendererRootNode=n;class r extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.text=e.text??""}text}e.RendererCommentNode=r;class o extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.domElement=e.domElement??document.createElement("div")}domElement}e.RendererDomNode=o;class a extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.text=e.text??""}text}e.RendererTextNode=a;class i extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.attached=e.attached??[],this.attachedArray=e.attachedArray??{},this.instance=e.instance??null}attached;attachedArray;instance}e.RendererStandardNode=i}(J||(J={}));(new J.RendererRootNode).minidomType="RootNode";const te=[];let ne;const re=[],oe=[],ae=e=>{ne=requestAnimationFrame((()=>ae({app:e.app,renderer:W.value?.instance,scene:I.value.instance,camera:D.value?.instance})));const{app:n,renderer:r,scene:o,camera:a}=e;re.forEach((t=>{t&&t(e)})),r&&o&&a&&(n.customRender?n.customRender(e):r.render(t.toRaw(o),t.toRaw(a))),oe.forEach((t=>{t&&t(e)}))},se=(e,t=1/0)=>{t===1/0?re.push(e):re.splice(t,0,e)},ie=()=>{ne&&cancelAnimationFrame(ne)};const ce={x:"position.x",y:"position.y",z:"position.z"},de=["","parameters"],ue=["args","attach","attachArray","is.default","isDefault","key","onAdded","ref","src"],le=["geometry","material"];function pe(e,t,n,r){const o=r??e.instance,a=t.instance;e.props.attach===n&&(t.attached={[n]:o,...t.attached||{}},a[n]=r??e.instance),e.props.attachArray===n&&(t.attachedArray[e.props.attachArray]||(t.attachedArray[e.props.attachArray]=[]),t.attachedArray[e.props.attachArray].push(o),a[n]=[a[n]])}const me={createElement:(e,t,n,r)=>{const o={type:e};r&&(o.props=r);if(h(e)){const e=function(e={}){const t={domElement:document.createElement(e.type??"")};return new J.RendererDomNode({...t,...e,metaType:"domMeta"})}(o);return e}const a=K(o);return le.forEach((t=>{e.toLowerCase().endsWith(t)&&(a.props.attach=t)})),a},createText:e=>function(e={}){const t={text:e.text??""};return new J.RendererTextNode({...e,...t,metaType:"textMeta"})}({text:e}),createComment:e=>function(e={}){const t={text:e.text??""};return new J.RendererCommentNode({...t,...e,metaType:"commentMeta"})}({text:e}),insert:(e,t,n)=>{let r=t??B();if(r.insertBefore(e,n),"commentMeta"!==e.metaType&&"textMeta"!==e.metaType&&(h(e)&&(h(t)||y(t))&&t.domElement.appendChild(e.domElement),f(e))){let n=r.metaType;if("textMeta"===n||"commentMeta"===n){const e=r.getPath();for(let t=e.length-1;t>=0;t--)if("textMeta"!==e[t].metaType&&"commentMeta"!==e[t].metaType){r=e[t];break}}if("standardMeta"===e.metaType&&"scene"!==e.type&&y(r)){const t=I.value;t.instance&&e&&t.addChild(e),e.instance&&e.instance.isObject3D&&t.instance&&t!==e&&t.instance.add(e.instance)}else f(e)&&e.instance?.isObject3D&&f(r)&&r.instance?.isObject3D&&r.instance?.add?.(e.instance);if(e?.props?.attach&&f(t)&&t?.instance){e.type?.toLowerCase().endsWith("loader")&&e.props.src&&(e.props.attach||e.props.attachArray)?function(e,t){const n=e.instance;if(t.attached=t.attached||{},t.attachedArray=t.attachedArray||{},!e.props.attach)return;if("textureloader"===e.type?.toLowerCase()){const r=n.load(e.props.src);pe(e,t,e.props.attach,r)}else n.load(e.props.src,(n=>{pe(e,t,e.props.attach,n)}),null,(e=>{throw new Error(e)}))}(e,t):pe(e,t,e.props.attach)}e.props?.onAdded&&e.props.onAdded({instance:e.instance})}},nextSibling(e){const t=e.nextSibling;return t||null},parentNode(e){const t=e.parentNode;return t||null},patchProp(e,t,n,o){h(e)?"style"===t?Object.keys(o).forEach((t=>{e.domElement.style[t]=o[t]})):e.domElement.setAttribute(t,o):y(e)||t.startsWith("$")||function({node:e,key:t,value:n}){if((e=>["onClick","onContextMenu","onDoubleClick","onPointerUp","onPointerDown","onPointerOver","onPointerOut","onPointerEnter","onPointerLeave","onPointerMove","onWheel"].includes(e))(t))return b({node:e,key:t,value:n});const o=t.replace(/-/g,"."),a=ce[o]||o;if(ue.includes(t)||ue.includes(a))return e;if(!f(e))return e;if("string"==typeof n&&n.startsWith("$attached")){const t=n.replace("$attached.","");n=r.get(e.attached,t,null)}const s=e.instance;if(!s)return e;let i;for(let e=0;e<de.length&&!i;e++){const t=[de[e],a].filter(Boolean).join(".");i=i=r.get(s,t)}if(i&&r.isNumber(n)&&i.setScalar)i.setScalar(n);else if(i&&i.set){const e=Array.isArray(n)?n:[n];s[a].set(...e)}else"function"==typeof i?i.bind(e.instance)(...n):void 0!==r.get(s,a,void 0)?r.set(s,a,""===n||n):console.log(`No property ${a} found on ${s}`);const c=s?.texture?.type||s?.type;if("string"==typeof c){const e=c.toLowerCase();switch(!0){case e.includes("material"):s.needsUpdate=!0;break;case e.includes("camera")&&s.updateProjectionMatrix:s.updateProjectionMatrix()}}}({node:e,key:t,value:o})},remove:e=>{if(!e)return;const t=Object.keys(T),n=[];e.walk((e=>(n.push(e),!0))),n.forEach((e=>{const n=t.find((t=>T[t]?.uuid===e.uuid));if(n&&(T[n]=null),f(e)){e.instance?.removeFromParent?.();const t="scene"!==e.type&&e.instance?.dispose;t&&t.bind(e.instance)(),e.instance=null}e.drop();const r=s.findIndex((t=>t.uuid===e.uuid));-1!==r&&s.splice(r,1)}))},setElementText(){},setText(){}},he={dpr:t.ref(1),inputActive:g,mousePos:C},fe=t.computed((()=>D.value?.instance??null));const ye=t.computed((()=>W.value?.instance??null));const ve=t.computed((()=>I.value.instance));let ge=null,be=null;e.camera=fe,e.clearCustomRender=()=>{ge?ge.clearCustomRender():be=null},e.createApp=e=>{ge=t.createRenderer(me).createApp(e),Object.keys(p).forEach((e=>{ge?.component(e,p[e])}));const{mount:n}=ge;return ge.mount=(e,...t)=>{const r=B({domElement:"string"==typeof e?document.querySelector(e):e,isLunchboxRootNode:!0,name:"root",metaType:"rootMeta",type:"root",uuid:"LUNCHBOX_ROOT"});ge.rootNode=r;return n(r,...t)},ge.extend=e=>((({app:e,...t})=>{Object.keys(t).forEach((n=>{e.component(n,_(n)),u[n]=t[n]}))})({app:ge,...e}),ge),ge.setCustomRender=e=>{ge.customRender=e},be&&(ge.setCustomRender(be),be=null),ge.clearCustomRender=()=>{ge.customRender=null},ge},e.find=function(e){return e=t.isRef(e)?e.value:e,f(e)?e?.instance:m(e)?e?.$el?.instance:t.isVNode(e)?e.el?.instance:null},e.globals=he,e.offAfterRender=e=>{if(isFinite(e))oe.splice(e,1);else{const t=oe.findIndex((t=>t==e));oe.splice(t,1)}},e.offBeforeRender=e=>{if(isFinite(e))re.splice(e,1);else{const t=re.findIndex((t=>t==e));re.splice(t,1)}},e.onAfterRender=(e,t=1/0)=>{t===1/0?oe.push(e):oe.splice(t,0,e)},e.onBeforeRender=se,e.onStart=(e,t=1/0)=>{t===1/0?te.push(e):te.splice(t,0,e)},e.renderer=ye,e.scene=ve,e.setCustomRender=e=>{ge?ge.setCustomRender(e):be=e},e.useCamera=function(e){return t.watch(fe,(t=>{t&&e(t)}),{immediate:!0})},e.useRenderer=function(e){return t.watch(ye,(t=>{t&&e(t)}),{immediate:!0})},e.useScene=function(e){return t.watch(ve,(t=>{t&&e(t)}),{immediate:!0})},Object.defineProperty(e,"__esModule",{value:!0})}));
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vue"),require("three"),require("lodash")):"function"==typeof define&&define.amd?define(["exports","vue","three","lodash"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).LunchboxRenderer={},e.vue,e.three,e.lodash)}(this,(function(e,t,n,r){"use strict";function o(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}})),t.default=e,Object.freeze(t)}var a=o(n);const s=[],i=(e,n)=>{const r=I.value?.instance,o=K.value.instance,a=$.value;if(!r?.domElement||!o||!a)return;const s=(e=e??window.innerWidth)/(n=n??window.innerHeight);if("perspectivecamera"===a.type?.toLowerCase()){const e=a.instance;e.aspect=s,e.updateProjectionMatrix()}else if("orthographiccamera"===a.type?.toLowerCase()){const t=a.instance,r=n/e;t.top=10*r,t.bottom=10*-r,t.right=10,t.left=-10,t.updateProjectionMatrix()}else console.log("TODO: non-ortho or perspective camera");r.setSize(e,n),o&&a.instance&&r.render(t.toRaw(o),t.toRaw(a.instance))},c=(e,t,n,r)=>{const o=e.value?.domElement;if(!o)throw new Error("missing container");const a=()=>{if("container"===r){const e=(e=>{const t=getComputedStyle(e);return{width:e.clientWidth-parseFloat(t.paddingLeft)-parseFloat(t.paddingRight),height:e.clientHeight-parseFloat(t.paddingTop)-parseFloat(t.paddingBottom)}})(o);i(e.width,e.height)}else i()};a();const s=new ResizeObserver((([e])=>{a()}));o&&s.observe(o),n((()=>{t&&s.unobserve(t)}))},d=e=>({position:e,top:0,right:0,bottom:0,left:0,width:"100%",height:"100%",display:"block"}),u={name:"Lunchbox",props:{background:String,cameraArgs:Array,cameraLook:Array,cameraLookAt:Array,cameraPosition:Array,dpr:Number,ortho:Boolean,orthographic:Boolean,r3f:Boolean,rendererArguments:Object,rendererProperties:Object,sizePolicy:String,shadow:[Boolean,Object],transparent:Boolean,zoom:Number,updateSource:Object},setup(e,n){const o=t.ref(),s=t.ref(!0),i=t.ref(e.dpr??-1),u=t.ref();let l,p,m;e.r3f&&a?.ColorManagement&&(a.ColorManagement.legacyMode=!1),t.onMounted((()=>{if(!o.value)throw new Error("missing canvas");if(l=T(["WebGLRenderer"]),l)s.value=!1,z.value=!0;else{const t={alpha:e.transparent,antialias:!0,canvas:o.value.domElement,powerPreference:e.r3f?"high-performance":"default",...e.rendererArguments??{}};I.value=q({type:"WebGLRenderer",uuid:U,props:{args:[t]}}),z.value=!0;const n=I;e.r3f&&n.value.instance&&(n.value.instance.outputEncoding=a.sRGBEncoding,n.value.instance.toneMapping=a.ACESFilmicToneMapping);const s={shadow:e.shadow};n.value.instance&&s?.shadow&&(n.value.instance.shadowMap.enabled=!0,"object"==typeof s.shadow&&(n.value.instance.shadowMap.type=s.shadow.type)),e.rendererProperties&&Object.keys(e.rendererProperties).forEach((t=>{r.set(n.value,t,e.rendererProperties[t])})),l=n.value}if(p=T(["PerspectiveCamera","OrthographicCamera"]),p?D.value=!0:(e.ortho||e.orthographic?$.value=q({props:{args:e.cameraArgs??[]},type:"OrthographicCamera",uuid:S}):$.value=q({props:{args:e.cameraArgs??[e.r3f?75:45,.5625,1,1e3]},type:"PerspectiveCamera",uuid:S}),D.value=!0,p=$.value),!p.instance)throw new Error("Error creating camera.");if(p&&e.cameraPosition&&p.instance.position.set(...e.cameraPosition),p&&(e.cameraLookAt||e.cameraLook)){const t=e.cameraLookAt||e.cameraLook;p.instance.lookAt(...t)}if(p&&void 0!==e.zoom&&(p.instance.zoom=e.zoom),m=K.value,m&&m.instance&&e.background&&(m.instance.background=new a.Color(e.background)),-1===i.value&&(i.value=window.devicePixelRatio),!l?.instance)throw new Error("missing renderer");l.instance.setPixelRatio(i.value),ge.dpr.value=i.value,c(u,l.instance.domElement,t.onBeforeUnmount,e.sizePolicy);const n=t.getCurrentInstance().appContext.app;for(let e of re)e({app:n,camera:p.instance,renderer:l.instance,scene:m.instance});de({app:n,camera:p.instance,renderer:l.instance,scene:m.instance,updateSource:e.updateSource})})),t.onBeforeUnmount((()=>{ue(),le()}));const h="container"===e.sizePolicy?"static":"absolute",f="container"===e.sizePolicy?"static":"fixed";return()=>[n.slots.default?.()??null,t.h("div",{style:d(h),ref:u},[s.value?t.h("canvas",{style:d(f),class:"lunchbox-canvas",ref:o}):null])]}},l={},p=["canvas","div","LunchboxWrapper"],m={...["mesh","instancedMesh","scene","sprite","object3D","instancedBufferGeometry","bufferGeometry","boxBufferGeometry","circleBufferGeometry","coneBufferGeometry","cylinderBufferGeometry","dodecahedronBufferGeometry","extrudeBufferGeometry","icosahedronBufferGeometry","latheBufferGeometry","octahedronBufferGeometry","parametricBufferGeometry","planeBufferGeometry","polyhedronBufferGeometry","ringBufferGeometry","shapeBufferGeometry","sphereBufferGeometry","tetrahedronBufferGeometry","textBufferGeometry","torusBufferGeometry","torusKnotBufferGeometry","tubeBufferGeometry","wireframeGeometry","parametricGeometry","tetrahedronGeometry","octahedronGeometry","icosahedronGeometry","dodecahedronGeometry","polyhedronGeometry","tubeGeometry","torusKnotGeometry","torusGeometry","sphereGeometry","ringGeometry","planeGeometry","latheGeometry","shapeGeometry","extrudeGeometry","edgesGeometry","coneGeometry","cylinderGeometry","circleGeometry","boxGeometry","material","shadowMaterial","spriteMaterial","rawShaderMaterial","shaderMaterial","pointsMaterial","meshPhysicalMaterial","meshStandardMaterial","meshPhongMaterial","meshToonMaterial","meshNormalMaterial","meshLambertMaterial","meshDepthMaterial","meshDistanceMaterial","meshBasicMaterial","meshMatcapMaterial","lineDashedMaterial","lineBasicMaterial","light","spotLightShadow","spotLight","pointLight","rectAreaLight","hemisphereLight","directionalLightShadow","directionalLight","ambientLight","lightShadow","ambientLightProbe","hemisphereLightProbe","lightProbe","texture","videoTexture","dataTexture","dataTexture3D","compressedTexture","cubeTexture","canvasTexture","depthTexture","textureLoader","group","catmullRomCurve3","points","cameraHelper","camera","perspectiveCamera","orthographicCamera","cubeCamera","arrayCamera","webGLRenderer"].map((e=>t.defineComponent({inheritAttrs:!1,name:e,setup:(n,r)=>()=>t.h(e,r.attrs,r.slots?.default?.()||[])}))).reduce(((e,t)=>(e[t.name]=t,e)),{}),Lunchbox:u};const h=e=>e?.$el&&e?.$el?.hasOwnProperty?.("instance"),f=e=>{if("domMeta"===e?.metaType)return!0;const t="string"==typeof e?e:e?.type;return p.includes(t??"")},y=e=>"standardMeta"===e?.metaType,v=e=>e.isLunchboxRootNode,g=[],b=t.ref(!1);function x({node:e,key:n,value:r}){var o;if(e.eventListeners[n]||(e.eventListeners[n]=[]),e.eventListenerRemoveFunctions[n]||(e.eventListenerRemoveFunctions[n]=[]),e.eventListeners[n].push(r),w.includes(n)&&(_.value,e.instance&&!g.includes(e)&&(o=e,g.push(o),e.eventListenerRemoveFunctions[n].push((()=>(e=>{const t=g.indexOf(e);-1!==t&&g.splice(t,1)})(e))))),"onClick"===n||"onPointerDown"===n||"onPointerUp"===n){const r=t.watch((()=>b.value),(t=>{const r=M.map((e=>e.element)).findIndex((t=>t.instance&&t.instance.uuid===e.instance?.uuid));-1!==r&&((!t||"onClick"!==n&&"onPointerDown"!==n)&&(t||"onPointerUp"!==n)||e.eventListeners[n].forEach((e=>{e({intersection:M[r].intersection})})))}));e.eventListenerRemoveFunctions[n].push(r)}return e}const w=["onClick","onPointerUp","onPointerDown","onPointerOver","onPointerOut","onPointerEnter","onPointerLeave","onPointerMove"];let A,R,C;const E=t.ref({x:1/0,y:1/0});let L,P=!1;let M=[];const B=()=>{const e=_.value?.instance,t=$.value?.instance;if(!e||!t)return;e.setFromCamera(ge.mousePos.value,t);const n=e.intersectObjects(g.map((e=>e.instance)));let r=[],o=[],a=[];r=M.map((e=>e.intersection)),n?.forEach((e=>{if(-1===M.findIndex((t=>t.intersection.object===e.object))){const t=g.find((t=>t.instance?.uuid===e.object.uuid));t&&o.push({element:t,intersection:e})}else{const t=g.find((t=>t.instance?.uuid===e.object.uuid));t&&a.push({element:t,intersection:e})}const t=r.findIndex((t=>t.object.uuid===e.object.uuid));-1!==t&&r.splice(t,1)}));const s=r.map((e=>({element:g.find((t=>t.instance?.uuid===e.object.uuid)),intersection:e})));o.forEach((({element:e,intersection:t})=>{N({element:e,eventKeys:["onPointerEnter"],intersection:t})})),a.forEach((({element:e,intersection:t})=>{N({element:e,eventKeys:["onPointerOver","onPointerMove"],intersection:t})})),s.forEach((({element:e,intersection:t})=>{N({element:e,eventKeys:["onPointerLeave","onPointerOut"],intersection:t})})),M=[].concat(o,a)},N=({element:e,eventKeys:t,intersection:n})=>{e&&t.forEach((t=>{e.eventListeners[t]&&e.eventListeners[t].forEach((e=>{e({intersection:n})}))}))};function G(t={}){return e.lunchboxTree||(e.lunchboxTree=new Z.RendererRootNode(t)),e.lunchboxTree}function T(e){Array.isArray(e)||(e=[e]);for(let t of e)if(j[t])return j[t];for(let n of e){const e=O[n]||s.find((e=>e.type?.toLowerCase()===n.toLowerCase()));if(!(t=e,"RendererNode"!==t?.minidomType||!1!==e.props["is-default"]&&!1!=!e.props.isDefault))return null;if(e){const t=e;return O[n]=t,t}}var t;return null}e.lunchboxTree=void 0;const O=t.reactive({}),j=t.reactive({});function k(e,n,r={},o=null){Array.isArray(e)||(e=[e]);for(let t of e)O[t]||(O[t]=null),j[t]||(j[t]=null);return t.computed({get(){const t=T(e);if(t)return t;const a=G(),s=q({type:e[0],uuid:n,props:r});return a.addChild(s),O[e[0]]=s,o&&o(s),s},set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);j[n]=e}})}const S="FALLBACK_CAMERA",F=k(["PerspectiveCamera","OrthographicCamera"],S,{args:[45,.5625,1,1e3]}),D=t.ref(!1),$=t.computed({get:()=>D.value?F.value:null,set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);j[n]=e}}),U="FALLBACK_RENDERER",W=k(["WebGLRenderer"],U,{}),z=t.ref(!1),I=t.computed({get:()=>z.value?W.value:null,set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);j[n]=e}}),K=k("Scene","FALLBACK_SCENE"),_=k("Raycaster","AUTO_RAYCASTER",{},(e=>(e=>{if(!e.instance)return;let n=null;n=t.watch((()=>I.value),(e=>{e?.instance&&(P||(A=t=>{const n=(e.instance.domElement.width??1)/ge.dpr.value,r=(e.instance.domElement.height??1)/ge.dpr.value;E.value.x=t.offsetX/n*2-1,E.value.y=-t.offsetY/r*2+1},R=()=>b.value=!0,C=()=>b.value=!1,e.instance.domElement.addEventListener("mousemove",A),e.instance.domElement.addEventListener("mousedown",R),e.instance.domElement.addEventListener("mouseup",C),t.watch((()=>[b.value,E.value.x,E.value.y]),(()=>{L&&cancelAnimationFrame(L),L=requestAnimationFrame((()=>{B()}))})),P=!0),n&&n())}),{immediate:!0})})(e)));function q(e={},t={}){const n={attached:e.attached??[],attachedArray:e.attachedArray??{},instance:e.instance??null},r=new Z.RendererStandardNode({...e,...n,metaType:"standardMeta"});return!r.type||v(r)||r.instance||(r.instance=function(e){if(!e.type)return null;const t=e.type[0].toUpperCase()+e.type.slice(1),n=l[e.type]||a[t];if(!n)throw`${t} is not part of the THREE namespace! Did you forget to extend? import {extend} from 'lunchbox'; extend({app, YourComponent, ...})`;const r=(e.props.args??[]).map((t=>function({node:e,prop:t}){const n="string"==typeof t&&t.startsWith("$attachedArray"),r=function({node:e,prop:t}){if("string"==typeof t&&t.startsWith("$attachedArray"))return e.attachedArray[t.replace("$attachedArray.","")];if("string"==typeof t&&t.startsWith("$attached"))return e.attached[t.replace("$attached.","")];return t}({node:e,prop:t});return Array.isArray(r)&&n?r:[r]}({node:e,prop:t})));let o=[];r.forEach((e=>{o=o.concat(e)}));return new n(...o)}({...r,props:{...r.props,...t}})),"scene"===r.type?.toLowerCase()?K.value=r:r.type?.toLowerCase().endsWith("camera")&&($.value=r),r}const V=e=>t.defineComponent({inheritAttrs:!1,name:e,render(){return t.h(e,this.$attrs,this.$slots?.default?.()||[])}});var H,Y=new Uint8Array(16);function X(){if(!H&&!(H="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return H(Y)}var J=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;function Q(e){return"string"==typeof e&&J.test(e)}for(var Z,ee=[],te=0;te<256;++te)ee.push((te+256).toString(16).substr(1));function ne(e,t,n){var r=(e=e||{}).random||(e.rng||X)();if(r[6]=15&r[6]|64,r[8]=63&r[8]|128,t){n=n||0;for(var o=0;o<16;++o)t[n+o]=r[o];return t}return function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=(ee[e[t+0]]+ee[e[t+1]]+ee[e[t+2]]+ee[e[t+3]]+"-"+ee[e[t+4]]+ee[e[t+5]]+"-"+ee[e[t+6]]+ee[e[t+7]]+"-"+ee[e[t+8]]+ee[e[t+9]]+"-"+ee[e[t+10]]+ee[e[t+11]]+ee[e[t+12]]+ee[e[t+13]]+ee[e[t+14]]+ee[e[t+15]]).toLowerCase();if(!Q(n))throw TypeError("Stringified UUID is invalid");return n}(r)}!function(e){e.BaseNode=class{constructor(e={},t){this.parentNode=e?.parentNode??t??null,this.minidomType="MinidomBaseNode",this.uuid=e?.uuid??ne(),s.push(this)}uuid;parentNode;get nextSibling(){if(!this.parentNode)return null;const e=this.parentNode.children.findIndex((e=>e.uuid===this.uuid));return-1!==e&&e<this.parentNode.children.length-1?this.parentNode.children[e+1]:null}insertBefore(e,t){e.removeAsChildFromAnyParents(),e.parentNode=this;const n=this.children.findIndex((e=>e.uuid===t?.uuid));-1!==n?this.children.splice(n,0,e):this.children.push(e)}removeChild(e){const t=this.children.findIndex((t=>t?.uuid===e?.uuid));-1!==t&&this.children.splice(t,1)}children=[];addChild(e){return e&&(e.removeAsChildFromAnyParents(),e.parentNode=this,this.insertBefore(e,null)),this}getPath(){const e=[];let t=this;for(;t;)e.unshift(t),t=t.parentNode;return e}drop(){this.parentNode=null,this.removeAsChildFromAnyParents()}walk(e){const t=[this,...this.children],n=[];let r=!0;for(;t.length&&r;){const o=t.shift();if(o){if(n.includes(o))continue;n.push(o),t.push(...o.children.filter((e=>!n.includes(e)))),r=e(o)}else r=!1}}minidomType;removeAsChildFromAnyParents(){s.forEach((e=>e.removeChild(this)))}};class t extends e.BaseNode{constructor(e={},t){super(e,t),this.minidomType="RendererNode",this.eventListeners={},this.eventListenerRemoveFunctions={},this.name=e.name??"",this.metaType=e.metaType??"standardMeta",this.props=e.props??[],this.type=e.type??""}eventListeners;eventListenerRemoveFunctions;name;metaType;props;type;drop(){super.drop(),Object.keys(this.eventListenerRemoveFunctions).forEach((e=>{this.eventListenerRemoveFunctions[e].forEach((e=>e()))}))}}e.RendererBaseNode=t;class n extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.domElement=e.domElement??document.createElement("div")}domElement;isLunchboxRootNode=!0}e.RendererRootNode=n;class r extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.text=e.text??""}text}e.RendererCommentNode=r;class o extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.domElement=e.domElement??document.createElement("div")}domElement}e.RendererDomNode=o;class a extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.text=e.text??""}text}e.RendererTextNode=a;class i extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.attached=e.attached??[],this.attachedArray=e.attachedArray??{},this.instance=e.instance??null}attached;attachedArray;instance}e.RendererStandardNode=i}(Z||(Z={}));(new Z.RendererRootNode).minidomType="RootNode";const re=[];let oe,ae;const se=[],ie=[],ce=e=>{ue(),oe=requestAnimationFrame((()=>de({app:e.app,renderer:I.value?.instance,scene:K.value.instance,camera:$.value?.instance,updateSource:e.updateSource})))},de=e=>{e.updateSource?ae||(ae=t.watch(e.updateSource,(()=>{ce(e)}),{deep:!0})):ce(e);const{app:n,renderer:r,scene:o,camera:a}=e;se.forEach((t=>{t&&t(e)})),r&&o&&a&&(n.customRender?n.customRender(e):r.render(t.toRaw(o),t.toRaw(a))),ie.forEach((t=>{t&&t(e)}))},ue=()=>{oe&&cancelAnimationFrame(oe)},le=()=>{ae&&ae()};const pe={x:"position.x",y:"position.y",z:"position.z"},me=["","parameters"],he=["args","attach","attachArray","is.default","isDefault","key","onAdded","ref","src"],fe=["geometry","material"];function ye(e,t,n,r){const o=r??e.instance,a=t.instance;e.props.attach===n&&(t.attached={[n]:o,...t.attached||{}},a[n]=r??e.instance),e.props.attachArray===n&&(t.attachedArray[e.props.attachArray]||(t.attachedArray[e.props.attachArray]=[]),t.attachedArray[e.props.attachArray].push(o),a[n]=[a[n]])}const ve={createElement:(e,t,n,r)=>{const o={type:e};r&&(o.props=r);if(f(e)){const e=function(e={}){const t={domElement:document.createElement(e.type??"")};return new Z.RendererDomNode({...t,...e,metaType:"domMeta"})}(o);return e}const a=q(o);return fe.forEach((t=>{e.toLowerCase().endsWith(t)&&(a.props.attach=t)})),a},createText:e=>function(e={}){const t={text:e.text??""};return new Z.RendererTextNode({...e,...t,metaType:"textMeta"})}({text:e}),createComment:e=>function(e={}){const t={text:e.text??""};return new Z.RendererCommentNode({...t,...e,metaType:"commentMeta"})}({text:e}),insert:(e,t,n)=>{let r=t??G();if(r.insertBefore(e,n),"commentMeta"!==e.metaType&&"textMeta"!==e.metaType&&(f(e)&&(f(t)||v(t))&&t.domElement.appendChild(e.domElement),y(e))){let n=r.metaType;if("textMeta"===n||"commentMeta"===n){const e=r.getPath();for(let t=e.length-1;t>=0;t--)if("textMeta"!==e[t].metaType&&"commentMeta"!==e[t].metaType){r=e[t];break}}if("standardMeta"===e.metaType&&"scene"!==e.type&&v(r)){const t=K.value;t.instance&&e&&t.addChild(e),e.instance&&e.instance.isObject3D&&t.instance&&t!==e&&t.instance.add(e.instance)}else y(e)&&e.instance?.isObject3D&&y(r)&&r.instance?.isObject3D&&r.instance?.add?.(e.instance);if(e?.props?.attach&&y(t)&&t?.instance){e.type?.toLowerCase().endsWith("loader")&&e.props.src&&(e.props.attach||e.props.attachArray)?function(e,t){const n=e.instance;if(t.attached=t.attached||{},t.attachedArray=t.attachedArray||{},!e.props.attach)return;if("textureloader"===e.type?.toLowerCase()){const r=n.load(e.props.src);ye(e,t,e.props.attach,r)}else n.load(e.props.src,(n=>{ye(e,t,e.props.attach,n)}),null,(e=>{throw new Error(e)}))}(e,t):ye(e,t,e.props.attach)}e.props?.onAdded&&e.props.onAdded({instance:e.instance})}},nextSibling(e){const t=e.nextSibling;return t||null},parentNode(e){const t=e.parentNode;return t||null},patchProp(e,t,n,o){f(e)?"style"===t?Object.keys(o).forEach((t=>{e.domElement.style[t]=o[t]})):e.domElement.setAttribute(t,o):v(e)||t.startsWith("$")||function({node:e,key:t,value:n}){if((e=>["onClick","onContextMenu","onDoubleClick","onPointerUp","onPointerDown","onPointerOver","onPointerOut","onPointerEnter","onPointerLeave","onPointerMove","onWheel"].includes(e))(t))return x({node:e,key:t,value:n});const o=t.replace(/-/g,"."),a=pe[o]||o;if(he.includes(t)||he.includes(a))return e;if(!y(e))return e;if("string"==typeof n&&n.startsWith("$attached")){const t=n.replace("$attached.","");n=r.get(e.attached,t,null)}const s=e.instance;if(!s)return e;let i;for(let e=0;e<me.length&&!i;e++){const t=[me[e],a].filter(Boolean).join(".");i=i=r.get(s,t)}if(i&&r.isNumber(n)&&i.setScalar)i.setScalar(n);else if(i&&i.set){const e=Array.isArray(n)?n:[n];s[a].set(...e)}else if("function"==typeof i)if("onbeforerender"===a.toLowerCase()||"onafterrender"===a.toLowerCase())s[a]=n;else{if(!Array.isArray(n))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" />');i.bind(e.instance)(...n)}else void 0!==r.get(s,a,void 0)?r.set(s,a,""===n||n):console.log(`No property ${a} found on ${s}`);const c=s?.texture?.type||s?.type;if("string"==typeof c){const e=c.toLowerCase();switch(!0){case e.includes("material"):s.needsUpdate=!0;break;case e.includes("camera")&&s.updateProjectionMatrix:s.updateProjectionMatrix()}}}({node:e,key:t,value:o})},remove:e=>{if(!e)return;const t=Object.keys(j),n=[];e.walk((e=>(n.push(e),!0))),n.forEach((e=>{const n=t.find((t=>j[t]?.uuid===e.uuid));if(n&&(j[n]=null),y(e)){e.instance?.removeFromParent?.();const t="scene"!==e.type&&e.instance?.dispose;t&&t.bind(e.instance)(),e.instance=null}e.drop();const r=s.findIndex((t=>t.uuid===e.uuid));-1!==r&&s.splice(r,1)}))},setElementText(){},setText(){}},ge={dpr:t.ref(1),inputActive:b,mousePos:E},be=t.computed((()=>$.value?.instance??null));const xe=t.computed((()=>I.value?.instance??null));const we=t.computed((()=>K.value.instance));let Ae=null,Re=null;e.camera=be,e.clearCustomRender=()=>{Ae?Ae.clearCustomRender():Re=null},e.createApp=e=>{Ae=t.createRenderer(ve).createApp(e),Object.keys(m).forEach((e=>{Ae?.component(e,m[e])}));const{mount:n}=Ae;return Ae.mount=(e,...t)=>{const r=G({domElement:"string"==typeof e?document.querySelector(e):e,isLunchboxRootNode:!0,name:"root",metaType:"rootMeta",type:"root",uuid:"LUNCHBOX_ROOT"});Ae.rootNode=r;return n(r,...t)},Ae.extend=e=>((({app:e,...t})=>{Object.keys(t).forEach((n=>{e.component(n,V(n)),l[n]=t[n]}))})({app:Ae,...e}),Ae),Ae.setCustomRender=e=>{Ae.customRender=e},Re&&(Ae.setCustomRender(Re),Re=null),Ae.clearCustomRender=()=>{Ae.customRender=null},Ae},e.find=function(e){return e=t.isRef(e)?e.value:e,y(e)?e?.instance:h(e)?e?.$el?.instance:t.isVNode(e)?e.el?.instance:null},e.globals=ge,e.offAfterRender=e=>{if(isFinite(e))ie.splice(e,1);else{const t=ie.findIndex((t=>t==e));ie.splice(t,1)}},e.offBeforeRender=e=>{if(isFinite(e))se.splice(e,1);else{const t=se.findIndex((t=>t==e));se.splice(t,1)}},e.onAfterRender=(e,t=1/0)=>{t===1/0?ie.push(e):ie.splice(t,0,e)},e.onBeforeRender=(e,t=1/0)=>{t===1/0?se.push(e):se.splice(t,0,e)},e.onStart=(e,t=1/0)=>{t===1/0?re.push(e):re.splice(t,0,e)},e.renderer=xe,e.scene=we,e.setCustomRender=e=>{Ae?Ae.setCustomRender(e):Re=e},e.useCamera=function(e){return t.watch(be,(t=>{t&&e(t)}),{immediate:!0})},e.useRenderer=function(e){return t.watch(xe,(t=>{t&&e(t)}),{immediate:!0})},e.useScene=function(e){return t.watch(we,(t=>{t&&e(t)}),{immediate:!0})},Object.defineProperty(e,"__esModule",{value:!0})}));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { toRaw, ref, onMounted,
|
|
1
|
+
import { toRaw, ref, onMounted, onBeforeUnmount, getCurrentInstance, h, defineComponent, isRef, isVNode, watch, reactive, computed, createRenderer } from 'vue';
|
|
2
2
|
import * as THREE from 'three';
|
|
3
3
|
import { set, get, isNumber } from 'lodash';
|
|
4
4
|
|
|
@@ -42,16 +42,30 @@ const resizeCanvas = (width, height) => {
|
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
const
|
|
45
|
+
const getInnerDimensions = (node) => {
|
|
46
|
+
const computedStyle = getComputedStyle(node);
|
|
47
|
+
const width = node.clientWidth - parseFloat(computedStyle.paddingLeft) - parseFloat(computedStyle.paddingRight);
|
|
48
|
+
const height = node.clientHeight - parseFloat(computedStyle.paddingTop) - parseFloat(computedStyle.paddingBottom);
|
|
49
|
+
return { width, height };
|
|
50
|
+
};
|
|
51
|
+
const prepCanvas = (container, canvasElement, onBeforeUnmount, sizePolicy) => {
|
|
46
52
|
const containerElement = container.value?.domElement;
|
|
47
53
|
if (!containerElement)
|
|
48
54
|
throw new Error('missing container');
|
|
49
55
|
// save...
|
|
50
56
|
// ...and size element
|
|
51
|
-
|
|
57
|
+
const resizeCanvasByPolicy = () => {
|
|
58
|
+
if (sizePolicy === "container") {
|
|
59
|
+
const dims = getInnerDimensions(containerElement);
|
|
60
|
+
resizeCanvas(dims.width, dims.height);
|
|
61
|
+
}
|
|
62
|
+
else
|
|
63
|
+
resizeCanvas();
|
|
64
|
+
};
|
|
65
|
+
resizeCanvasByPolicy();
|
|
52
66
|
// attach listeners
|
|
53
67
|
const observer = new ResizeObserver(([canvas]) => {
|
|
54
|
-
|
|
68
|
+
resizeCanvasByPolicy();
|
|
55
69
|
});
|
|
56
70
|
// window.addEventListener('resize', resizeCanvas)
|
|
57
71
|
if (containerElement) {
|
|
@@ -77,6 +91,7 @@ const fillStyle = (position) => {
|
|
|
77
91
|
left: 0,
|
|
78
92
|
width: '100%',
|
|
79
93
|
height: '100%',
|
|
94
|
+
display: 'block',
|
|
80
95
|
};
|
|
81
96
|
};
|
|
82
97
|
const LunchboxWrapper = {
|
|
@@ -94,9 +109,11 @@ const LunchboxWrapper = {
|
|
|
94
109
|
r3f: Boolean,
|
|
95
110
|
rendererArguments: Object,
|
|
96
111
|
rendererProperties: Object,
|
|
112
|
+
sizePolicy: String,
|
|
97
113
|
shadow: [Boolean, Object],
|
|
98
114
|
transparent: Boolean,
|
|
99
115
|
zoom: Number,
|
|
116
|
+
updateSource: Object,
|
|
100
117
|
},
|
|
101
118
|
setup(props, context) {
|
|
102
119
|
const canvas = ref();
|
|
@@ -251,7 +268,7 @@ const LunchboxWrapper = {
|
|
|
251
268
|
renderer.instance.setPixelRatio(dpr.value);
|
|
252
269
|
globals.dpr.value = dpr.value;
|
|
253
270
|
// prep canvas (sizing, observe, unmount, etc)
|
|
254
|
-
prepCanvas(container, renderer.instance.domElement, onBeforeUnmount);
|
|
271
|
+
prepCanvas(container, renderer.instance.domElement, onBeforeUnmount, props.sizePolicy);
|
|
255
272
|
}
|
|
256
273
|
else {
|
|
257
274
|
throw new Error('missing renderer');
|
|
@@ -276,24 +293,28 @@ const LunchboxWrapper = {
|
|
|
276
293
|
camera: camera.instance,
|
|
277
294
|
renderer: renderer.instance,
|
|
278
295
|
scene: scene.instance,
|
|
296
|
+
updateSource: props.updateSource,
|
|
279
297
|
});
|
|
280
298
|
});
|
|
281
299
|
// UNMOUNT
|
|
282
300
|
// ====================
|
|
283
301
|
onBeforeUnmount(() => {
|
|
284
302
|
cancelUpdate();
|
|
303
|
+
cancelUpdateSource();
|
|
285
304
|
});
|
|
286
305
|
// RENDER FUNCTION
|
|
287
306
|
// ====================
|
|
307
|
+
const containerFillStyle = props.sizePolicy === 'container' ? 'static' : 'absolute';
|
|
308
|
+
const canvasFillStyle = props.sizePolicy === 'container' ? 'static' : 'fixed';
|
|
288
309
|
return () => [
|
|
289
310
|
context.slots.default?.() ?? null,
|
|
290
311
|
h('div', {
|
|
291
|
-
style: fillStyle(
|
|
312
|
+
style: fillStyle(containerFillStyle),
|
|
292
313
|
ref: container,
|
|
293
314
|
}, [
|
|
294
315
|
useFallbackRenderer.value
|
|
295
316
|
? h('canvas', {
|
|
296
|
-
style: fillStyle(
|
|
317
|
+
style: fillStyle(canvasFillStyle),
|
|
297
318
|
class: 'lunchbox-canvas',
|
|
298
319
|
ref: canvas,
|
|
299
320
|
})
|
|
@@ -627,6 +648,7 @@ let mouseDownListener;
|
|
|
627
648
|
let mouseUpListener;
|
|
628
649
|
const mousePos = ref({ x: Infinity, y: Infinity });
|
|
629
650
|
let autoRaycasterEventsInitialized = false;
|
|
651
|
+
let frameID$1;
|
|
630
652
|
const setupAutoRaycaster = (node) => {
|
|
631
653
|
const instance = node.instance;
|
|
632
654
|
if (!instance)
|
|
@@ -659,8 +681,14 @@ const setupAutoRaycaster = (node) => {
|
|
|
659
681
|
renderer.instance.domElement.addEventListener('mousedown', mouseDownListener);
|
|
660
682
|
renderer.instance.domElement.addEventListener('mouseup', mouseUpListener);
|
|
661
683
|
// TODO: add touch events
|
|
662
|
-
//
|
|
663
|
-
|
|
684
|
+
// process mouse events asynchronously, whenever the mouse state changes
|
|
685
|
+
watch(() => [inputActive.value, mousePos.value.x, mousePos.value.y], () => {
|
|
686
|
+
if (frameID$1)
|
|
687
|
+
cancelAnimationFrame(frameID$1);
|
|
688
|
+
frameID$1 = requestAnimationFrame(() => {
|
|
689
|
+
autoRaycasterBeforeRender();
|
|
690
|
+
});
|
|
691
|
+
});
|
|
664
692
|
// mark complete
|
|
665
693
|
autoRaycasterEventsInitialized = true;
|
|
666
694
|
// cancel setup watcher
|
|
@@ -989,7 +1017,7 @@ const extend = ({ app, ...targets }) => {
|
|
|
989
1017
|
};
|
|
990
1018
|
|
|
991
1019
|
/** Process props into either themselves or the $attached value */
|
|
992
|
-
function processProp({ node, prop }) {
|
|
1020
|
+
function processProp({ node, prop, }) {
|
|
993
1021
|
// return $attachedArray value if needed
|
|
994
1022
|
if (typeof prop === 'string' && prop.startsWith('$attachedArray')) {
|
|
995
1023
|
return node.attachedArray[prop.replace('$attachedArray.', '')];
|
|
@@ -1001,10 +1029,12 @@ function processProp({ node, prop }) {
|
|
|
1001
1029
|
// otherwise, return plain value
|
|
1002
1030
|
return prop;
|
|
1003
1031
|
}
|
|
1004
|
-
function processPropAsArray({ node, prop }) {
|
|
1032
|
+
function processPropAsArray({ node, prop, }) {
|
|
1005
1033
|
const isAttachedArray = typeof prop === 'string' && prop.startsWith('$attachedArray');
|
|
1006
1034
|
const output = processProp({ node, prop });
|
|
1007
|
-
return Array.isArray(output) && isAttachedArray
|
|
1035
|
+
return Array.isArray(output) && isAttachedArray
|
|
1036
|
+
? output
|
|
1037
|
+
: [output];
|
|
1008
1038
|
}
|
|
1009
1039
|
|
|
1010
1040
|
function instantiateThreeObject(node) {
|
|
@@ -1303,16 +1333,34 @@ const onStart = (cb, index = Infinity) => {
|
|
|
1303
1333
|
};
|
|
1304
1334
|
|
|
1305
1335
|
let frameID;
|
|
1336
|
+
let watchStopHandle;
|
|
1306
1337
|
const beforeRender = [];
|
|
1307
1338
|
const afterRender = [];
|
|
1308
|
-
const
|
|
1309
|
-
|
|
1339
|
+
const requestUpdate = (opts) => {
|
|
1340
|
+
cancelUpdate();
|
|
1310
1341
|
frameID = requestAnimationFrame(() => update({
|
|
1311
1342
|
app: opts.app,
|
|
1312
1343
|
renderer: ensureRenderer.value?.instance,
|
|
1313
1344
|
scene: ensuredScene.value.instance,
|
|
1314
1345
|
camera: ensuredCamera.value?.instance,
|
|
1346
|
+
updateSource: opts.updateSource,
|
|
1315
1347
|
}));
|
|
1348
|
+
};
|
|
1349
|
+
const update = (opts) => {
|
|
1350
|
+
if (opts.updateSource) {
|
|
1351
|
+
if (!watchStopHandle) {
|
|
1352
|
+
// request next frame only when state changes
|
|
1353
|
+
watchStopHandle = watch(opts.updateSource, () => {
|
|
1354
|
+
requestUpdate(opts);
|
|
1355
|
+
}, {
|
|
1356
|
+
deep: true,
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
else {
|
|
1361
|
+
// request next frame on a continuous loop
|
|
1362
|
+
requestUpdate(opts);
|
|
1363
|
+
}
|
|
1316
1364
|
// prep options
|
|
1317
1365
|
const { app, renderer, scene, camera } = opts;
|
|
1318
1366
|
// BEFORE RENDER
|
|
@@ -1375,6 +1423,10 @@ const cancelUpdate = () => {
|
|
|
1375
1423
|
if (frameID)
|
|
1376
1424
|
cancelAnimationFrame(frameID);
|
|
1377
1425
|
};
|
|
1426
|
+
const cancelUpdateSource = () => {
|
|
1427
|
+
if (watchStopHandle)
|
|
1428
|
+
watchStopHandle();
|
|
1429
|
+
};
|
|
1378
1430
|
|
|
1379
1431
|
/** Update a single prop on a given node. */
|
|
1380
1432
|
function updateObjectProp({ node, key, value, }) {
|
|
@@ -1412,6 +1464,7 @@ function updateObjectProp({ node, key, value, }) {
|
|
|
1412
1464
|
liveProperty = liveProperty = get(target, fullPath);
|
|
1413
1465
|
}
|
|
1414
1466
|
// change property
|
|
1467
|
+
// first, save as array in case we need to spread it
|
|
1415
1468
|
if (liveProperty && isNumber(value) && liveProperty.setScalar) {
|
|
1416
1469
|
// if value is a number and the property has a `setScalar` method, use that
|
|
1417
1470
|
liveProperty.setScalar(value);
|
|
@@ -1422,8 +1475,18 @@ function updateObjectProp({ node, key, value, }) {
|
|
|
1422
1475
|
target[finalKey].set(...nextValueAsArray);
|
|
1423
1476
|
}
|
|
1424
1477
|
else if (typeof liveProperty === 'function') {
|
|
1425
|
-
//
|
|
1426
|
-
|
|
1478
|
+
// some function properties are set rather than called, so let's handle them
|
|
1479
|
+
if (finalKey.toLowerCase() === 'onbeforerender' ||
|
|
1480
|
+
finalKey.toLowerCase() === 'onafterrender') {
|
|
1481
|
+
target[finalKey] = value;
|
|
1482
|
+
}
|
|
1483
|
+
else {
|
|
1484
|
+
if (!Array.isArray(value)) {
|
|
1485
|
+
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" />');
|
|
1486
|
+
}
|
|
1487
|
+
// if property is a function, let's try calling it
|
|
1488
|
+
liveProperty.bind(node.instance)(...value);
|
|
1489
|
+
}
|
|
1427
1490
|
// pass the result to the parent
|
|
1428
1491
|
// const parent = node.parentNode
|
|
1429
1492
|
// if (parent) {
|
package/package.json
CHANGED
|
@@ -5,11 +5,13 @@ import {
|
|
|
5
5
|
onBeforeUnmount,
|
|
6
6
|
onMounted,
|
|
7
7
|
ref,
|
|
8
|
+
WatchSource,
|
|
8
9
|
WritableComputedRef,
|
|
9
10
|
} from 'vue'
|
|
10
11
|
import {
|
|
11
12
|
cameraReady,
|
|
12
13
|
cancelUpdate,
|
|
14
|
+
cancelUpdateSource,
|
|
13
15
|
createNode,
|
|
14
16
|
ensuredCamera,
|
|
15
17
|
ensureRenderer,
|
|
@@ -41,6 +43,7 @@ const fillStyle = (position: string) => {
|
|
|
41
43
|
left: 0,
|
|
42
44
|
width: '100%',
|
|
43
45
|
height: '100%',
|
|
46
|
+
display: 'block',
|
|
44
47
|
}
|
|
45
48
|
}
|
|
46
49
|
|
|
@@ -59,9 +62,11 @@ export const LunchboxWrapper: ComponentOptions = {
|
|
|
59
62
|
r3f: Boolean,
|
|
60
63
|
rendererArguments: Object,
|
|
61
64
|
rendererProperties: Object,
|
|
65
|
+
sizePolicy: String,
|
|
62
66
|
shadow: [Boolean, Object],
|
|
63
67
|
transparent: Boolean,
|
|
64
68
|
zoom: Number,
|
|
69
|
+
updateSource: Object,
|
|
65
70
|
},
|
|
66
71
|
setup(props: Lunch.WrapperProps, context) {
|
|
67
72
|
const canvas = ref<MiniDom.RendererDomNode>()
|
|
@@ -239,7 +244,8 @@ export const LunchboxWrapper: ComponentOptions = {
|
|
|
239
244
|
prepCanvas(
|
|
240
245
|
container,
|
|
241
246
|
renderer.instance.domElement,
|
|
242
|
-
onBeforeUnmount
|
|
247
|
+
onBeforeUnmount,
|
|
248
|
+
props.sizePolicy,
|
|
243
249
|
)
|
|
244
250
|
} else {
|
|
245
251
|
throw new Error('missing renderer')
|
|
@@ -267,6 +273,7 @@ export const LunchboxWrapper: ComponentOptions = {
|
|
|
267
273
|
camera: camera.instance,
|
|
268
274
|
renderer: renderer.instance,
|
|
269
275
|
scene: scene.instance,
|
|
276
|
+
updateSource: props.updateSource,
|
|
270
277
|
})
|
|
271
278
|
})
|
|
272
279
|
|
|
@@ -274,22 +281,26 @@ export const LunchboxWrapper: ComponentOptions = {
|
|
|
274
281
|
// ====================
|
|
275
282
|
onBeforeUnmount(() => {
|
|
276
283
|
cancelUpdate()
|
|
284
|
+
cancelUpdateSource()
|
|
277
285
|
})
|
|
278
286
|
|
|
279
287
|
// RENDER FUNCTION
|
|
280
288
|
// ====================
|
|
289
|
+
const containerFillStyle = props.sizePolicy === 'container' ? 'static' : 'absolute'
|
|
290
|
+
const canvasFillStyle = props.sizePolicy === 'container' ? 'static' : 'fixed'
|
|
291
|
+
|
|
281
292
|
return () => [
|
|
282
293
|
context.slots.default?.() ?? null,
|
|
283
294
|
h(
|
|
284
295
|
'div',
|
|
285
296
|
{
|
|
286
|
-
style: fillStyle(
|
|
297
|
+
style: fillStyle(containerFillStyle),
|
|
287
298
|
ref: container,
|
|
288
299
|
},
|
|
289
300
|
[
|
|
290
301
|
useFallbackRenderer.value
|
|
291
302
|
? h('canvas', {
|
|
292
|
-
style: fillStyle(
|
|
303
|
+
style: fillStyle(canvasFillStyle),
|
|
293
304
|
class: 'lunchbox-canvas',
|
|
294
305
|
ref: canvas,
|
|
295
306
|
})
|
|
@@ -1,22 +1,39 @@
|
|
|
1
1
|
import { MiniDom } from '../../core'
|
|
2
|
+
import { Lunch } from '../../types'
|
|
2
3
|
import { Ref } from 'vue'
|
|
3
4
|
import { resizeCanvas } from './resizeCanvas'
|
|
4
5
|
|
|
6
|
+
const getInnerDimensions = (node: Element) => {
|
|
7
|
+
const computedStyle = getComputedStyle(node)
|
|
8
|
+
const width = node.clientWidth - parseFloat(computedStyle.paddingLeft) - parseFloat(computedStyle.paddingRight)
|
|
9
|
+
const height = node.clientHeight - parseFloat(computedStyle.paddingTop) - parseFloat(computedStyle.paddingBottom)
|
|
10
|
+
return { width, height }
|
|
11
|
+
}
|
|
12
|
+
|
|
5
13
|
export const prepCanvas = (
|
|
6
14
|
container: Ref<MiniDom.RendererDomNode | undefined>,
|
|
7
15
|
canvasElement: HTMLCanvasElement,
|
|
8
16
|
onBeforeUnmount: Function,
|
|
17
|
+
sizePolicy?: Lunch.SizePolicy
|
|
9
18
|
) => {
|
|
10
19
|
const containerElement = container.value?.domElement
|
|
11
20
|
if (!containerElement) throw new Error('missing container')
|
|
12
21
|
|
|
13
22
|
// save...
|
|
14
23
|
// ...and size element
|
|
15
|
-
|
|
24
|
+
const resizeCanvasByPolicy = () => {
|
|
25
|
+
if (sizePolicy === "container") {
|
|
26
|
+
const dims = getInnerDimensions(containerElement);
|
|
27
|
+
resizeCanvas(dims.width, dims.height);
|
|
28
|
+
}
|
|
29
|
+
else
|
|
30
|
+
resizeCanvas()
|
|
31
|
+
};
|
|
32
|
+
resizeCanvasByPolicy();
|
|
16
33
|
|
|
17
34
|
// attach listeners
|
|
18
35
|
const observer = new ResizeObserver(([canvas]) => {
|
|
19
|
-
|
|
36
|
+
resizeCanvasByPolicy();
|
|
20
37
|
})
|
|
21
38
|
// window.addEventListener('resize', resizeCanvas)
|
|
22
39
|
if (containerElement) {
|
|
@@ -29,4 +46,4 @@ export const prepCanvas = (
|
|
|
29
46
|
observer.unobserve(canvasElement)
|
|
30
47
|
}
|
|
31
48
|
})
|
|
32
|
-
}
|
|
49
|
+
}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { Lunch } from '../..'
|
|
2
2
|
|
|
3
3
|
/** Process props into either themselves or the $attached value */
|
|
4
|
-
export function processProp<T, U = THREE.Object3D>({
|
|
4
|
+
export function processProp<T, U = THREE.Object3D>({
|
|
5
|
+
node,
|
|
6
|
+
prop,
|
|
7
|
+
}: {
|
|
8
|
+
node: Lunch.StandardMeta<U>
|
|
9
|
+
prop: any
|
|
10
|
+
}) {
|
|
5
11
|
// return $attachedArray value if needed
|
|
6
12
|
if (typeof prop === 'string' && prop.startsWith('$attachedArray')) {
|
|
7
|
-
return node.attachedArray[
|
|
13
|
+
return node.attachedArray[
|
|
14
|
+
prop.replace('$attachedArray.', '')
|
|
15
|
+
] as any as T
|
|
8
16
|
}
|
|
9
17
|
|
|
10
18
|
// return $attached value if needed
|
|
@@ -16,8 +24,17 @@ export function processProp<T, U = THREE.Object3D>({ node, prop }: { node: Lunch
|
|
|
16
24
|
return prop as T
|
|
17
25
|
}
|
|
18
26
|
|
|
19
|
-
export function processPropAsArray<T, U = THREE.Object3D>({
|
|
20
|
-
|
|
27
|
+
export function processPropAsArray<T, U = THREE.Object3D>({
|
|
28
|
+
node,
|
|
29
|
+
prop,
|
|
30
|
+
}: {
|
|
31
|
+
node: Lunch.StandardMeta<U>
|
|
32
|
+
prop: any
|
|
33
|
+
}) {
|
|
34
|
+
const isAttachedArray =
|
|
35
|
+
typeof prop === 'string' && prop.startsWith('$attachedArray')
|
|
21
36
|
const output = processProp<T, U>({ node, prop })
|
|
22
|
-
return Array.isArray(output) && isAttachedArray
|
|
23
|
-
|
|
37
|
+
return Array.isArray(output) && isAttachedArray
|
|
38
|
+
? (output as Array<T>)
|
|
39
|
+
: [output]
|
|
40
|
+
}
|
|
@@ -17,6 +17,8 @@ let mouseUpListener: (event: MouseEvent) => void
|
|
|
17
17
|
export const mousePos = ref({ x: Infinity, y: Infinity })
|
|
18
18
|
let autoRaycasterEventsInitialized = false
|
|
19
19
|
|
|
20
|
+
let frameID: number
|
|
21
|
+
|
|
20
22
|
export const setupAutoRaycaster = (node: Lunch.Node<THREE.Raycaster>) => {
|
|
21
23
|
const instance = node.instance
|
|
22
24
|
|
|
@@ -67,8 +69,16 @@ export const setupAutoRaycaster = (node: Lunch.Node<THREE.Raycaster>) => {
|
|
|
67
69
|
|
|
68
70
|
// TODO: add touch events
|
|
69
71
|
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
+
// process mouse events asynchronously, whenever the mouse state changes
|
|
73
|
+
watch(
|
|
74
|
+
() => [inputActive.value, mousePos.value.x, mousePos.value.y],
|
|
75
|
+
() => {
|
|
76
|
+
if (frameID) cancelAnimationFrame(frameID)
|
|
77
|
+
frameID = requestAnimationFrame(() => {
|
|
78
|
+
autoRaycasterBeforeRender()
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
)
|
|
72
82
|
|
|
73
83
|
// mark complete
|
|
74
84
|
autoRaycasterEventsInitialized = true
|
package/src/core/update.ts
CHANGED
|
@@ -1,22 +1,44 @@
|
|
|
1
1
|
import { ensureRenderer, ensuredScene, ensuredCamera } from '.'
|
|
2
2
|
import { Lunch } from '..'
|
|
3
|
-
import { toRaw } from 'vue'
|
|
3
|
+
import { toRaw, watch, WatchStopHandle } from 'vue'
|
|
4
4
|
|
|
5
5
|
let frameID: number
|
|
6
|
+
let watchStopHandle: WatchStopHandle
|
|
6
7
|
|
|
7
8
|
export const beforeRender = [] as Lunch.UpdateCallback[]
|
|
8
9
|
export const afterRender = [] as Lunch.UpdateCallback[]
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
const requestUpdate = (opts: Lunch.UpdateCallbackProperties) => {
|
|
12
|
+
cancelUpdate()
|
|
12
13
|
frameID = requestAnimationFrame(() =>
|
|
13
14
|
update({
|
|
14
15
|
app: opts.app,
|
|
15
16
|
renderer: ensureRenderer.value?.instance,
|
|
16
17
|
scene: ensuredScene.value.instance,
|
|
17
18
|
camera: ensuredCamera.value?.instance,
|
|
19
|
+
updateSource: opts.updateSource,
|
|
18
20
|
})
|
|
19
21
|
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const update: Lunch.UpdateCallback = (opts) => {
|
|
25
|
+
if (opts.updateSource) {
|
|
26
|
+
if (!watchStopHandle) {
|
|
27
|
+
// request next frame only when state changes
|
|
28
|
+
watchStopHandle = watch(
|
|
29
|
+
opts.updateSource,
|
|
30
|
+
() => {
|
|
31
|
+
requestUpdate(opts)
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
deep: true,
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
// request next frame on a continuous loop
|
|
40
|
+
requestUpdate(opts)
|
|
41
|
+
}
|
|
20
42
|
|
|
21
43
|
// prep options
|
|
22
44
|
const { app, renderer, scene, camera } = opts
|
|
@@ -82,3 +104,7 @@ export const offAfterRender = (cb: Lunch.UpdateCallback | number) => {
|
|
|
82
104
|
export const cancelUpdate = () => {
|
|
83
105
|
if (frameID) cancelAnimationFrame(frameID)
|
|
84
106
|
}
|
|
107
|
+
|
|
108
|
+
export const cancelUpdateSource = () => {
|
|
109
|
+
if (watchStopHandle) watchStopHandle()
|
|
110
|
+
}
|
|
@@ -67,6 +67,7 @@ export function updateObjectProp({
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// change property
|
|
70
|
+
// first, save as array in case we need to spread it
|
|
70
71
|
if (liveProperty && isNumber(value) && liveProperty.setScalar) {
|
|
71
72
|
// if value is a number and the property has a `setScalar` method, use that
|
|
72
73
|
liveProperty.setScalar(value)
|
|
@@ -75,8 +76,21 @@ export function updateObjectProp({
|
|
|
75
76
|
const nextValueAsArray = Array.isArray(value) ? value : [value]
|
|
76
77
|
;(target as any)[finalKey].set(...nextValueAsArray)
|
|
77
78
|
} else if (typeof liveProperty === 'function') {
|
|
78
|
-
//
|
|
79
|
-
|
|
79
|
+
// some function properties are set rather than called, so let's handle them
|
|
80
|
+
if (
|
|
81
|
+
finalKey.toLowerCase() === 'onbeforerender' ||
|
|
82
|
+
finalKey.toLowerCase() === 'onafterrender'
|
|
83
|
+
) {
|
|
84
|
+
;(target as any)[finalKey] = value
|
|
85
|
+
} else {
|
|
86
|
+
if (!Array.isArray(value)) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
'Arguments on a declarative method must be wrapped in an array.\nWorks:\n<example :methodCall="[256]" />\nDoesn\'t work:\n<example :methodCall="256" />'
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
// if property is a function, let's try calling it
|
|
92
|
+
liveProperty.bind(node.instance)(...value)
|
|
93
|
+
}
|
|
80
94
|
|
|
81
95
|
// pass the result to the parent
|
|
82
96
|
// const parent = node.parentNode
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
type RootNode = import('./core/minidom').MiniDom.RendererRootNode
|
|
2
2
|
type VNodeProps = import('vue').VNodeProps
|
|
3
3
|
type VueApp<T> = import('vue').App<T>
|
|
4
|
+
type WatchSource = import('vue').WatchSource
|
|
4
5
|
type RendererStandardNode<T = THREE.Object3D> =
|
|
5
6
|
import('./core').MiniDom.RendererStandardNode<T>
|
|
6
7
|
|
|
@@ -127,6 +128,7 @@ export declare namespace Lunch {
|
|
|
127
128
|
scene?: THREE.Scene | null
|
|
128
129
|
renderer?: THREE.Renderer | null
|
|
129
130
|
camera?: THREE.Camera | null
|
|
131
|
+
updateSource?: WatchSource | null
|
|
130
132
|
|
|
131
133
|
// sceneNode: Node<THREE.Scene> | null
|
|
132
134
|
// rendererNode: Node<THREE.Renderer> | null
|
|
@@ -136,6 +138,8 @@ export declare namespace Lunch {
|
|
|
136
138
|
/** Universally unique identifier. */
|
|
137
139
|
type Uuid = string
|
|
138
140
|
|
|
141
|
+
type SizePolicy = 'full' | 'container'
|
|
142
|
+
|
|
139
143
|
interface WrapperProps {
|
|
140
144
|
background?: string
|
|
141
145
|
cameraArgs?: any[]
|
|
@@ -149,8 +153,10 @@ export declare namespace Lunch {
|
|
|
149
153
|
// TODO: Why doesn't ConstructorParameters<THREE.WebGLRenderer> work here?
|
|
150
154
|
rendererArguments?: object
|
|
151
155
|
rendererProperties?: Partial<THREE.WebGLRenderer>
|
|
156
|
+
sizePolicy?: SizePolicy
|
|
152
157
|
shadow?: ShadowSugar
|
|
153
158
|
transparent?: boolean
|
|
154
159
|
zoom?: number
|
|
160
|
+
updateSource?: WatchSource | null
|
|
155
161
|
}
|
|
156
162
|
}
|