@vladkrutenyuk/three-kvy-core
Version:
Everything you need to create any-complexity 3D apps with Three.js. Empower Three.js with a modular, lifecycle-managed context that seamlessly propagates through objects via reusable features providing structured logic.
4 lines (3 loc) • 14 kB
JavaScript
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).KVY={})}(this,(function(t){"use strict";function e(t,e){const r=t.indexOf(e),s=-1!==r;return s&&t.splice(r,1),s}const r=Object.defineProperties,s=t=>({value:t,writable:!1,configurable:!1}),n=t=>({value:t,enumerable:!1,configurable:!0});var i=Object.freeze({__proto__:null,defineProps:r,notEnumer:n,readOnly:s});function o(t,e){const r=t.parent;null!==r&&e(r)&&o(r,e)}const h=(t,e)=>{if(!t)throw new ReferenceError(`Attempted to access '${e}' before it was initialized.`);return t};function c(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var a,u={exports:{}};var d=(a||(a=1,function(t){var e=Object.prototype.hasOwnProperty,r="~";function s(){}function n(t,e,r){this.fn=t,this.context=e,this.once=r||!1}function i(t,e,s,i,o){if("function"!=typeof s)throw new TypeError("The listener must be a function");var h=new n(s,i||t,o),c=r?r+e:e;return t._events[c]?t._events[c].fn?t._events[c]=[t._events[c],h]:t._events[c].push(h):(t._events[c]=h,t._eventsCount++),t}function o(t,e){0==--t._eventsCount?t._events=new s:delete t._events[e]}function h(){this._events=new s,this._eventsCount=0}Object.create&&(s.prototype=Object.create(null),(new s).__proto__||(r=!1)),h.prototype.eventNames=function(){var t,s,n=[];if(0===this._eventsCount)return n;for(s in t=this._events)e.call(t,s)&&n.push(r?s.slice(1):s);return Object.getOwnPropertySymbols?n.concat(Object.getOwnPropertySymbols(t)):n},h.prototype.listeners=function(t){var e=r?r+t:t,s=this._events[e];if(!s)return[];if(s.fn)return[s.fn];for(var n=0,i=s.length,o=new Array(i);n<i;n++)o[n]=s[n].fn;return o},h.prototype.listenerCount=function(t){var e=r?r+t:t,s=this._events[e];return s?s.fn?1:s.length:0},h.prototype.emit=function(t,e,s,n,i,o){var h=r?r+t:t;if(!this._events[h])return!1;var c,a,u=this._events[h],d=arguments.length;if(u.fn){switch(u.once&&this.removeListener(t,u.fn,void 0,!0),d){case 1:return u.fn.call(u.context),!0;case 2:return u.fn.call(u.context,e),!0;case 3:return u.fn.call(u.context,e,s),!0;case 4:return u.fn.call(u.context,e,s,n),!0;case 5:return u.fn.call(u.context,e,s,n,i),!0;case 6:return u.fn.call(u.context,e,s,n,i,o),!0}for(a=1,c=new Array(d-1);a<d;a++)c[a-1]=arguments[a];u.fn.apply(u.context,c)}else{var l,f=u.length;for(a=0;a<f;a++)switch(u[a].once&&this.removeListener(t,u[a].fn,void 0,!0),d){case 1:u[a].fn.call(u[a].context);break;case 2:u[a].fn.call(u[a].context,e);break;case 3:u[a].fn.call(u[a].context,e,s);break;case 4:u[a].fn.call(u[a].context,e,s,n);break;default:if(!c)for(l=1,c=new Array(d-1);l<d;l++)c[l-1]=arguments[l];u[a].fn.apply(u[a].context,c)}}return!0},h.prototype.on=function(t,e,r){return i(this,t,e,r,!1)},h.prototype.once=function(t,e,r){return i(this,t,e,r,!0)},h.prototype.removeListener=function(t,e,s,n){var i=r?r+t:t;if(!this._events[i])return this;if(!e)return o(this,i),this;var h=this._events[i];if(h.fn)h.fn!==e||n&&!h.once||s&&h.context!==s||o(this,i);else{for(var c=0,a=[],u=h.length;c<u;c++)(h[c].fn!==e||n&&!h[c].once||s&&h[c].context!==s)&&a.push(h[c]);a.length?this._events[i]=1===a.length?a[0]:a:o(this,i)}return this},h.prototype.removeAllListeners=function(t){var e;return t?(e=r?r+t:t,this._events[e]&&o(this,e)):(this._events=new s,this._eventsCount=0),this},h.prototype.off=h.prototype.removeListener,h.prototype.addListener=h.prototype.on,h.prefixed=r,h.EventEmitter=h,t.exports=h}(u)),u.exports),l=c(d);class f extends l{static create(t,e){return new f(new t.WebGLRenderer(null==e?void 0:e.renderer),new t.PerspectiveCamera,new t.Scene,new t.Clock,new t.Raycaster)}get camera(){return this._camera}set camera(t){const e=this._camera;this._camera=t,this.cameraChanged(t,e)}get container(){return this._container}get isMounted(){return this._isMounted}get isDestroyed(){return this._isDestroyed}constructor(t,e,n,i,o){super(),this._container=null,this._resizeObserver=null,this._isMounted=!1,this._isDestroyed=!1,this._srcRenderFn=()=>{this.renderer.render(this.scene,this._camera)},this._renderFn=this._srcRenderFn,this.resizeHandler=()=>{const t=this._container;if(!t)return;const e=t.offsetWidth,r=t.offsetHeight,s=this._camera;s.aspect=e/r,s.updateProjectionMatrix(),this.renderer.setSize(e,r),this.emit(_.Resize,e,r),this.render()},r(this,{isThreeContext:s(!0)}),this.renderer=t,this.scene=n,this._camera=e,this.clock=i,this.raycaster=o,this._renderFn=this._srcRenderFn}render(){this.emit(_.RenderBefore),this._renderFn(),this.emit(_.RenderAfter)}overrideRender(t){return this._renderFn=t,this}resetRender(){return this._renderFn=this._srcRenderFn,this}mount(t){if(this._isMounted||this._isDestroyed)return;this._isMounted=!0;const e=this.renderer.domElement;return this._container=t,t.append(e),e.tabIndex=0,e.style.touchAction="none",this.emit(_.Mount,t),this._resizeObserver=new ResizeObserver(this.resizeHandler),this._resizeObserver.observe(t),this.resizeHandler(),this}unmount(){var t;if(this._isMounted)return this._isMounted=!1,null===(t=this._resizeObserver)||void 0===t||t.disconnect(),this._resizeObserver=null,this.renderer.domElement.remove(),this.emit(_.Unmount),this}destroy(){if(this._isDestroyed)return;this._isDestroyed=!0;const t=()=>{console.error("render is called after ThreeContext is destroyed.")};this._renderFn=t,this._srcRenderFn=t,this.unmount(),this.renderer.dispose(),this.emit(_.Destroy),Object.values(_).forEach((t=>this.removeAllListeners(t)))}cameraChanged(t,e){const r=this._camera,s=this._container;s&&(r.aspect=s.offsetWidth/s.offsetHeight),r.updateProjectionMatrix(),this.emit(_.CameraChanged,t,e)}}const _=Object.freeze({RenderBefore:"renderbefore",RenderAfter:"renderafter",Mount:"mount",Unmount:"unmount",Destroy:"destroy",CameraChanged:"camerachanged",Resize:"resize"}),x=Object.freeze({AttCtx:"attachedctx",DetCtx:"detachedctx",FtAdd:"featureadded",FtRem:"featureremoved",Dstr:"destroy"}),p="__kvy_ftblty__";class v extends l{static extract(t){const e=t[p];return void 0!==e&&e.isObjectFeaturability?e:null}static from(t){let e=v.extract(t);return e||(e=new v(t),e)}static destroy(t,e){var r;null===(r=v.extract(t))||void 0===r||r.destroy(e)}get ctx(){return this._ctx}get features(){return[...this._features]}constructor(t){super(),this.isObjectFeaturability=!0,this._ctx=null,this._features=[],this.object=t,t.addEventListener("added",this.onObjectAdded),t.addEventListener("removed",this.onObjectRemoved),r(t,{[p]:n(this),isFeaturable:n(!0)}),t.parent&&this.onObjectAdded({target:t}),this.inheritCtx()}destroy(t){if(this.destroyAllFeatures(),this.object.isRoot&&!t)return;this.detachCtx();const e=this.object;e.removeEventListener("added",this.onObjectAdded),e.removeEventListener("removed",this.onObjectRemoved),delete e[p],delete e.isFeaturable}destroyAllFeatures(){const t=this.features;for(const e of t)e.destroy()}addFeature(t,e,r){const s=new t(this.object,null!=e?e:{});return null==r||r(s),this._features.push(s),s.init(),this.emit(x.FtAdd,s),s}getFeature(t){var e;return null!==(e=this._features.find((e=>e instanceof t)))&&void 0!==e?e:null}getFeatureBy(t){var e;return null!==(e=this._features.find(t))&&void 0!==e?e:null}destroyFeature(t){this._log("destroying feature...");return!!e(this._features,t)&&(t.destroy(),this.emit(x.FtRem,t),!0)}setCtx(t){return t?this.attachCtx(t):this.detachCtx(),this}onObjectAdded({target:t}){const e=v.extract(t);e?(e._log("object added"),e.inheritCtx()):console.error("Object3DFeaturability is not in target object.")}inheritCtx(){var t;const e=this.object,r=e.parent;if(!r)return;const s=null===(t=v.extract(r))||void 0===t?void 0:t._ctx;if(s)return this._log("onAdded parent has ctx"),void this.propagateAttachCtxDown(s);this._log("onAdded parent is just object3d");let n=null;if(o(e,(t=>(n=v.extract(t),!n))),null===n)return;const i=n.ctx;this._log("onAdded found featurable object ancestor"),i&&this.propagateAttachCtxDown(i)}onObjectRemoved({target:t}){const e=v.extract(t);e?(e._log("object removed"),e.propagateDetachCtxDown()):console.error("Object3DFeaturability is not in target object.")}attachCtx(t){if(this._log("attaching ctx..."),null!==this._ctx)return this._log("there is some ctx here"),this._ctx!==t?void console.error("Cannot attach this object. It had attached to another ctx."):void this._log("had attached already");this._ctx=t,this.emit(x.AttCtx,t),t.once(x.Dstr,this.detachCtx,this),this._log("attached ctx")}detachCtx(){if(this._log("detaching ctx..."),null===this._ctx)return;const t=this._ctx;this._ctx=null,this.emit(x.DetCtx,t),t.off(x.Dstr,this.detachCtx,this),this._log("detached ctx")}propagateAttachCtxDown(t){this._log("attaching ctx recursively..."),this.object.traverse((e=>{var r;null===(r=v.extract(e))||void 0===r||r.attachCtx(t)}))}propagateDetachCtxDown(){this._log("detaching ctx recursively..."),this.object.traverse((t=>{var e;null===(e=v.extract(t))||void 0===e||e.detachCtx()}))}_log(t){v.log(this,t)}}v.log=()=>{};class m extends l{get ctx(){return h(this._ctx,"ctx")}get hasCtx(){return!!this._ctx}constructor(t){super(),this._ctx=null,this.attachCtx=t=>{if(this._ctx){if(this._ctx===t)return;this.detachCtx()}this._ctx=t,this._log("ctx attached"),this.emit(x.AttCtx,t),this._log("useCtx"),this._useCtxReturn=this.useCtx(t),this.initCtxEventMethods(t)},this.detachCtx=()=>{if(!this._ctx)return;const t=this._ctx;this._ctx=null,this.emit(x.DetCtx,t),this._log("ctx detached"),this._useCtxReturn&&(this._log("useCtx cleanup"),this._useCtxReturn(),this._useCtxReturn=void 0)},this._ftblty=v.extract(t),r(this,{id:s(y++),isObject3DFeature:s(!0),object:s(t)}),this.uuid=m.generateUUID(),this._ftblty.on(x.AttCtx,this.ftbltyAttachedToCtxHandler,this),this._ftblty.on(x.DetCtx,this.ftbltyDetachedFromCtxHandler,this),this._log("init")}init(){this._ftblty.ctx&&this.attachCtx(this._ftblty.ctx)}destroy(){this.detachCtx(),this._ftblty.off(x.AttCtx,this.ftbltyAttachedToCtxHandler,this),this._ftblty.off(x.DetCtx,this.ftbltyDetachedFromCtxHandler,this);this._ftblty.destroyFeature(this)&&(this._log("destroyed"),this.emit(x.Dstr),this.onDestroy())}ftbltyAttachedToCtxHandler(t){this._log("obj attached to ctx"),this.attachCtx(t)}ftbltyDetachedFromCtxHandler(t){this._log("obj detached from ctx"),this.detachCtx()}useCtx(t){}onDestroy(){}onBeforeRender(t){}onAfterRender(t){}onResize(t){}onMount(t){}onUnmount(t){}onLoopRun(t){}onLoopStop(t){}_log(t){m.log(this,t)}initCtxEventMethods(t){const e=m.prototype;this.onAfterRender!==e.onAfterRender&&this.iehm(t.three,"renderafter","onAfterRender"),this.onBeforeRender!==e.onBeforeRender&&this.iehm(t.three,"renderbefore","onBeforeRender"),this.onMount!==e.onMount&&this.iehm(t.three,"mount","onMount"),this.onResize!==e.onResize&&this.iehm(t.three,"unmount","onUnmount"),this.onLoopRun!==e.onLoopRun&&this.iehm(t,"looprun","onLoopRun"),this.onLoopStop!==e.onLoopStop&&this.iehm(t,"loopstop","onLoopStop")}iehm(t,e,r){let s=null;const n=n=>{s=function(){this[r](n)},t.on(e,s,this)};this.on(x.AttCtx,n),this.on(x.DetCtx,(()=>{s&&t.off(e,s,this)})),this._ctx&&n(this._ctx)}}m.generateUUID=()=>{try{return crypto.randomUUID()}catch(t){return`${Math.random()}-${Date.now()}`}},m.log=()=>{};let y=0;class g extends l{static create(t,e,r){const s=f.create(t,r);return new g(s,s.scene,e)}get root(){return this._root}get deltaTime(){return this._deltaTime}get time(){return this._time}get isDestroyed(){return this._isDestroyed}get isRunning(){return this._isRunning}constructor(t,e,n){super(),this._time=0,this._deltaTime=0,this._isDestroyed=!1,this._isRunning=!1,this._cleanups={},this._clock=t.clock,r(this,{isCoreContext:s(!0),modules:s({}),three:s(t)});const i=v.from(null!=e?e:t.scene).setCtx(this).object;i.isRoot=!0,this._root=i,n&&this.assignModules(n)}run(){this._isRunning||this._isDestroyed||(this._isRunning=!0,this._clock.start(),this.three.renderer.setAnimationLoop((()=>{
//! its very important to getDelta() before getElapsedTime()
this._deltaTime=this._clock.getDelta(),this._time=this._clock.getElapsedTime(),this.three.render()})),this.emit("looprun"))}stop(){this._isRunning=!1,this._clock.stop(),this.three.renderer.setAnimationLoop(null),this.emit("loopstop")}assignModules(t){for(const e in t){const r=t[e];r&&this.assignModule(e,r)}return this}assignModule(t,e){if(this.modules[t])return void console.warn(`Key [${t.toString()}] is already assinged in modules.`);this.modules[t]=e;const r=e;r._ctx=this;const s=r.useCtx(this);this._cleanups[t]=s}removeModule(t){const e=this._cleanups[t];delete this._cleanups[t],e&&"function"==typeof e&&e();this.modules[t]._ctx=void 0,delete this.modules[t]}destroy(){return this._isDestroyed||(this._isDestroyed=!0,this.stop(),this.three.destroy(),this.emit("destroy"),v.destroy(this._root,!0),Object.values(this._cleanups).forEach((t=>t&&t())),["destroy","looprun","loopstop"].forEach((t=>this.removeAllListeners(t)))),this}}const b={removeArrayItem:e,props:i,traverseUp:o,assertDefined:h},C="2.0.0",R="__THREE_KVY_CORE__";"undefined"!=typeof window&&(window[R]?console.warn("WARNING: Multiple instances of `@vladkrutenyuk/three-kvy-core` being imported."):window[R]=C),t.CoreContext=g,t.CoreContextModule=class extends l{get ctx(){return h(this._ctx,"ctx")}get hasCtx(){return!!this._ctx}constructor(){super(),r(this,{isCoreContextModule:s(!0)})}useCtx(t){}},t.Object3DFeaturability=v,t.Object3DFeature=m,t.REVISION=C,t.ThreeContext=f,t.addFeature=function(t,e,r,s){return v.from(t).addFeature(e,r,s)},t.clear=function(t,e){e?t.traverse(v.destroy):v.destroy(t)},t.getFeature=function(t,e){var r,s;return null!==(s=null===(r=v.extract(t))||void 0===r?void 0:r.getFeature(e))&&void 0!==s?s:null},t.getFeatureBy=function(t,e){var r,s;return null!==(s=null===(r=v.extract(t))||void 0===r?void 0:r.getFeatureBy(e))&&void 0!==s?s:null},t.getFeatures=function(t){var e,r;return null!==(r=null===(e=v.extract(t))||void 0===e?void 0:e.features)&&void 0!==r?r:null},t.utils=b}));