@motion-core/motion-gpu 0.6.0 → 0.8.0
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/README.md +40 -1
- package/dist/core/pointer.d.ts +96 -0
- package/dist/core/pointer.d.ts.map +1 -0
- package/dist/core/pointer.js +71 -0
- package/dist/core/pointer.js.map +1 -0
- package/dist/motion-gpu.css +295 -0
- package/dist/react/advanced.js +2 -1
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +2 -1
- package/dist/react/use-pointer.d.ts +94 -0
- package/dist/react/use-pointer.d.ts.map +1 -0
- package/dist/react/use-pointer.js +285 -0
- package/dist/react/use-pointer.js.map +1 -0
- package/dist/svelte/advanced.js +2 -1
- package/dist/svelte/index.d.ts +2 -0
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +2 -1
- package/dist/svelte/use-pointer.d.ts +94 -0
- package/dist/svelte/use-pointer.d.ts.map +1 -0
- package/dist/svelte/use-pointer.js +292 -0
- package/dist/svelte/use-pointer.js.map +1 -0
- package/dist/vue/FragCanvas.js +8 -0
- package/dist/vue/FragCanvas.js.map +1 -0
- package/dist/vue/FragCanvas.vue.d.ts +49 -0
- package/dist/vue/FragCanvas.vue.d.ts.map +1 -0
- package/dist/vue/FragCanvas.vue_vue_type_script_setup_true_lang.js +228 -0
- package/dist/vue/FragCanvas.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/vue/MotionGPUErrorOverlay.js +8 -0
- package/dist/vue/MotionGPUErrorOverlay.js.map +1 -0
- package/dist/vue/MotionGPUErrorOverlay.vue.d.ts +8 -0
- package/dist/vue/MotionGPUErrorOverlay.vue.d.ts.map +1 -0
- package/dist/vue/MotionGPUErrorOverlay.vue_vue_type_script_setup_true_lang.js +166 -0
- package/dist/vue/MotionGPUErrorOverlay.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/vue/Portal.js +7 -0
- package/dist/vue/Portal.js.map +1 -0
- package/dist/vue/Portal.vue.d.ts +18 -0
- package/dist/vue/Portal.vue.d.ts.map +1 -0
- package/dist/vue/Portal.vue_vue_type_script_setup_true_lang.js +29 -0
- package/dist/vue/Portal.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/vue/advanced.d.ts +12 -0
- package/dist/vue/advanced.d.ts.map +1 -0
- package/dist/vue/advanced.js +15 -0
- package/dist/vue/frame-context.d.ts +22 -0
- package/dist/vue/frame-context.d.ts.map +1 -0
- package/dist/vue/frame-context.js +38 -0
- package/dist/vue/frame-context.js.map +1 -0
- package/dist/vue/index.d.ts +21 -0
- package/dist/vue/index.d.ts.map +1 -0
- package/dist/vue/index.js +14 -0
- package/dist/vue/motiongpu-context.d.ts +81 -0
- package/dist/vue/motiongpu-context.d.ts.map +1 -0
- package/dist/vue/motiongpu-context.js +29 -0
- package/dist/vue/motiongpu-context.js.map +1 -0
- package/dist/vue/shims-vue.d.js +0 -0
- package/dist/vue/use-motiongpu-user-context.d.ts +44 -0
- package/dist/vue/use-motiongpu-user-context.d.ts.map +1 -0
- package/dist/vue/use-motiongpu-user-context.js +76 -0
- package/dist/vue/use-motiongpu-user-context.js.map +1 -0
- package/dist/vue/use-pointer.d.ts +94 -0
- package/dist/vue/use-pointer.d.ts.map +1 -0
- package/dist/vue/use-pointer.js +298 -0
- package/dist/vue/use-pointer.js.map +1 -0
- package/dist/vue/use-texture.d.ts +45 -0
- package/dist/vue/use-texture.d.ts.map +1 -0
- package/dist/vue/use-texture.js +135 -0
- package/dist/vue/use-texture.js.map +1 -0
- package/package.json +25 -7
- package/src/lib/core/pointer.ts +177 -0
- package/src/lib/react/index.ts +10 -0
- package/src/lib/react/use-pointer.ts +515 -0
- package/src/lib/svelte/index.ts +10 -0
- package/src/lib/svelte/use-pointer.ts +507 -0
- package/src/lib/vue/FragCanvas.vue +294 -0
- package/src/lib/vue/MotionGPUErrorOverlay.vue +518 -0
- package/src/lib/vue/Portal.vue +46 -0
- package/src/lib/vue/advanced.ts +32 -0
- package/src/lib/vue/frame-context.ts +96 -0
- package/src/lib/vue/index.ts +78 -0
- package/src/lib/vue/motiongpu-context.ts +97 -0
- package/src/lib/vue/shims-vue.d.ts +6 -0
- package/src/lib/vue/use-motiongpu-user-context.ts +145 -0
- package/src/lib/vue/use-pointer.ts +514 -0
- package/src/lib/vue/use-texture.ts +232 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { type InjectionKey } from 'vue';
|
|
2
|
+
import type { RenderMode } from '../core/types.js';
|
|
3
|
+
import type { CurrentReadable, CurrentWritable } from '../core/current-value.js';
|
|
4
|
+
import type { FrameProfilingSnapshot, FrameRunTimings, FrameScheduleSnapshot } from '../core/frame-registry.js';
|
|
5
|
+
import type { MotionGPUScheduler as CoreMotionGPUScheduler } from '../core/scheduler-helpers.js';
|
|
6
|
+
export type MotionGPUScheduler = CoreMotionGPUScheduler;
|
|
7
|
+
export type { FrameProfilingSnapshot, FrameRunTimings, FrameScheduleSnapshot };
|
|
8
|
+
/**
|
|
9
|
+
* Namespace identifier for user-owned context entries.
|
|
10
|
+
*/
|
|
11
|
+
export type MotionGPUUserNamespace = string | symbol;
|
|
12
|
+
/**
|
|
13
|
+
* Shared user context store exposed by `FragCanvas`.
|
|
14
|
+
*/
|
|
15
|
+
export type MotionGPUUserContext = CurrentWritable<Record<MotionGPUUserNamespace, unknown>>;
|
|
16
|
+
/**
|
|
17
|
+
* Public `FragCanvas` runtime context available to composables and user components.
|
|
18
|
+
*/
|
|
19
|
+
export interface MotionGPUContext {
|
|
20
|
+
/**
|
|
21
|
+
* Underlying canvas element used by the renderer.
|
|
22
|
+
*/
|
|
23
|
+
canvas: HTMLCanvasElement | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Reactive canvas pixel size.
|
|
26
|
+
*/
|
|
27
|
+
size: CurrentReadable<{
|
|
28
|
+
width: number;
|
|
29
|
+
height: number;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* Device pixel ratio multiplier.
|
|
33
|
+
*/
|
|
34
|
+
dpr: CurrentWritable<number>;
|
|
35
|
+
/**
|
|
36
|
+
* Max frame delta clamp passed to scheduled callbacks.
|
|
37
|
+
*/
|
|
38
|
+
maxDelta: CurrentWritable<number>;
|
|
39
|
+
/**
|
|
40
|
+
* Scheduler render mode (`always`, `on-demand`, `manual`).
|
|
41
|
+
*/
|
|
42
|
+
renderMode: CurrentWritable<RenderMode>;
|
|
43
|
+
/**
|
|
44
|
+
* Global toggle for automatic rendering.
|
|
45
|
+
*/
|
|
46
|
+
autoRender: CurrentWritable<boolean>;
|
|
47
|
+
/**
|
|
48
|
+
* Namespaced user context store shared within the canvas subtree.
|
|
49
|
+
*/
|
|
50
|
+
user: MotionGPUUserContext;
|
|
51
|
+
/**
|
|
52
|
+
* Marks current frame as invalidated.
|
|
53
|
+
*/
|
|
54
|
+
invalidate: () => void;
|
|
55
|
+
/**
|
|
56
|
+
* Requests one manual frame advance.
|
|
57
|
+
*/
|
|
58
|
+
advance: () => void;
|
|
59
|
+
/**
|
|
60
|
+
* Public scheduler API.
|
|
61
|
+
*/
|
|
62
|
+
scheduler: MotionGPUScheduler;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Vue injection key used to expose `FragCanvas` runtime state.
|
|
66
|
+
*/
|
|
67
|
+
export declare const motionGPUContextKey: InjectionKey<MotionGPUContext>;
|
|
68
|
+
/**
|
|
69
|
+
* Registers the motiongpu context in the current Vue component tree.
|
|
70
|
+
*
|
|
71
|
+
* @param context - Context payload to provide.
|
|
72
|
+
*/
|
|
73
|
+
export declare function provideMotionGPUContext(context: MotionGPUContext): void;
|
|
74
|
+
/**
|
|
75
|
+
* Returns the active motiongpu context.
|
|
76
|
+
*
|
|
77
|
+
* @returns Active context.
|
|
78
|
+
* @throws {Error} When called outside `<FragCanvas>`.
|
|
79
|
+
*/
|
|
80
|
+
export declare function useMotionGPU(): MotionGPUContext;
|
|
81
|
+
//# sourceMappingURL=motiongpu-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"motiongpu-context.d.ts","sourceRoot":"","sources":["../../src/lib/vue/motiongpu-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,KAAK,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACjF,OAAO,KAAK,EACX,sBAAsB,EACtB,eAAe,EACf,qBAAqB,EACrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,kBAAkB,IAAI,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAEjG,MAAM,MAAM,kBAAkB,GAAG,sBAAsB,CAAC;AACxD,YAAY,EAAE,sBAAsB,EAAE,eAAe,EAAE,qBAAqB,EAAE,CAAC;AAE/E;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,MAAM,CAAC;AAErD;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,eAAe,CAAC,MAAM,CAAC,sBAAsB,EAAE,OAAO,CAAC,CAAC,CAAC;AAE5F;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC;;OAEG;IACH,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACtC;;OAEG;IACH,IAAI,EAAE,eAAe,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzD;;OAEG;IACH,GAAG,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IAC7B;;OAEG;IACH,QAAQ,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IAClC;;OAEG;IACH,UAAU,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC;IACxC;;OAEG;IACH,UAAU,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;IACrC;;OAEG;IACH,IAAI,EAAE,oBAAoB,CAAC;IAC3B;;OAEG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB;;OAEG;IACH,SAAS,EAAE,kBAAkB,CAAC;CAC9B;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB,EAAE,YAAY,CAAC,gBAAgB,CAA+B,CAAC;AAE/F;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAEvE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,gBAAgB,CAO/C"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { inject, provide } from "vue";
|
|
2
|
+
//#region src/lib/vue/motiongpu-context.ts
|
|
3
|
+
/**
|
|
4
|
+
* Vue injection key used to expose `FragCanvas` runtime state.
|
|
5
|
+
*/
|
|
6
|
+
var motionGPUContextKey = Symbol("motiongpu.context");
|
|
7
|
+
/**
|
|
8
|
+
* Registers the motiongpu context in the current Vue component tree.
|
|
9
|
+
*
|
|
10
|
+
* @param context - Context payload to provide.
|
|
11
|
+
*/
|
|
12
|
+
function provideMotionGPUContext(context) {
|
|
13
|
+
provide(motionGPUContextKey, context);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Returns the active motiongpu context.
|
|
17
|
+
*
|
|
18
|
+
* @returns Active context.
|
|
19
|
+
* @throws {Error} When called outside `<FragCanvas>`.
|
|
20
|
+
*/
|
|
21
|
+
function useMotionGPU() {
|
|
22
|
+
const context = inject(motionGPUContextKey, null);
|
|
23
|
+
if (!context) throw new Error("useMotionGPU must be used inside <FragCanvas>");
|
|
24
|
+
return context;
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { motionGPUContextKey, provideMotionGPUContext, useMotionGPU };
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=motiongpu-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"motiongpu-context.js","names":[],"sources":["../../src/lib/vue/motiongpu-context.ts"],"sourcesContent":["import { inject, provide, type InjectionKey } from 'vue';\nimport type { RenderMode } from '../core/types.js';\nimport type { CurrentReadable, CurrentWritable } from '../core/current-value.js';\nimport type {\n\tFrameProfilingSnapshot,\n\tFrameRunTimings,\n\tFrameScheduleSnapshot\n} from '../core/frame-registry.js';\nimport type { MotionGPUScheduler as CoreMotionGPUScheduler } from '../core/scheduler-helpers.js';\n\nexport type MotionGPUScheduler = CoreMotionGPUScheduler;\nexport type { FrameProfilingSnapshot, FrameRunTimings, FrameScheduleSnapshot };\n\n/**\n * Namespace identifier for user-owned context entries.\n */\nexport type MotionGPUUserNamespace = string | symbol;\n\n/**\n * Shared user context store exposed by `FragCanvas`.\n */\nexport type MotionGPUUserContext = CurrentWritable<Record<MotionGPUUserNamespace, unknown>>;\n\n/**\n * Public `FragCanvas` runtime context available to composables and user components.\n */\nexport interface MotionGPUContext {\n\t/**\n\t * Underlying canvas element used by the renderer.\n\t */\n\tcanvas: HTMLCanvasElement | undefined;\n\t/**\n\t * Reactive canvas pixel size.\n\t */\n\tsize: CurrentReadable<{ width: number; height: number }>;\n\t/**\n\t * Device pixel ratio multiplier.\n\t */\n\tdpr: CurrentWritable<number>;\n\t/**\n\t * Max frame delta clamp passed to scheduled callbacks.\n\t */\n\tmaxDelta: CurrentWritable<number>;\n\t/**\n\t * Scheduler render mode (`always`, `on-demand`, `manual`).\n\t */\n\trenderMode: CurrentWritable<RenderMode>;\n\t/**\n\t * Global toggle for automatic rendering.\n\t */\n\tautoRender: CurrentWritable<boolean>;\n\t/**\n\t * Namespaced user context store shared within the canvas subtree.\n\t */\n\tuser: MotionGPUUserContext;\n\t/**\n\t * Marks current frame as invalidated.\n\t */\n\tinvalidate: () => void;\n\t/**\n\t * Requests one manual frame advance.\n\t */\n\tadvance: () => void;\n\t/**\n\t * Public scheduler API.\n\t */\n\tscheduler: MotionGPUScheduler;\n}\n\n/**\n * Vue injection key used to expose `FragCanvas` runtime state.\n */\nexport const motionGPUContextKey: InjectionKey<MotionGPUContext> = Symbol('motiongpu.context');\n\n/**\n * Registers the motiongpu context in the current Vue component tree.\n *\n * @param context - Context payload to provide.\n */\nexport function provideMotionGPUContext(context: MotionGPUContext): void {\n\tprovide(motionGPUContextKey, context);\n}\n\n/**\n * Returns the active motiongpu context.\n *\n * @returns Active context.\n * @throws {Error} When called outside `<FragCanvas>`.\n */\nexport function useMotionGPU(): MotionGPUContext {\n\tconst context = inject(motionGPUContextKey, null);\n\tif (!context) {\n\t\tthrow new Error('useMotionGPU must be used inside <FragCanvas>');\n\t}\n\n\treturn context;\n}\n"],"mappings":";;;;;AAwEA,IAAa,sBAAsD,OAAO,oBAAoB;;;;;;AAO9F,SAAgB,wBAAwB,SAAiC;AACxE,SAAQ,qBAAqB,QAAQ;;;;;;;;AAStC,SAAgB,eAAiC;CAChD,MAAM,UAAU,OAAO,qBAAqB,KAAK;AACjD,KAAI,CAAC,QACJ,OAAM,IAAI,MAAM,gDAAgD;AAGjE,QAAO"}
|
|
File without changes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { CurrentReadable } from '../core/current-value.js';
|
|
2
|
+
import { type MotionGPUUserNamespace } from './motiongpu-context.js';
|
|
3
|
+
/**
|
|
4
|
+
* Internal shape of the user context store.
|
|
5
|
+
*/
|
|
6
|
+
type UserContextStore = Record<MotionGPUUserNamespace, unknown>;
|
|
7
|
+
/**
|
|
8
|
+
* Controls how a namespaced user context value behaves when already present.
|
|
9
|
+
*/
|
|
10
|
+
export interface SetMotionGPUUserContextOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Conflict strategy when namespace already exists:
|
|
13
|
+
* - `skip`: keep current value
|
|
14
|
+
* - `replace`: replace current value
|
|
15
|
+
* - `merge`: shallow merge object values, fallback to replace otherwise
|
|
16
|
+
*
|
|
17
|
+
* @default 'skip'
|
|
18
|
+
*/
|
|
19
|
+
existing?: 'merge' | 'replace' | 'skip';
|
|
20
|
+
/**
|
|
21
|
+
* How function inputs should be interpreted:
|
|
22
|
+
* - `factory`: call function and store its return value
|
|
23
|
+
* - `value`: store function itself
|
|
24
|
+
*
|
|
25
|
+
* @default 'factory'
|
|
26
|
+
*/
|
|
27
|
+
functionValue?: 'factory' | 'value';
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Returns a read-only view of the entire motiongpu user context store.
|
|
31
|
+
*/
|
|
32
|
+
export declare function useMotionGPUUserContext<UC extends UserContextStore = UserContextStore>(): CurrentReadable<UC>;
|
|
33
|
+
/**
|
|
34
|
+
* Reads a namespaced user context value as a reactive readable store.
|
|
35
|
+
*/
|
|
36
|
+
export declare function useMotionGPUUserContext<UC extends UserContextStore = UserContextStore, K extends keyof UC & MotionGPUUserNamespace = keyof UC & MotionGPUUserNamespace>(namespace: K): CurrentReadable<UC[K] | undefined>;
|
|
37
|
+
/**
|
|
38
|
+
* Sets a namespaced user context value with explicit write semantics.
|
|
39
|
+
*
|
|
40
|
+
* Returns the effective value stored under the namespace.
|
|
41
|
+
*/
|
|
42
|
+
export declare function setMotionGPUUserContext<UCT = unknown>(namespace: MotionGPUUserNamespace, value: UCT | (() => UCT), options?: SetMotionGPUUserContextOptions): UCT | undefined;
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=use-motiongpu-user-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-motiongpu-user-context.d.ts","sourceRoot":"","sources":["../../src/lib/vue/use-motiongpu-user-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAgB,KAAK,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAEnF;;GAEG;AACH,KAAK,gBAAgB,GAAG,MAAM,CAAC,sBAAsB,EAAE,OAAO,CAAC,CAAC;AAOhE;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC9C;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACxC;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;CACpC;AASD;;GAEG;AACH,wBAAgB,uBAAuB,CACtC,EAAE,SAAS,gBAAgB,GAAG,gBAAgB,KAC1C,eAAe,CAAC,EAAE,CAAC,CAAC;AAEzB;;GAEG;AACH,wBAAgB,uBAAuB,CACtC,EAAE,SAAS,gBAAgB,GAAG,gBAAgB,EAC9C,CAAC,SAAS,MAAM,EAAE,GAAG,sBAAsB,GAAG,MAAM,EAAE,GAAG,sBAAsB,EAC9E,SAAS,EAAE,CAAC,GAAG,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;AAwCpD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,GAAG,OAAO,EACpD,SAAS,EAAE,sBAAsB,EACjC,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EACxB,OAAO,CAAC,EAAE,8BAA8B,GACtC,GAAG,GAAG,SAAS,CAuCjB"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { useMotionGPU } from "./motiongpu-context.js";
|
|
2
|
+
//#region src/lib/vue/use-motiongpu-user-context.ts
|
|
3
|
+
/**
|
|
4
|
+
* Checks whether a value is a non-array object suitable for shallow merge.
|
|
5
|
+
*/
|
|
6
|
+
function isObjectEntry(value) {
|
|
7
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Read-only user context composable:
|
|
11
|
+
* - no args: returns full user context store
|
|
12
|
+
* - namespace: returns namespaced store view
|
|
13
|
+
*
|
|
14
|
+
* @param namespace - Optional namespace key.
|
|
15
|
+
*/
|
|
16
|
+
function useMotionGPUUserContext(namespace) {
|
|
17
|
+
const userStore = useMotionGPU().user;
|
|
18
|
+
if (namespace === void 0) return {
|
|
19
|
+
get current() {
|
|
20
|
+
return userStore.current;
|
|
21
|
+
},
|
|
22
|
+
subscribe(run) {
|
|
23
|
+
return userStore.subscribe((context) => run(context));
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
return {
|
|
27
|
+
get current() {
|
|
28
|
+
return userStore.current[namespace];
|
|
29
|
+
},
|
|
30
|
+
subscribe(run) {
|
|
31
|
+
return userStore.subscribe((context) => run(context[namespace]));
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Sets a namespaced user context value with explicit write semantics.
|
|
37
|
+
*
|
|
38
|
+
* Returns the effective value stored under the namespace.
|
|
39
|
+
*/
|
|
40
|
+
function setMotionGPUUserContext(namespace, value, options) {
|
|
41
|
+
const userStore = useMotionGPU().user;
|
|
42
|
+
const mode = options?.existing ?? "skip";
|
|
43
|
+
const functionValueMode = options?.functionValue ?? "factory";
|
|
44
|
+
let resolvedValue;
|
|
45
|
+
userStore.update((context) => {
|
|
46
|
+
const hasExisting = namespace in context;
|
|
47
|
+
if (hasExisting && mode === "skip") {
|
|
48
|
+
resolvedValue = context[namespace];
|
|
49
|
+
return context;
|
|
50
|
+
}
|
|
51
|
+
const nextValue = typeof value === "function" && functionValueMode === "factory" ? value() : value;
|
|
52
|
+
if (hasExisting && mode === "merge") {
|
|
53
|
+
const currentValue = context[namespace];
|
|
54
|
+
if (isObjectEntry(currentValue) && isObjectEntry(nextValue)) {
|
|
55
|
+
resolvedValue = {
|
|
56
|
+
...currentValue,
|
|
57
|
+
...nextValue
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
...context,
|
|
61
|
+
[namespace]: resolvedValue
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
resolvedValue = nextValue;
|
|
66
|
+
return {
|
|
67
|
+
...context,
|
|
68
|
+
[namespace]: nextValue
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
return resolvedValue;
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { setMotionGPUUserContext, useMotionGPUUserContext };
|
|
75
|
+
|
|
76
|
+
//# sourceMappingURL=use-motiongpu-user-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-motiongpu-user-context.js","names":[],"sources":["../../src/lib/vue/use-motiongpu-user-context.ts"],"sourcesContent":["import type { CurrentReadable } from '../core/current-value.js';\nimport { useMotionGPU, type MotionGPUUserNamespace } from './motiongpu-context.js';\n\n/**\n * Internal shape of the user context store.\n */\ntype UserContextStore = Record<MotionGPUUserNamespace, unknown>;\n\n/**\n * Object-like context payload used by merge semantics.\n */\ntype UserContextEntry = Record<string, unknown>;\n\n/**\n * Controls how a namespaced user context value behaves when already present.\n */\nexport interface SetMotionGPUUserContextOptions {\n\t/**\n\t * Conflict strategy when namespace already exists:\n\t * - `skip`: keep current value\n\t * - `replace`: replace current value\n\t * - `merge`: shallow merge object values, fallback to replace otherwise\n\t *\n\t * @default 'skip'\n\t */\n\texisting?: 'merge' | 'replace' | 'skip';\n\t/**\n\t * How function inputs should be interpreted:\n\t * - `factory`: call function and store its return value\n\t * - `value`: store function itself\n\t *\n\t * @default 'factory'\n\t */\n\tfunctionValue?: 'factory' | 'value';\n}\n\n/**\n * Checks whether a value is a non-array object suitable for shallow merge.\n */\nfunction isObjectEntry(value: unknown): value is UserContextEntry {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Returns a read-only view of the entire motiongpu user context store.\n */\nexport function useMotionGPUUserContext<\n\tUC extends UserContextStore = UserContextStore\n>(): CurrentReadable<UC>;\n\n/**\n * Reads a namespaced user context value as a reactive readable store.\n */\nexport function useMotionGPUUserContext<\n\tUC extends UserContextStore = UserContextStore,\n\tK extends keyof UC & MotionGPUUserNamespace = keyof UC & MotionGPUUserNamespace\n>(namespace: K): CurrentReadable<UC[K] | undefined>;\n\n/**\n * Read-only user context composable:\n * - no args: returns full user context store\n * - namespace: returns namespaced store view\n *\n * @param namespace - Optional namespace key.\n */\nexport function useMotionGPUUserContext<\n\tUC extends UserContextStore = UserContextStore,\n\tK extends keyof UC & MotionGPUUserNamespace = keyof UC & MotionGPUUserNamespace\n>(namespace?: K): CurrentReadable<UC> | CurrentReadable<UC[K] | undefined> {\n\tconst userStore = useMotionGPU().user;\n\n\tif (namespace === undefined) {\n\t\tconst allStore: CurrentReadable<UC> = {\n\t\t\tget current() {\n\t\t\t\treturn userStore.current as UC;\n\t\t\t},\n\t\t\tsubscribe(run) {\n\t\t\t\treturn userStore.subscribe((context) => run(context as UC));\n\t\t\t}\n\t\t};\n\n\t\treturn allStore;\n\t}\n\n\tconst scopedStore: CurrentReadable<UC[K] | undefined> = {\n\t\tget current() {\n\t\t\treturn userStore.current[namespace] as UC[K] | undefined;\n\t\t},\n\t\tsubscribe(run) {\n\t\t\treturn userStore.subscribe((context) => run(context[namespace] as UC[K] | undefined));\n\t\t}\n\t};\n\n\treturn scopedStore;\n}\n\n/**\n * Sets a namespaced user context value with explicit write semantics.\n *\n * Returns the effective value stored under the namespace.\n */\nexport function setMotionGPUUserContext<UCT = unknown>(\n\tnamespace: MotionGPUUserNamespace,\n\tvalue: UCT | (() => UCT),\n\toptions?: SetMotionGPUUserContextOptions\n): UCT | undefined {\n\tconst userStore = useMotionGPU().user;\n\tconst mode = options?.existing ?? 'skip';\n\tconst functionValueMode = options?.functionValue ?? 'factory';\n\tlet resolvedValue: UCT | undefined;\n\n\tuserStore.update((context) => {\n\t\tconst hasExisting = namespace in context;\n\t\tif (hasExisting && mode === 'skip') {\n\t\t\tresolvedValue = context[namespace] as UCT | undefined;\n\t\t\treturn context;\n\t\t}\n\n\t\tconst nextValue =\n\t\t\ttypeof value === 'function' && functionValueMode === 'factory'\n\t\t\t\t? (value as () => UCT)()\n\t\t\t\t: (value as UCT);\n\t\tif (hasExisting && mode === 'merge') {\n\t\t\tconst currentValue = context[namespace];\n\t\t\tif (isObjectEntry(currentValue) && isObjectEntry(nextValue)) {\n\t\t\t\tresolvedValue = {\n\t\t\t\t\t...currentValue,\n\t\t\t\t\t...nextValue\n\t\t\t\t} as UCT;\n\t\t\t\treturn {\n\t\t\t\t\t...context,\n\t\t\t\t\t[namespace]: resolvedValue\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tresolvedValue = nextValue;\n\t\treturn {\n\t\t\t...context,\n\t\t\t[namespace]: nextValue\n\t\t};\n\t});\n\n\treturn resolvedValue;\n}\n"],"mappings":";;;;;AAuCA,SAAS,cAAc,OAA2C;AACjE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM;;;;;;;;;AAyB5E,SAAgB,wBAGd,WAAyE;CAC1E,MAAM,YAAY,cAAc,CAAC;AAEjC,KAAI,cAAc,OAUjB,QATsC;EACrC,IAAI,UAAU;AACb,UAAO,UAAU;;EAElB,UAAU,KAAK;AACd,UAAO,UAAU,WAAW,YAAY,IAAI,QAAc,CAAC;;EAE5D;AAcF,QATwD;EACvD,IAAI,UAAU;AACb,UAAO,UAAU,QAAQ;;EAE1B,UAAU,KAAK;AACd,UAAO,UAAU,WAAW,YAAY,IAAI,QAAQ,WAAgC,CAAC;;EAEtF;;;;;;;AAUF,SAAgB,wBACf,WACA,OACA,SACkB;CAClB,MAAM,YAAY,cAAc,CAAC;CACjC,MAAM,OAAO,SAAS,YAAY;CAClC,MAAM,oBAAoB,SAAS,iBAAiB;CACpD,IAAI;AAEJ,WAAU,QAAQ,YAAY;EAC7B,MAAM,cAAc,aAAa;AACjC,MAAI,eAAe,SAAS,QAAQ;AACnC,mBAAgB,QAAQ;AACxB,UAAO;;EAGR,MAAM,YACL,OAAO,UAAU,cAAc,sBAAsB,YACjD,OAAqB,GACrB;AACL,MAAI,eAAe,SAAS,SAAS;GACpC,MAAM,eAAe,QAAQ;AAC7B,OAAI,cAAc,aAAa,IAAI,cAAc,UAAU,EAAE;AAC5D,oBAAgB;KACf,GAAG;KACH,GAAG;KACH;AACD,WAAO;KACN,GAAG;MACF,YAAY;KACb;;;AAIH,kBAAgB;AAChB,SAAO;GACN,GAAG;IACF,YAAY;GACb;GACA;AAEF,QAAO"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { type CurrentReadable } from '../core/current-value.js';
|
|
2
|
+
import { type PointerClick, type PointerFrameRequestMode, type PointerState } from '../core/pointer.js';
|
|
3
|
+
export type { PointerClick, PointerFrameRequestMode, PointerKind, PointerPoint, PointerState } from '../core/pointer.js';
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for pointer input handling in `usePointer`.
|
|
6
|
+
*/
|
|
7
|
+
export interface UsePointerOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Enables pointer listeners.
|
|
10
|
+
*
|
|
11
|
+
* @default true
|
|
12
|
+
*/
|
|
13
|
+
enabled?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Frame wake-up strategy for pointer-driven state changes.
|
|
16
|
+
*
|
|
17
|
+
* @default 'auto'
|
|
18
|
+
*/
|
|
19
|
+
requestFrame?: PointerFrameRequestMode;
|
|
20
|
+
/**
|
|
21
|
+
* Requests pointer capture on pointer down.
|
|
22
|
+
*
|
|
23
|
+
* @default true
|
|
24
|
+
*/
|
|
25
|
+
capturePointer?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Tracks pointer move/up outside canvas while pointer is pressed.
|
|
28
|
+
*
|
|
29
|
+
* @default true
|
|
30
|
+
*/
|
|
31
|
+
trackWhilePressedOutsideCanvas?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Enables click/tap synthesis on pointer up.
|
|
34
|
+
*
|
|
35
|
+
* @default true
|
|
36
|
+
*/
|
|
37
|
+
clickEnabled?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Maximum press duration to consider pointer up a click (milliseconds).
|
|
40
|
+
*
|
|
41
|
+
* @default 350
|
|
42
|
+
*/
|
|
43
|
+
clickMaxDurationMs?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Maximum pointer travel from down to up to consider pointer up a click (pixels).
|
|
46
|
+
*
|
|
47
|
+
* @default 8
|
|
48
|
+
*/
|
|
49
|
+
clickMaxMovePx?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Allowed pointer buttons for click synthesis.
|
|
52
|
+
*
|
|
53
|
+
* @default [0]
|
|
54
|
+
*/
|
|
55
|
+
clickButtons?: number[];
|
|
56
|
+
/**
|
|
57
|
+
* Called after pointer move state update.
|
|
58
|
+
*/
|
|
59
|
+
onMove?: (state: PointerState, event: PointerEvent) => void;
|
|
60
|
+
/**
|
|
61
|
+
* Called after pointer down state update.
|
|
62
|
+
*/
|
|
63
|
+
onDown?: (state: PointerState, event: PointerEvent) => void;
|
|
64
|
+
/**
|
|
65
|
+
* Called after pointer up/cancel state update.
|
|
66
|
+
*/
|
|
67
|
+
onUp?: (state: PointerState, event: PointerEvent) => void;
|
|
68
|
+
/**
|
|
69
|
+
* Called when click/tap is synthesized.
|
|
70
|
+
*/
|
|
71
|
+
onClick?: (click: PointerClick, state: PointerState, event: PointerEvent) => void;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Reactive state returned by `usePointer`.
|
|
75
|
+
*/
|
|
76
|
+
export interface UsePointerResult {
|
|
77
|
+
/**
|
|
78
|
+
* Current pointer state.
|
|
79
|
+
*/
|
|
80
|
+
state: CurrentReadable<PointerState>;
|
|
81
|
+
/**
|
|
82
|
+
* Last synthesized click/tap event.
|
|
83
|
+
*/
|
|
84
|
+
lastClick: CurrentReadable<PointerClick | null>;
|
|
85
|
+
/**
|
|
86
|
+
* Clears last click snapshot.
|
|
87
|
+
*/
|
|
88
|
+
resetClick: () => void;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Tracks normalized pointer coordinates and click/tap snapshots for the active `FragCanvas`.
|
|
92
|
+
*/
|
|
93
|
+
export declare function usePointer(options?: UsePointerOptions): UsePointerResult;
|
|
94
|
+
//# sourceMappingURL=use-pointer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-pointer.d.ts","sourceRoot":"","sources":["../../src/lib/vue/use-pointer.ts"],"names":[],"mappings":"AACA,OAAO,EAEN,KAAK,eAAe,EACpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAMN,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EAEjB,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EACX,YAAY,EACZ,uBAAuB,EACvB,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,MAAM,oBAAoB,CAAC;AAE5B;;GAEG;AACH,MAAM,WAAW,iBAAiB;IACjC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,YAAY,CAAC,EAAE,uBAAuB,CAAC;IACvC;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,8BAA8B,CAAC,EAAE,OAAO,CAAC;IACzC;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;OAEG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5D;;OAEG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5D;;OAEG;IACH,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAC1D;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CAClF;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC;;OAEG;IACH,KAAK,EAAE,eAAe,CAAC,YAAY,CAAC,CAAC;IACrC;;OAEG;IACH,SAAS,EAAE,eAAe,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAChD;;OAEG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;CACvB;AA0CD;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,gBAAgB,CAoW5E"}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { createCurrentWritable } from "../core/current-value.js";
|
|
2
|
+
import { createInitialPointerState, getPointerCoordinates, getPointerNowSeconds, normalizePointerKind, resolvePointerFrameRequestMode } from "../core/pointer.js";
|
|
3
|
+
import { useMotionGPU } from "./motiongpu-context.js";
|
|
4
|
+
import { onBeforeUnmount, onMounted } from "vue";
|
|
5
|
+
//#region src/lib/vue/use-pointer.ts
|
|
6
|
+
/**
|
|
7
|
+
* Normalizes click button configuration with a primary-button fallback.
|
|
8
|
+
*/
|
|
9
|
+
function normalizeClickButtons(buttons) {
|
|
10
|
+
const source = buttons && buttons.length > 0 ? buttons : [0];
|
|
11
|
+
return new Set(source);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Resolves a valid click duration threshold in milliseconds.
|
|
15
|
+
*/
|
|
16
|
+
function resolveClickMaxDurationMs(value) {
|
|
17
|
+
if (typeof value !== "number" || Number.isNaN(value) || value <= 0) return 350;
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Resolves a valid click travel threshold in pixels.
|
|
22
|
+
*/
|
|
23
|
+
function resolveClickMaxMovePx(value) {
|
|
24
|
+
if (typeof value !== "number" || Number.isNaN(value) || value < 0) return 8;
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Tracks normalized pointer coordinates and click/tap snapshots for the active `FragCanvas`.
|
|
29
|
+
*/
|
|
30
|
+
function usePointer(options = {}) {
|
|
31
|
+
const motiongpu = useMotionGPU();
|
|
32
|
+
const pointerState = createCurrentWritable(createInitialPointerState());
|
|
33
|
+
const lastClick = createCurrentWritable(null);
|
|
34
|
+
const enabled = options.enabled ?? true;
|
|
35
|
+
const requestFrameMode = options.requestFrame ?? "auto";
|
|
36
|
+
const capturePointer = options.capturePointer ?? true;
|
|
37
|
+
const trackOutside = options.trackWhilePressedOutsideCanvas ?? true;
|
|
38
|
+
const clickEnabled = options.clickEnabled ?? true;
|
|
39
|
+
const clickMaxDurationMs = resolveClickMaxDurationMs(options.clickMaxDurationMs);
|
|
40
|
+
const clickMaxMovePx = resolveClickMaxMovePx(options.clickMaxMovePx);
|
|
41
|
+
const clickButtons = normalizeClickButtons(options.clickButtons);
|
|
42
|
+
let activePointerId = null;
|
|
43
|
+
let downSnapshot = null;
|
|
44
|
+
let clickCounter = 0;
|
|
45
|
+
let previousPx = null;
|
|
46
|
+
let previousUv = null;
|
|
47
|
+
let previousTimeSeconds = 0;
|
|
48
|
+
let cleanup = null;
|
|
49
|
+
const requestFrame = () => {
|
|
50
|
+
const mode = resolvePointerFrameRequestMode(requestFrameMode, motiongpu.renderMode.current);
|
|
51
|
+
if (mode === "invalidate") {
|
|
52
|
+
motiongpu.invalidate();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (mode === "advance") motiongpu.advance();
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Commits a full pointer state snapshot with computed delta and velocity vectors.
|
|
59
|
+
*/
|
|
60
|
+
const updatePointerState = (input) => {
|
|
61
|
+
const nowSeconds = getPointerNowSeconds();
|
|
62
|
+
const dt = previousTimeSeconds > 0 ? Math.max(nowSeconds - previousTimeSeconds, 1e-6) : 0;
|
|
63
|
+
const deltaPx = input.resetDelta || !previousPx ? [0, 0] : [input.point.px[0] - previousPx[0], input.point.px[1] - previousPx[1]];
|
|
64
|
+
const deltaUv = input.resetDelta || !previousUv ? [0, 0] : [input.point.uv[0] - previousUv[0], input.point.uv[1] - previousUv[1]];
|
|
65
|
+
const velocityPx = dt > 0 ? [deltaPx[0] / dt, deltaPx[1] / dt] : [0, 0];
|
|
66
|
+
const velocityUv = dt > 0 ? [deltaUv[0] / dt, deltaUv[1] / dt] : [0, 0];
|
|
67
|
+
const nextState = {
|
|
68
|
+
px: input.point.px,
|
|
69
|
+
uv: input.point.uv,
|
|
70
|
+
ndc: input.point.ndc,
|
|
71
|
+
inside: input.inside,
|
|
72
|
+
pressed: input.pressed,
|
|
73
|
+
dragging: input.dragging,
|
|
74
|
+
pointerType: input.pointerType,
|
|
75
|
+
pointerId: input.pointerId,
|
|
76
|
+
button: input.button,
|
|
77
|
+
buttons: input.buttons,
|
|
78
|
+
time: nowSeconds,
|
|
79
|
+
downPx: input.downPx,
|
|
80
|
+
downUv: input.downUv,
|
|
81
|
+
deltaPx,
|
|
82
|
+
deltaUv,
|
|
83
|
+
velocityPx,
|
|
84
|
+
velocityUv
|
|
85
|
+
};
|
|
86
|
+
pointerState.set(nextState);
|
|
87
|
+
previousPx = input.point.px;
|
|
88
|
+
previousUv = input.point.uv;
|
|
89
|
+
previousTimeSeconds = nowSeconds;
|
|
90
|
+
requestFrame();
|
|
91
|
+
return nextState;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Updates only the `inside` flag while keeping the latest pointer coordinates.
|
|
95
|
+
*/
|
|
96
|
+
const updateInsideState = (inside) => {
|
|
97
|
+
const nextState = {
|
|
98
|
+
...pointerState.current,
|
|
99
|
+
inside,
|
|
100
|
+
time: getPointerNowSeconds(),
|
|
101
|
+
deltaPx: [0, 0],
|
|
102
|
+
deltaUv: [0, 0],
|
|
103
|
+
velocityPx: [0, 0],
|
|
104
|
+
velocityUv: [0, 0]
|
|
105
|
+
};
|
|
106
|
+
pointerState.set(nextState);
|
|
107
|
+
requestFrame();
|
|
108
|
+
return nextState;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Checks whether an event belongs to the active tracked pointer.
|
|
112
|
+
*/
|
|
113
|
+
const isTrackedPointer = (event) => activePointerId === null || event.pointerId === activePointerId;
|
|
114
|
+
const attachListeners = () => {
|
|
115
|
+
if (!enabled) return;
|
|
116
|
+
const canvas = motiongpu.canvas;
|
|
117
|
+
if (!canvas) return;
|
|
118
|
+
const handlePointerDown = (event) => {
|
|
119
|
+
const point = getPointerCoordinates(event.clientX, event.clientY, canvas.getBoundingClientRect());
|
|
120
|
+
const pointerType = normalizePointerKind(event.pointerType);
|
|
121
|
+
activePointerId = event.pointerId;
|
|
122
|
+
downSnapshot = {
|
|
123
|
+
pointerId: event.pointerId,
|
|
124
|
+
pointerType,
|
|
125
|
+
button: event.button,
|
|
126
|
+
timeMs: getPointerNowSeconds() * 1e3,
|
|
127
|
+
px: point.px,
|
|
128
|
+
uv: point.uv,
|
|
129
|
+
inside: point.inside
|
|
130
|
+
};
|
|
131
|
+
if (capturePointer) try {
|
|
132
|
+
canvas.setPointerCapture(event.pointerId);
|
|
133
|
+
} catch {}
|
|
134
|
+
const nextState = updatePointerState({
|
|
135
|
+
point,
|
|
136
|
+
inside: point.inside,
|
|
137
|
+
pressed: true,
|
|
138
|
+
dragging: false,
|
|
139
|
+
pointerType,
|
|
140
|
+
pointerId: event.pointerId,
|
|
141
|
+
button: event.button,
|
|
142
|
+
buttons: event.buttons,
|
|
143
|
+
downPx: point.px,
|
|
144
|
+
downUv: point.uv,
|
|
145
|
+
resetDelta: true
|
|
146
|
+
});
|
|
147
|
+
options.onDown?.(nextState, event);
|
|
148
|
+
};
|
|
149
|
+
const handleMove = (event) => {
|
|
150
|
+
if (!isTrackedPointer(event)) return;
|
|
151
|
+
const point = getPointerCoordinates(event.clientX, event.clientY, canvas.getBoundingClientRect());
|
|
152
|
+
const pressed = activePointerId !== null && event.pointerId === activePointerId;
|
|
153
|
+
const downPx = pressed ? downSnapshot?.px ?? point.px : null;
|
|
154
|
+
const downUv = pressed ? downSnapshot?.uv ?? point.uv : null;
|
|
155
|
+
let dragging = false;
|
|
156
|
+
if (pressed && downPx) {
|
|
157
|
+
const dx = point.px[0] - downPx[0];
|
|
158
|
+
const dy = point.px[1] - downPx[1];
|
|
159
|
+
dragging = Math.hypot(dx, dy) > 0;
|
|
160
|
+
}
|
|
161
|
+
const nextState = updatePointerState({
|
|
162
|
+
point,
|
|
163
|
+
inside: point.inside,
|
|
164
|
+
pressed,
|
|
165
|
+
dragging,
|
|
166
|
+
pointerType: normalizePointerKind(event.pointerType),
|
|
167
|
+
pointerId: event.pointerId,
|
|
168
|
+
button: pressed ? downSnapshot?.button ?? event.button : null,
|
|
169
|
+
buttons: event.buttons,
|
|
170
|
+
downPx,
|
|
171
|
+
downUv
|
|
172
|
+
});
|
|
173
|
+
options.onMove?.(nextState, event);
|
|
174
|
+
};
|
|
175
|
+
const handleWindowMove = (event) => {
|
|
176
|
+
if (!trackOutside || activePointerId === null || event.pointerId !== activePointerId) return;
|
|
177
|
+
const point = getPointerCoordinates(event.clientX, event.clientY, canvas.getBoundingClientRect());
|
|
178
|
+
if (point.inside) return;
|
|
179
|
+
const downPx = downSnapshot?.px ?? point.px;
|
|
180
|
+
const downUv = downSnapshot?.uv ?? point.uv;
|
|
181
|
+
const dx = point.px[0] - downPx[0];
|
|
182
|
+
const dy = point.px[1] - downPx[1];
|
|
183
|
+
const nextState = updatePointerState({
|
|
184
|
+
point,
|
|
185
|
+
inside: false,
|
|
186
|
+
pressed: true,
|
|
187
|
+
dragging: Math.hypot(dx, dy) > 0,
|
|
188
|
+
pointerType: downSnapshot?.pointerType ?? normalizePointerKind(event.pointerType),
|
|
189
|
+
pointerId: event.pointerId,
|
|
190
|
+
button: downSnapshot?.button ?? event.button,
|
|
191
|
+
buttons: event.buttons,
|
|
192
|
+
downPx,
|
|
193
|
+
downUv
|
|
194
|
+
});
|
|
195
|
+
options.onMove?.(nextState, event);
|
|
196
|
+
};
|
|
197
|
+
const releasePointer = (event, emitClick) => {
|
|
198
|
+
if (activePointerId === null || event.pointerId !== activePointerId) return;
|
|
199
|
+
const point = getPointerCoordinates(event.clientX, event.clientY, canvas.getBoundingClientRect());
|
|
200
|
+
const pointerType = downSnapshot?.pointerType ?? normalizePointerKind(event.pointerType);
|
|
201
|
+
const previous = downSnapshot;
|
|
202
|
+
const nextState = updatePointerState({
|
|
203
|
+
point,
|
|
204
|
+
inside: point.inside,
|
|
205
|
+
pressed: false,
|
|
206
|
+
dragging: false,
|
|
207
|
+
pointerType,
|
|
208
|
+
pointerId: null,
|
|
209
|
+
button: null,
|
|
210
|
+
buttons: event.buttons,
|
|
211
|
+
downPx: null,
|
|
212
|
+
downUv: null
|
|
213
|
+
});
|
|
214
|
+
options.onUp?.(nextState, event);
|
|
215
|
+
if (capturePointer && canvas.hasPointerCapture(event.pointerId)) try {
|
|
216
|
+
canvas.releasePointerCapture(event.pointerId);
|
|
217
|
+
} catch {}
|
|
218
|
+
if (emitClick && clickEnabled && previous && clickButtons.has(previous.button)) {
|
|
219
|
+
const durationMs = getPointerNowSeconds() * 1e3 - previous.timeMs;
|
|
220
|
+
const dx = point.px[0] - previous.px[0];
|
|
221
|
+
const dy = point.px[1] - previous.px[1];
|
|
222
|
+
const moveDistance = Math.hypot(dx, dy);
|
|
223
|
+
if (previous.inside && point.inside && durationMs <= clickMaxDurationMs && moveDistance <= clickMaxMovePx) {
|
|
224
|
+
clickCounter += 1;
|
|
225
|
+
const click = {
|
|
226
|
+
id: clickCounter,
|
|
227
|
+
time: getPointerNowSeconds(),
|
|
228
|
+
pointerType,
|
|
229
|
+
pointerId: event.pointerId,
|
|
230
|
+
button: previous.button,
|
|
231
|
+
modifiers: {
|
|
232
|
+
alt: event.altKey,
|
|
233
|
+
ctrl: event.ctrlKey,
|
|
234
|
+
shift: event.shiftKey,
|
|
235
|
+
meta: event.metaKey
|
|
236
|
+
},
|
|
237
|
+
px: point.px,
|
|
238
|
+
uv: point.uv,
|
|
239
|
+
ndc: point.ndc
|
|
240
|
+
};
|
|
241
|
+
lastClick.set(click);
|
|
242
|
+
options.onClick?.(click, nextState, event);
|
|
243
|
+
requestFrame();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
activePointerId = null;
|
|
247
|
+
downSnapshot = null;
|
|
248
|
+
};
|
|
249
|
+
const handlePointerUp = (event) => {
|
|
250
|
+
releasePointer(event, true);
|
|
251
|
+
};
|
|
252
|
+
const handlePointerCancel = (event) => {
|
|
253
|
+
releasePointer(event, false);
|
|
254
|
+
};
|
|
255
|
+
const handlePointerLeave = () => {
|
|
256
|
+
if (activePointerId !== null) return;
|
|
257
|
+
updateInsideState(false);
|
|
258
|
+
};
|
|
259
|
+
canvas.addEventListener("pointerdown", handlePointerDown);
|
|
260
|
+
canvas.addEventListener("pointermove", handleMove);
|
|
261
|
+
canvas.addEventListener("pointerup", handlePointerUp);
|
|
262
|
+
canvas.addEventListener("pointercancel", handlePointerCancel);
|
|
263
|
+
canvas.addEventListener("pointerleave", handlePointerLeave);
|
|
264
|
+
if (trackOutside) {
|
|
265
|
+
window.addEventListener("pointermove", handleWindowMove);
|
|
266
|
+
window.addEventListener("pointerup", handlePointerUp);
|
|
267
|
+
window.addEventListener("pointercancel", handlePointerCancel);
|
|
268
|
+
}
|
|
269
|
+
cleanup = () => {
|
|
270
|
+
canvas.removeEventListener("pointerdown", handlePointerDown);
|
|
271
|
+
canvas.removeEventListener("pointermove", handleMove);
|
|
272
|
+
canvas.removeEventListener("pointerup", handlePointerUp);
|
|
273
|
+
canvas.removeEventListener("pointercancel", handlePointerCancel);
|
|
274
|
+
canvas.removeEventListener("pointerleave", handlePointerLeave);
|
|
275
|
+
if (trackOutside) {
|
|
276
|
+
window.removeEventListener("pointermove", handleWindowMove);
|
|
277
|
+
window.removeEventListener("pointerup", handlePointerUp);
|
|
278
|
+
window.removeEventListener("pointercancel", handlePointerCancel);
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
};
|
|
282
|
+
onMounted(attachListeners);
|
|
283
|
+
onBeforeUnmount(() => {
|
|
284
|
+
cleanup?.();
|
|
285
|
+
cleanup = null;
|
|
286
|
+
});
|
|
287
|
+
return {
|
|
288
|
+
state: pointerState,
|
|
289
|
+
lastClick,
|
|
290
|
+
resetClick: () => {
|
|
291
|
+
lastClick.set(null);
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
//#endregion
|
|
296
|
+
export { usePointer };
|
|
297
|
+
|
|
298
|
+
//# sourceMappingURL=use-pointer.js.map
|