@zakkster/lite-camera-pro 1.0.0 → 1.1.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/CHANGELOG.md +122 -0
- package/README.md +7 -0
- package/llms.txt +15 -0
- package/package.json +50 -6
- package/src/BoundsSystem.d.ts +54 -0
- package/src/BoundsSystem.js +1 -1
- package/src/CameraSequence.d.ts +75 -0
- package/src/CinematicCameraPro.js +42 -8
- package/src/FollowMode.d.ts +25 -0
- package/src/MultiTarget.d.ts +49 -0
- package/src/MultiTarget.js +1 -1
- package/src/ParallaxManager.d.ts +60 -0
- package/src/ParallaxManager.js +4 -1
- package/src/Shake.d.ts +82 -0
- package/src/Shake.js +7 -0
- package/src/ShakeEngine.js +19 -1
- package/src/index.d.ts +37 -130
- package/src/index.js +13 -11
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@zakkster/lite-camera-pro` are documented here. The
|
|
4
|
+
format follows Keep a Changelog, and this project adheres to Semantic
|
|
5
|
+
Versioning. Version lives in three places at once -- `package.json`, the
|
|
6
|
+
`VERSION` const in `src/index.js`, and the `Version:` header in `llms.txt` --
|
|
7
|
+
bumped together or not at all.
|
|
8
|
+
|
|
9
|
+
## [1.1.0] -- 2026-08-26
|
|
10
|
+
|
|
11
|
+
Subpath exports. A consumer who needs only screen shake now imports
|
|
12
|
+
`@zakkster/lite-camera-pro/shake` and pays for the shake engine plus its
|
|
13
|
+
tree-shaken noise sampler -- nothing else. No runtime behavior changed: the hot
|
|
14
|
+
bodies (`computeShake`, `updateShake`, `apply`, `updateParallax`, `applyBounds`,
|
|
15
|
+
`updateMultiTarget`) are byte-identical, and the camera class imports the same
|
|
16
|
+
module files the subpaths expose (one engine, no fork -- proven by the T8
|
|
17
|
+
Object.is identity check).
|
|
18
|
+
|
|
19
|
+
Measured: the Las Vegas scratch-card consumer (BRIEF.md) reported a 73.5 KB gz
|
|
20
|
+
whole-package pull for a three-tier win-shake ramp that touches only the shake
|
|
21
|
+
API. Through `@zakkster/lite-camera-pro/shake` the same integration measures
|
|
22
|
+
**2.82 KB gz** (2,883 B; 8.79 KB raw, esbuild esm, minify=false, gzip -9) --
|
|
23
|
+
about **26x smaller**. Of that bundle, lite-noise's `Noise.js` contributes
|
|
24
|
+
2,009 B (5.1% of its 39,613 B source), so tree-shaking through the noise
|
|
25
|
+
dependency works as intended.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- **Subpath exports map.** Seven entries beside `.`: `./shake`, `./parallax`,
|
|
30
|
+
`./bounds`, `./multi`, `./follow`, `./sequence`, and `./package.json`. Each
|
|
31
|
+
runtime entry carries a sibling `types` condition (declared first, per the
|
|
32
|
+
TypeScript exports-map requirement). Per-subpath gz weights (esm, unminified,
|
|
33
|
+
gzip -9): `./shake` 2.82 KB, `./parallax` 0.89 KB, `./bounds` 1.13 KB,
|
|
34
|
+
`./multi` 0.89 KB, `./follow` 0.71 KB, `./sequence` 5.94 KB, `.` 21.70 KB.
|
|
35
|
+
`./sequence` drags `@zakkster/lite-timeline` + `@zakkster/lite-ease` by design
|
|
36
|
+
-- it is the only subpath that does.
|
|
37
|
+
- **`src/Shake.js` barrel.** A two-line re-export over `ShakeEngine.js` +
|
|
38
|
+
`ShakePresets.js` (never a copy), so a shake-only consumer gets the engine,
|
|
39
|
+
the presets, and `getPreset`/`registerPreset`/`listPresets` from one import.
|
|
40
|
+
- **Typed functional layer (CP-16a).** `src/index.d.ts` now re-exports six
|
|
41
|
+
per-subsystem sibling declarations (`Shake.d.ts`, `ParallaxManager.d.ts`,
|
|
42
|
+
`BoundsSystem.d.ts`, `MultiTarget.d.ts`, `FollowMode.d.ts`,
|
|
43
|
+
`CameraSequence.d.ts`), each declaring its module's complete runtime surface
|
|
44
|
+
-- state interfaces, enums, defaults, no `any`. The standalone functions are
|
|
45
|
+
now visible and typed at the main entry, not just the class.
|
|
46
|
+
- **Size gate.** `test/size.mjs` bundles every subpath with the esbuild JS API
|
|
47
|
+
and asserts `./shake` gz stays at or under the fixed 16,384 B charter budget.
|
|
48
|
+
Wired into `npm run verify`.
|
|
49
|
+
- **Dependency decision record (CP-17).** `decisions/0001-layout-and-deps.md`
|
|
50
|
+
documents the multi-file layout and the five first-party runtime deps, with
|
|
51
|
+
per-dep floor evidence. Repo-only; not shipped in the tarball.
|
|
52
|
+
- **TypeScript smoke.** `test/types-smoke/smoke.ts` exercises every subpath plus
|
|
53
|
+
the main entry under `strict` + `noImplicitAny` + node16 resolution;
|
|
54
|
+
`npm run typecheck` runs it and joins `prepublishOnly`.
|
|
55
|
+
|
|
56
|
+
## [1.0.1] -- 2026-08-25
|
|
57
|
+
|
|
58
|
+
Foundation release: make the suite run, gate it, and land the fixes that cost
|
|
59
|
+
nothing at the public surface. No behavior change to any valid, finite,
|
|
60
|
+
in-bounds call -- CP-13 and CP-14 change only what defective input does.
|
|
61
|
+
|
|
62
|
+
### Fixed
|
|
63
|
+
|
|
64
|
+
- **CP-1 -- the standalone functional API is now reachable from the package
|
|
65
|
+
entry.** `src/index.js` re-exports `createShakeState` (Shake Engine) and
|
|
66
|
+
`createMultiTargetState` (Multi-Target); before, every state-taking function
|
|
67
|
+
in the documented tree-shakeable layer took a state no consumer could
|
|
68
|
+
construct. Reported in `BRIEF.md` by the Las Vegas scratch-card consumer.
|
|
69
|
+
- **CP-8 -- use-after-destroy fails closed.** After
|
|
70
|
+
`CinematicCameraPro.destroy()`, EVERY public method throws an error with code
|
|
71
|
+
`ERR_CAMERA_DESTROYED` (fail closed) instead of a raw null deref. destroy()
|
|
72
|
+
calls `super.destroy()` for base parity, then rebinds the whole public surface
|
|
73
|
+
to the named-error sentinel, so the guarantee cannot drift as methods are
|
|
74
|
+
added. A double `destroy()` throws the same named error.
|
|
75
|
+
- **CP-13 -- uniform floor snap.** `apply()` and `applyParallaxLayer()` now
|
|
76
|
+
snap the world/layer scroll with `Math.floor` instead of `| 0`, matching the
|
|
77
|
+
base camera. `| 0` truncated toward zero, disagreeing with the base by a full
|
|
78
|
+
pixel at negative fractional positions and taking a double-length integer step
|
|
79
|
+
about the origin.
|
|
80
|
+
- **CP-14 + H-F -- trauma default fail-closed.** `addShake`/`addTraumaSimple`
|
|
81
|
+
replaced the falsy `profile.trauma || 0.5` default: `undefined` still means
|
|
82
|
+
0.5, but a non-finite trauma/intensity now activates NO slot (fail closed) and
|
|
83
|
+
an explicit `0` fires nothing. The old `||` laundered `NaN` into 0.5 -- the
|
|
84
|
+
only reason a NaN could not poison the shake sum from the trauma side (H-F).
|
|
85
|
+
The laundering is removed and the poison door closed in one change. Guards live
|
|
86
|
+
in the cold entry functions only; the per-frame `updateShake`/`computeShake`
|
|
87
|
+
loops are unchanged.
|
|
88
|
+
|
|
89
|
+
### Added
|
|
90
|
+
|
|
91
|
+
- **node:test suite.** Ported both vitest suites to `node:test` +
|
|
92
|
+
`node:assert/strict` (facade + standalone subsystems), added
|
|
93
|
+
`test/consumer.test.js` (the feel-freeze pinning the consumer's three-tier
|
|
94
|
+
shake ramp), `test/regressions.test.js` (one named test per finding above),
|
|
95
|
+
and `test/metadata.test.js` (three-place version law, packaging, CP-1 export
|
|
96
|
+
guard). Removed vitest and its config.
|
|
97
|
+
- **Torture gate.** `test/torture.mjs` plus a tiered harness
|
|
98
|
+
(`@zakkster/lite-gc-profiler` + `@zakkster/lite-leak` + `@zakkster/lite-signal`
|
|
99
|
+
dev-only): metamorphic laws (T0), degenerate known-bad pins (T1), lifecycle
|
|
100
|
+
abuse (T4), the zero-alloc budget gate (T6: 200k `update`+`apply` under active
|
|
101
|
+
shake, `maxMajor:0`/`maxPauseMs:4`, pool-not-reallocated), retention soak (T7),
|
|
102
|
+
and the BREAK control (T9). `CAMPRO_TORTURE_BREAK=1` exits non-zero.
|
|
103
|
+
- **Version law.** `export const VERSION = "1.0.1"` from `src/index.js`, this
|
|
104
|
+
`CHANGELOG.md`, and a `Version:` header in `llms.txt`.
|
|
105
|
+
|
|
106
|
+
### Changed
|
|
107
|
+
|
|
108
|
+
- Corrected three stale dependency header comments: `src/index.js` no longer
|
|
109
|
+
credits `lite-random` (not a dependency); `src/BoundsSystem.js` and
|
|
110
|
+
`src/MultiTarget.js` no longer claim a `@zakkster/lite-lerp` dependency they
|
|
111
|
+
do not import (their clamps are inline).
|
|
112
|
+
|
|
113
|
+
## [1.0.0] -- 2025-06-28
|
|
114
|
+
|
|
115
|
+
Initial release. Cinematic Canvas2D camera system extending
|
|
116
|
+
`@zakkster/lite-camera`'s `CinematicCamera`: smooth/eased zoom, five follow
|
|
117
|
+
modes, multi-target auto-framing, an 8-slot simplex-noise shake engine with
|
|
118
|
+
presets, fluent timeline sequences, a 16-layer parallax manager, per-edge
|
|
119
|
+
bounds, a debug HUD, and zero-alloc coordinate conversion.
|
|
120
|
+
|
|
121
|
+
[1.0.1]: https://github.com/PeshoVurtoleta/lite-camera-pro/releases/tag/v1.0.1
|
|
122
|
+
[1.0.0]: https://github.com/PeshoVurtoleta/lite-camera-pro/releases/tag/v1.0.0
|
package/README.md
CHANGED
|
@@ -20,6 +20,13 @@
|
|
|
20
20
|
npm install @zakkster/lite-camera-pro
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
Need only screen shake? Import the `./shake` subpath and pull just the engine +
|
|
24
|
+
presets (2.82 KB gz -- esm, unminified, gzip -9):
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
import { createShakeState, addShake, updateShake, computeShake, getPreset } from '@zakkster/lite-camera-pro/shake';
|
|
28
|
+
```
|
|
29
|
+
|
|
23
30
|
---
|
|
24
31
|
|
|
25
32
|
## Why Pro?
|
package/llms.txt
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
> with presets, fluent timeline sequences, a 16-layer parallax manager, per-edge
|
|
7
7
|
> bounds, a debug HUD, and zero-alloc coordinate conversion.
|
|
8
8
|
|
|
9
|
+
Version: 1.1.0
|
|
10
|
+
|
|
9
11
|
ESM only. `import` from `@zakkster/lite-camera-pro`. Default export is the
|
|
10
12
|
CinematicCameraPro class. All hot-path APIs avoid allocation; setup-time calls
|
|
11
13
|
(constructor, createSequence, trackMultiple) may allocate.
|
|
@@ -107,6 +109,19 @@ Multi: createMultiTargetState, updateMultiTarget
|
|
|
107
109
|
Sequence: createCameraSequence, panTo, dramaticZoom, bossReveal, timedShake
|
|
108
110
|
Enums: FollowMode, FOLLOW_STRATEGIES, BoundsType, WrapMode
|
|
109
111
|
|
|
112
|
+
## Subpath exports (import only what you use; gz, esm, unminified, gzip -9)
|
|
113
|
+
Beside the "." main entry, six subsystem subpaths + "./package.json". Same runtime
|
|
114
|
+
identities as the main entry (one engine, no fork). Per-subpath gz weight:
|
|
115
|
+
./shake 2.82 KB createShakeState, addShake, addTraumaSimple, updateShake, computeShake, clearShakes + presets/getPreset/registerPreset/listPresets
|
|
116
|
+
./parallax 0.89 KB createParallaxState, addParallaxLayer, removeParallaxLayer, updateParallax, getLayerScroll, applyParallaxLayer, WrapMode
|
|
117
|
+
./bounds 1.13 KB createBoundsState, setBoundsAll, setBoundsEdges, setBoundsRect, clearBoundsRect, applyBounds, BoundsType
|
|
118
|
+
./multi 0.89 KB createMultiTargetState, updateMultiTarget
|
|
119
|
+
./follow 0.71 KB FollowMode, FOLLOW_STRATEGIES
|
|
120
|
+
./sequence 5.94 KB createCameraSequence, panTo, dramaticZoom, bossReveal, timedShake (drags lite-timeline + lite-ease by design -- the only subpath that does)
|
|
121
|
+
. 21.70 KB full class + functional layer
|
|
122
|
+
Self-reference example (shake only):
|
|
123
|
+
import { createShakeState, addShake, updateShake, computeShake, getPreset } from '@zakkster/lite-camera-pro/shake';
|
|
124
|
+
|
|
110
125
|
## Dependencies (all @zakkster, first-party)
|
|
111
126
|
@zakkster/lite-camera (base CinematicCamera), lite-ease, lite-lerp, lite-noise, lite-timeline.
|
|
112
127
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zakkster/lite-camera-pro",
|
|
3
3
|
"author": "Zahary Shinikchiev <shinikchiev@yahoo.com>",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"description": "Cinematic camera system for Canvas2D games. Zero-GC, multi-target framing, noise-based shake, and timeline sequences.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -13,11 +13,49 @@
|
|
|
13
13
|
"node": "./src/index.js",
|
|
14
14
|
"import": "./src/index.js",
|
|
15
15
|
"default": "./src/index.js"
|
|
16
|
-
}
|
|
16
|
+
},
|
|
17
|
+
"./shake": {
|
|
18
|
+
"types": "./src/Shake.d.ts",
|
|
19
|
+
"node": "./src/Shake.js",
|
|
20
|
+
"import": "./src/Shake.js",
|
|
21
|
+
"default": "./src/Shake.js"
|
|
22
|
+
},
|
|
23
|
+
"./parallax": {
|
|
24
|
+
"types": "./src/ParallaxManager.d.ts",
|
|
25
|
+
"node": "./src/ParallaxManager.js",
|
|
26
|
+
"import": "./src/ParallaxManager.js",
|
|
27
|
+
"default": "./src/ParallaxManager.js"
|
|
28
|
+
},
|
|
29
|
+
"./bounds": {
|
|
30
|
+
"types": "./src/BoundsSystem.d.ts",
|
|
31
|
+
"node": "./src/BoundsSystem.js",
|
|
32
|
+
"import": "./src/BoundsSystem.js",
|
|
33
|
+
"default": "./src/BoundsSystem.js"
|
|
34
|
+
},
|
|
35
|
+
"./multi": {
|
|
36
|
+
"types": "./src/MultiTarget.d.ts",
|
|
37
|
+
"node": "./src/MultiTarget.js",
|
|
38
|
+
"import": "./src/MultiTarget.js",
|
|
39
|
+
"default": "./src/MultiTarget.js"
|
|
40
|
+
},
|
|
41
|
+
"./follow": {
|
|
42
|
+
"types": "./src/FollowMode.d.ts",
|
|
43
|
+
"node": "./src/FollowMode.js",
|
|
44
|
+
"import": "./src/FollowMode.js",
|
|
45
|
+
"default": "./src/FollowMode.js"
|
|
46
|
+
},
|
|
47
|
+
"./sequence": {
|
|
48
|
+
"types": "./src/CameraSequence.d.ts",
|
|
49
|
+
"node": "./src/CameraSequence.js",
|
|
50
|
+
"import": "./src/CameraSequence.js",
|
|
51
|
+
"default": "./src/CameraSequence.js"
|
|
52
|
+
},
|
|
53
|
+
"./package.json": "./package.json"
|
|
17
54
|
},
|
|
18
55
|
"files": [
|
|
19
56
|
"src/",
|
|
20
57
|
"README.md",
|
|
58
|
+
"CHANGELOG.md",
|
|
21
59
|
"llms.txt",
|
|
22
60
|
"LICENSE"
|
|
23
61
|
],
|
|
@@ -46,14 +84,20 @@
|
|
|
46
84
|
"@zakkster/lite-timeline": "^1.0.0"
|
|
47
85
|
},
|
|
48
86
|
"devDependencies": {
|
|
87
|
+
"@zakkster/lite-gc-profiler": "^1.16.0",
|
|
88
|
+
"@zakkster/lite-leak": "^1.10.0",
|
|
89
|
+
"@zakkster/lite-signal": "^1.5.0",
|
|
49
90
|
"esbuild": "^0.25.0",
|
|
50
|
-
"
|
|
91
|
+
"typescript": "7.0.2"
|
|
51
92
|
},
|
|
52
93
|
"scripts": {
|
|
53
|
-
"test": "
|
|
54
|
-
"test:
|
|
94
|
+
"test": "node --test test/*.test.js",
|
|
95
|
+
"test:gc": "node --expose-gc --test test/*.test.js",
|
|
96
|
+
"torture": "node --expose-gc test/torture.mjs",
|
|
97
|
+
"verify": "npm run test:gc && npm run torture && node test/size.mjs",
|
|
98
|
+
"typecheck": "tsc -p test/types-smoke/tsconfig.json",
|
|
55
99
|
"bundle-check": "esbuild ./src/index.js --bundle --format=esm --outfile=test-bundle.js",
|
|
56
|
-
"prepublishOnly": "npm run
|
|
100
|
+
"prepublishOnly": "npm run verify && npm run typecheck && npm run bundle-check"
|
|
57
101
|
},
|
|
58
102
|
"keywords": [
|
|
59
103
|
"camera",
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zakkster/lite-camera-pro/bounds -- TypeScript declarations.
|
|
3
|
+
*
|
|
4
|
+
* Configurable per-edge boundary behavior. Pure math, no dependencies.
|
|
5
|
+
* Complete runtime surface, no `any`.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// -- Boundary types --
|
|
9
|
+
export declare const BoundsType: {
|
|
10
|
+
readonly HARD: 0;
|
|
11
|
+
readonly SOFT: 1;
|
|
12
|
+
readonly ELASTIC: 2;
|
|
13
|
+
readonly NONE: 3;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// -- Bounds system state (one per camera) --
|
|
17
|
+
export interface BoundsState {
|
|
18
|
+
left: number;
|
|
19
|
+
right: number;
|
|
20
|
+
top: number;
|
|
21
|
+
bottom: number;
|
|
22
|
+
softZone: number;
|
|
23
|
+
elasticMax: number;
|
|
24
|
+
elasticStrength: number;
|
|
25
|
+
boundsX: number;
|
|
26
|
+
boundsY: number;
|
|
27
|
+
boundsW: number;
|
|
28
|
+
boundsH: number;
|
|
29
|
+
customBounds: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// -- Per-edge config for setBoundsEdges --
|
|
33
|
+
export interface BoundsEdgesConfig {
|
|
34
|
+
left?: number;
|
|
35
|
+
right?: number;
|
|
36
|
+
top?: number;
|
|
37
|
+
bottom?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export declare function createBoundsState(): BoundsState;
|
|
41
|
+
export declare function setBoundsAll(state: BoundsState, type: number): void;
|
|
42
|
+
export declare function setBoundsEdges(state: BoundsState, config: BoundsEdgesConfig): void;
|
|
43
|
+
export declare function setBoundsRect(state: BoundsState, x: number, y: number, w: number, h: number): void;
|
|
44
|
+
export declare function clearBoundsRect(state: BoundsState): void;
|
|
45
|
+
export declare function applyBounds(
|
|
46
|
+
state: BoundsState,
|
|
47
|
+
target: Float32Array,
|
|
48
|
+
pos: Float32Array,
|
|
49
|
+
maxX: number,
|
|
50
|
+
maxY: number,
|
|
51
|
+
visW: number,
|
|
52
|
+
visH: number,
|
|
53
|
+
dt: number,
|
|
54
|
+
): void;
|
package/src/BoundsSystem.js
CHANGED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zakkster/lite-camera-pro/sequence -- TypeScript declarations.
|
|
3
|
+
*
|
|
4
|
+
* Timeline-driven camera choreography. NOTE: this subpath drags
|
|
5
|
+
* @zakkster/lite-timeline and @zakkster/lite-ease by design -- it is the only
|
|
6
|
+
* subpath that does. Complete runtime surface, no `any`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { CinematicCameraPro } from './index.js';
|
|
10
|
+
import type { ShakeProfile } from './Shake.js';
|
|
11
|
+
|
|
12
|
+
// -- Sequence construction options --
|
|
13
|
+
export interface CameraSequenceOptions {
|
|
14
|
+
loop?: boolean;
|
|
15
|
+
onComplete?: () => void;
|
|
16
|
+
blendOutTime?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// -- Per-step options --
|
|
20
|
+
export interface StepOptions {
|
|
21
|
+
ease?: (t: number) => number;
|
|
22
|
+
at?: string | number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// -- A fluent camera sequence handle --
|
|
26
|
+
export interface CameraSequence {
|
|
27
|
+
moveTo(x: number, y: number, duration: number, opts?: StepOptions): CameraSequence;
|
|
28
|
+
zoomTo(level: number, duration: number, opts?: StepOptions): CameraSequence;
|
|
29
|
+
moveAndZoom(x: number, y: number, level: number, duration: number, opts?: StepOptions): CameraSequence;
|
|
30
|
+
shake(profileOrName: string | ShakeProfile, intensity?: number, opts?: StepOptions): CameraSequence;
|
|
31
|
+
wait(duration: number, opts?: StepOptions): CameraSequence;
|
|
32
|
+
call(fn: () => void, opts?: StepOptions): CameraSequence;
|
|
33
|
+
play(): CameraSequence;
|
|
34
|
+
pause(): CameraSequence;
|
|
35
|
+
resume(): CameraSequence;
|
|
36
|
+
stop(): CameraSequence;
|
|
37
|
+
seek(timeMs: number): CameraSequence;
|
|
38
|
+
destroy(): void;
|
|
39
|
+
readonly duration: number;
|
|
40
|
+
readonly progress: number;
|
|
41
|
+
readonly playing: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export declare function createCameraSequence(cam: CinematicCameraPro, options?: CameraSequenceOptions): CameraSequence;
|
|
45
|
+
export declare function panTo(
|
|
46
|
+
cam: CinematicCameraPro,
|
|
47
|
+
x: number,
|
|
48
|
+
y: number,
|
|
49
|
+
duration: number,
|
|
50
|
+
opts?: CameraSequenceOptions & StepOptions,
|
|
51
|
+
): CameraSequence;
|
|
52
|
+
export declare function dramaticZoom(
|
|
53
|
+
cam: CinematicCameraPro,
|
|
54
|
+
x: number,
|
|
55
|
+
y: number,
|
|
56
|
+
zoom: number,
|
|
57
|
+
duration: number,
|
|
58
|
+
opts?: CameraSequenceOptions & StepOptions,
|
|
59
|
+
): CameraSequence;
|
|
60
|
+
export declare function bossReveal(
|
|
61
|
+
cam: CinematicCameraPro,
|
|
62
|
+
x: number,
|
|
63
|
+
y: number,
|
|
64
|
+
totalMs?: number,
|
|
65
|
+
opts?: CameraSequenceOptions,
|
|
66
|
+
): CameraSequence;
|
|
67
|
+
export declare function timedShake(
|
|
68
|
+
cam: CinematicCameraPro,
|
|
69
|
+
presetOrProfile: string | ShakeProfile,
|
|
70
|
+
holdMs?: number,
|
|
71
|
+
opts?: CameraSequenceOptions,
|
|
72
|
+
): CameraSequence;
|
|
73
|
+
|
|
74
|
+
declare const _default: typeof createCameraSequence;
|
|
75
|
+
export default _default;
|
|
@@ -46,6 +46,16 @@ import {
|
|
|
46
46
|
} from './BoundsSystem.js';
|
|
47
47
|
import {createDebugHUDConfig, drawDebugHUD, drawDebugWorld} from './DebugHUD.js';
|
|
48
48
|
|
|
49
|
+
// Cold-path sentinel: after destroy() every method that reads nulled internal
|
|
50
|
+
// state is rebound to this so a use-after-destroy fails closed with a named
|
|
51
|
+
// error instead of a raw null deref. Rebinding (not an in-body guard) keeps
|
|
52
|
+
// update()/apply() at zero per-frame cost. Matches base CinematicCamera (CP-8).
|
|
53
|
+
const _dead = () => {
|
|
54
|
+
const e = new Error("CinematicCameraPro: use after destroy()");
|
|
55
|
+
e.code = "ERR_CAMERA_DESTROYED";
|
|
56
|
+
throw e;
|
|
57
|
+
};
|
|
58
|
+
|
|
49
59
|
export class CinematicCameraPro extends CinematicCamera {
|
|
50
60
|
|
|
51
61
|
/**
|
|
@@ -798,8 +808,10 @@ export class CinematicCameraPro extends CinematicCamera {
|
|
|
798
808
|
ctx.scale(this.zoom, this.zoom);
|
|
799
809
|
ctx.translate(-this._halfW / this.zoom, -this._halfH / this.zoom);
|
|
800
810
|
|
|
801
|
-
// 6. Scroll to camera world position
|
|
802
|
-
|
|
811
|
+
// 6. Scroll to camera world position.
|
|
812
|
+
// int snap is deliberate: kills texture shimmer. floor (not | 0) so the
|
|
813
|
+
// snap is uniform about the origin -- | 0 truncates toward zero (CP-13).
|
|
814
|
+
ctx.translate(-Math.floor(this.pos[0]), -Math.floor(this.pos[1]));
|
|
803
815
|
}
|
|
804
816
|
|
|
805
817
|
// ─────────────────────────────────────────────────────
|
|
@@ -868,9 +880,10 @@ export class CinematicCameraPro extends CinematicCamera {
|
|
|
868
880
|
}
|
|
869
881
|
|
|
870
882
|
/**
|
|
871
|
-
* Destroy the camera. Releases sequences, clears shake state, and
|
|
872
|
-
*
|
|
873
|
-
*
|
|
883
|
+
* Destroy the camera. Releases sequences, clears shake state, and nulls all
|
|
884
|
+
* nested allocations so the GC can reclaim them. After destroy() the camera
|
|
885
|
+
* is unusable: EVERY public method throws an Error with code
|
|
886
|
+
* "ERR_CAMERA_DESTROYED" (fail closed) rather than a raw null deref.
|
|
874
887
|
*/
|
|
875
888
|
destroy() {
|
|
876
889
|
if (this._seq) {
|
|
@@ -878,16 +891,37 @@ export class CinematicCameraPro extends CinematicCamera {
|
|
|
878
891
|
this._seq = null;
|
|
879
892
|
}
|
|
880
893
|
|
|
894
|
+
// clearShakeState needs _shake still live -- run it before the null.
|
|
881
895
|
clearShakeState(this._shake);
|
|
882
896
|
|
|
883
|
-
// Release nested state so the slot pools / layer arrays can be GC'd
|
|
897
|
+
// Release nested Pro state so the slot pools / layer arrays can be GC'd.
|
|
884
898
|
this._shake = null;
|
|
885
899
|
this._mt = null;
|
|
886
900
|
this._parallax = null;
|
|
887
901
|
this._bounds = null;
|
|
888
902
|
this.debugConfig = null;
|
|
889
|
-
|
|
890
|
-
|
|
903
|
+
|
|
904
|
+
// Null the base typed arrays + rng and rebind the base methods
|
|
905
|
+
// (update/apply/debug/addTrauma/resize/destroy) to the base sentinel.
|
|
906
|
+
// Ordered after the Pro teardown so nothing above reads a nulled base
|
|
907
|
+
// field. T8 asserts parity with the base's destroy contract.
|
|
908
|
+
super.destroy();
|
|
909
|
+
|
|
910
|
+
// CP-8: a destroyed camera fails closed on EVERY public method. Any call
|
|
911
|
+
// throws ERR_CAMERA_DESTROYED (Pro's sentinel -- consistent message)
|
|
912
|
+
// instead of a raw null deref. Rebinding the WHOLE public surface, not a
|
|
913
|
+
// curated subset, means the guarantee cannot silently drift as methods
|
|
914
|
+
// are added -- and re-stamps the base rebinds from super.destroy() with
|
|
915
|
+
// Pro's message for parity. Getters (sequencePlaying) are already
|
|
916
|
+
// null-safe. A double destroy() throws the same named error.
|
|
917
|
+
this.update = this.apply = this.debug = this.debugHUD =
|
|
918
|
+
this.addTrauma = this.shake = this.shakePreset = this.clearShakes =
|
|
919
|
+
this.setMode = this.trackMultiple = this.trackSingle = this.setTargetCount =
|
|
920
|
+
this.createSequence = this.playSequence = this.stopSequence =
|
|
921
|
+
this.addParallaxLayer = this.removeParallaxLayer = this.applyParallax =
|
|
922
|
+
this.setBoundsType = this.setBoundsEdges = this.setBoundsRect = this.clearBoundsRect =
|
|
923
|
+
this.setZoom = this.zoomAt = this.screenToWorld = this.worldToScreen =
|
|
924
|
+
this.getState = this.setState = this.resize = this.destroy = _dead;
|
|
891
925
|
}
|
|
892
926
|
}
|
|
893
927
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zakkster/lite-camera-pro/follow -- TypeScript declarations.
|
|
3
|
+
*
|
|
4
|
+
* Follow-mode enum and the strategy dispatch table. Complete runtime surface,
|
|
5
|
+
* no `any`.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { CinematicCameraPro } from './index.js';
|
|
9
|
+
|
|
10
|
+
// -- Follow modes --
|
|
11
|
+
export declare const FollowMode: {
|
|
12
|
+
readonly SMOOTH: 0;
|
|
13
|
+
readonly LOCK: 1;
|
|
14
|
+
readonly PREDICTIVE: 2;
|
|
15
|
+
readonly CUT: 3;
|
|
16
|
+
readonly HYBRID: 4;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// -- Strategy dispatch table, indexed by FollowMode value --
|
|
20
|
+
export declare const FOLLOW_STRATEGIES: ReadonlyArray<
|
|
21
|
+
(cam: CinematicCameraPro, dt: number, px: number, py: number, pvx: number, pvy: number) => void
|
|
22
|
+
>;
|
|
23
|
+
|
|
24
|
+
declare const _default: typeof FollowMode;
|
|
25
|
+
export default _default;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zakkster/lite-camera-pro/multi -- TypeScript declarations.
|
|
3
|
+
*
|
|
4
|
+
* Multi-target framing: fit several targets in view. Pure math, no
|
|
5
|
+
* dependencies. Complete runtime surface, no `any`.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { CinematicCameraPro } from './index.js';
|
|
9
|
+
|
|
10
|
+
// -- A 2D point (target position) --
|
|
11
|
+
export interface Vec2 {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// -- Multi-target config/state (one per camera) --
|
|
17
|
+
export interface MultiTargetState {
|
|
18
|
+
active: boolean;
|
|
19
|
+
targets: Vec2[] | null;
|
|
20
|
+
count: number;
|
|
21
|
+
paddingX: number;
|
|
22
|
+
paddingY: number;
|
|
23
|
+
minZoom: number;
|
|
24
|
+
maxZoom: number;
|
|
25
|
+
zoomSpeed: number;
|
|
26
|
+
followSpeed: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// -- Options accepted by CinematicCameraPro.trackMultiple --
|
|
30
|
+
export interface MultiTargetOptions {
|
|
31
|
+
paddingX?: number;
|
|
32
|
+
paddingY?: number;
|
|
33
|
+
padding?: number;
|
|
34
|
+
minZoom?: number;
|
|
35
|
+
maxZoom?: number;
|
|
36
|
+
zoomSpeed?: number;
|
|
37
|
+
followSpeed?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export declare function updateMultiTarget(
|
|
41
|
+
cam: CinematicCameraPro,
|
|
42
|
+
dt: number,
|
|
43
|
+
targets: Vec2[],
|
|
44
|
+
count: number,
|
|
45
|
+
): void;
|
|
46
|
+
export declare function createMultiTargetState(): MultiTargetState;
|
|
47
|
+
|
|
48
|
+
declare const _default: typeof updateMultiTarget;
|
|
49
|
+
export default _default;
|
package/src/MultiTarget.js
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zakkster/lite-camera-pro/parallax -- TypeScript declarations.
|
|
3
|
+
*
|
|
4
|
+
* Multi-layer scroll manager. Pure math, no dependencies. Complete runtime
|
|
5
|
+
* surface, no `any`.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// -- Wrap modes for layer tiling --
|
|
9
|
+
export declare const WrapMode: {
|
|
10
|
+
readonly NONE: 0;
|
|
11
|
+
readonly REPEAT_X: 1;
|
|
12
|
+
readonly REPEAT_Y: 2;
|
|
13
|
+
readonly REPEAT_BOTH: 3;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// -- A single pre-allocated parallax layer --
|
|
17
|
+
export interface ParallaxLayer {
|
|
18
|
+
active: boolean;
|
|
19
|
+
id: string;
|
|
20
|
+
speedX: number;
|
|
21
|
+
speedY: number;
|
|
22
|
+
offsetX: number;
|
|
23
|
+
offsetY: number;
|
|
24
|
+
wrap: number;
|
|
25
|
+
scrollX: number;
|
|
26
|
+
scrollY: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// -- Parallax manager state (one per camera) --
|
|
30
|
+
export interface ParallaxState {
|
|
31
|
+
layers: ParallaxLayer[];
|
|
32
|
+
layerCount: number;
|
|
33
|
+
activeCount: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// -- Options accepted by addParallaxLayer --
|
|
37
|
+
export interface ParallaxLayerOptions {
|
|
38
|
+
offsetX?: number;
|
|
39
|
+
offsetY?: number;
|
|
40
|
+
wrap?: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// -- Output shape for getLayerScroll --
|
|
44
|
+
export interface ScrollOut {
|
|
45
|
+
x: number;
|
|
46
|
+
y: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export declare function createParallaxState(): ParallaxState;
|
|
50
|
+
export declare function addParallaxLayer(
|
|
51
|
+
state: ParallaxState,
|
|
52
|
+
id: string,
|
|
53
|
+
speedX: number,
|
|
54
|
+
speedY?: number,
|
|
55
|
+
opts?: ParallaxLayerOptions,
|
|
56
|
+
): ParallaxLayer | null;
|
|
57
|
+
export declare function removeParallaxLayer(state: ParallaxState, id: string): void;
|
|
58
|
+
export declare function updateParallax(state: ParallaxState, camX: number, camY: number, zoom: number): void;
|
|
59
|
+
export declare function getLayerScroll(state: ParallaxState, id: string, out: ScrollOut): ScrollOut | null;
|
|
60
|
+
export declare function applyParallaxLayer(state: ParallaxState, id: string, ctx: CanvasRenderingContext2D): boolean;
|
package/src/ParallaxManager.js
CHANGED
|
@@ -191,7 +191,10 @@ export function applyParallaxLayer(state, id, ctx) {
|
|
|
191
191
|
for (let i = 0; i < state.layerCount; i++) {
|
|
192
192
|
const layer = state.layers[i];
|
|
193
193
|
if (layer.active && layer.id === id) {
|
|
194
|
-
|
|
194
|
+
// int snap is deliberate: kills texture shimmer. floor (not | 0) so
|
|
195
|
+
// the snap is uniform about the origin -- | 0 truncates toward zero
|
|
196
|
+
// (CP-13), matching the base camera and CinematicCameraPro.apply().
|
|
197
|
+
ctx.translate(-Math.floor(layer.scrollX), -Math.floor(layer.scrollY));
|
|
195
198
|
return true;
|
|
196
199
|
}
|
|
197
200
|
}
|
package/src/Shake.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zakkster/lite-camera-pro/shake -- TypeScript declarations.
|
|
3
|
+
*
|
|
4
|
+
* The ./shake subpath barrel: the noise-based shake engine (ShakeEngine.js)
|
|
5
|
+
* plus the built-in presets and registry (ShakePresets.js). Complete runtime
|
|
6
|
+
* surface, no `any`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// -- Shake profile (input) --
|
|
10
|
+
export interface ShakeProfile {
|
|
11
|
+
/** Initial trauma [0, 1]. Undefined defaults to 0.5 in addShake. */
|
|
12
|
+
trauma?: number;
|
|
13
|
+
/** Noise sample frequency (higher = more jittery). Default 15. */
|
|
14
|
+
freq?: number;
|
|
15
|
+
/** Trauma units lost per second. Default 1. */
|
|
16
|
+
decay?: number;
|
|
17
|
+
/** Maximum pixel offset at trauma=1. Default 15. */
|
|
18
|
+
maxOffset?: number;
|
|
19
|
+
/** Maximum rotation (radians) at trauma=1. Default 0.05. */
|
|
20
|
+
maxAngle?: number;
|
|
21
|
+
/** Directional X component; (0,0) = omnidirectional. Default 0. */
|
|
22
|
+
dirX?: number;
|
|
23
|
+
/** Directional Y component. Default 0. */
|
|
24
|
+
dirY?: number;
|
|
25
|
+
/** Per-profile intensity multiplier used by addShake. Default 1. */
|
|
26
|
+
intensity?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// -- A single pre-allocated shake slot --
|
|
30
|
+
export interface ShakeSlot {
|
|
31
|
+
active: boolean;
|
|
32
|
+
isDefault: boolean;
|
|
33
|
+
trauma: number;
|
|
34
|
+
decay: number;
|
|
35
|
+
freq: number;
|
|
36
|
+
time: number;
|
|
37
|
+
maxOffset: number;
|
|
38
|
+
maxAngle: number;
|
|
39
|
+
dirX: number;
|
|
40
|
+
dirY: number;
|
|
41
|
+
isDirectional: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// -- Shake engine state (one per camera) --
|
|
45
|
+
export interface ShakeState {
|
|
46
|
+
slots: ShakeSlot[];
|
|
47
|
+
slotCount: number;
|
|
48
|
+
seedOffset: number;
|
|
49
|
+
/** Computed X offset in pixels (read by apply()). */
|
|
50
|
+
offsetX: number;
|
|
51
|
+
/** Computed Y offset in pixels. */
|
|
52
|
+
offsetY: number;
|
|
53
|
+
/** Computed rotation in radians. */
|
|
54
|
+
angle: number;
|
|
55
|
+
/** Global shake scale (0 = no shake, 1 = normal). */
|
|
56
|
+
globalScale: number;
|
|
57
|
+
/** True when any slot is active. */
|
|
58
|
+
active: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// -- Shake engine functions (ShakeEngine.js) --
|
|
62
|
+
export declare function createShakeState(seedOffset?: number): ShakeState;
|
|
63
|
+
export declare function addShake(state: ShakeState, profile: ShakeProfile, intensity?: number): void;
|
|
64
|
+
export declare function addTraumaSimple(state: ShakeState, amount: number): void;
|
|
65
|
+
export declare function updateShake(state: ShakeState, dt: number): void;
|
|
66
|
+
export declare function computeShake(state: ShakeState): void;
|
|
67
|
+
export declare function clearShakes(state: ShakeState): void;
|
|
68
|
+
|
|
69
|
+
// -- Built-in presets (ShakePresets.js) --
|
|
70
|
+
export declare const EXPLOSION: Readonly<ShakeProfile>;
|
|
71
|
+
export declare const EARTHQUAKE: Readonly<ShakeProfile>;
|
|
72
|
+
export declare const RECOIL: Readonly<ShakeProfile>;
|
|
73
|
+
export declare const IMPACT: Readonly<ShakeProfile>;
|
|
74
|
+
export declare const LANDING: Readonly<ShakeProfile>;
|
|
75
|
+
export declare const DAMAGE: Readonly<ShakeProfile>;
|
|
76
|
+
export declare const RUMBLE: Readonly<ShakeProfile>;
|
|
77
|
+
export declare const HEAVY_IMPACT: Readonly<ShakeProfile>;
|
|
78
|
+
|
|
79
|
+
// -- Preset registry (ShakePresets.js) --
|
|
80
|
+
export declare function getPreset(name: string): Readonly<ShakeProfile> | null;
|
|
81
|
+
export declare function registerPreset(name: string, profile: ShakeProfile): void;
|
|
82
|
+
export declare function listPresets(): string[];
|
package/src/Shake.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// @zakkster/lite-camera-pro -- the ./shake subpath barrel.
|
|
2
|
+
// Re-export only; never a copy. The camera class and this subpath import the
|
|
3
|
+
// SAME module files, so createShakeState et al. have one runtime identity.
|
|
4
|
+
// ShakePresets is folded in so a shake-only consumer gets presets + getPreset
|
|
5
|
+
// from a single import.
|
|
6
|
+
export * from './ShakeEngine.js';
|
|
7
|
+
export * from './ShakePresets.js';
|
package/src/ShakeEngine.js
CHANGED
|
@@ -129,11 +129,24 @@ function acquireSlot(state) {
|
|
|
129
129
|
* @param {number} [profile.intensity=1] Scale multiplier for the profile
|
|
130
130
|
*/
|
|
131
131
|
export function addShake(state, profile, intensity = 1) {
|
|
132
|
+
// CP-14 + H-F (fail closed): validate trauma/intensity in this COLD entry so
|
|
133
|
+
// the per-frame updateShake/computeShake loops gain zero new branches.
|
|
134
|
+
// - trauma undefined -> 0.5 (documented default)
|
|
135
|
+
// - trauma or intensity non-finite (NaN/Inf) -> activate NOTHING, return
|
|
136
|
+
// early. The old `profile.trauma || 0.5` laundered NaN to 0.5, opening a
|
|
137
|
+
// poison door: a single NaN shake would drive every later frame to NaN.
|
|
138
|
+
// null is not zero; an unverified number does not get a default.
|
|
139
|
+
// - resulting trauma <= 0 -> inert (a zero-trauma shake fires nothing).
|
|
140
|
+
const rawTrauma = profile.trauma === undefined ? 0.5 : profile.trauma;
|
|
141
|
+
if (!Number.isFinite(rawTrauma) || !Number.isFinite(intensity)) return;
|
|
142
|
+
const trauma = Math.min(1, rawTrauma * intensity);
|
|
143
|
+
if (trauma <= 0) return;
|
|
144
|
+
|
|
132
145
|
const slot = acquireSlot(state);
|
|
133
146
|
|
|
134
147
|
slot.active = true;
|
|
135
148
|
slot.isDefault = false;
|
|
136
|
-
slot.trauma =
|
|
149
|
+
slot.trauma = trauma;
|
|
137
150
|
slot.decay = profile.decay !== undefined ? profile.decay : 1.0;
|
|
138
151
|
slot.freq = profile.freq !== undefined ? profile.freq : 15;
|
|
139
152
|
slot.maxOffset = profile.maxOffset !== undefined ? profile.maxOffset : 15;
|
|
@@ -166,6 +179,11 @@ export function addShake(state, profile, intensity = 1) {
|
|
|
166
179
|
* @param {number} amount Trauma to add [0, 1]
|
|
167
180
|
*/
|
|
168
181
|
export function addTraumaSimple(state, amount) {
|
|
182
|
+
// CP-14 + H-F (fail closed): a non-finite amount activates NOTHING and an
|
|
183
|
+
// amount <= 0 is inert. Same policy as addShake, in this cold entry only so
|
|
184
|
+
// the hot per-frame loops stay branch-for-branch unchanged.
|
|
185
|
+
if (!Number.isFinite(amount) || amount <= 0) return;
|
|
186
|
+
|
|
169
187
|
// Try to find an existing default omni slot to stack onto.
|
|
170
188
|
// Preset/profile slots are NEVER stacked onto — they have parameters
|
|
171
189
|
// (freq, decay, etc.) that addTrauma's generic shake wouldn't match.
|
package/src/index.d.ts
CHANGED
|
@@ -1,68 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @zakkster/lite-camera-pro
|
|
2
|
+
* @zakkster/lite-camera-pro -- TypeScript declarations (main entry).
|
|
3
|
+
*
|
|
4
|
+
* The full functional layer is re-exported from the per-subsystem sibling
|
|
5
|
+
* declarations (one declaration per type -- no duplicates). This file adds the
|
|
6
|
+
* CinematicCameraPro class, the debug-HUD facade, and the VERSION const.
|
|
3
7
|
*/
|
|
4
8
|
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
readonly REPEAT_X: 1;
|
|
22
|
-
readonly REPEAT_Y: 2;
|
|
23
|
-
readonly REPEAT_BOTH: 3;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
// ── Bounds Types ──
|
|
27
|
-
export declare const BoundsType: {
|
|
28
|
-
readonly HARD: 0;
|
|
29
|
-
readonly SOFT: 1;
|
|
30
|
-
readonly ELASTIC: 2;
|
|
31
|
-
readonly NONE: 3;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
// ── Shake Profile ──
|
|
35
|
-
export interface ShakeProfile {
|
|
36
|
-
trauma?: number;
|
|
37
|
-
freq?: number;
|
|
38
|
-
decay?: number;
|
|
39
|
-
maxOffset?: number;
|
|
40
|
-
maxAngle?: number;
|
|
41
|
-
dirX?: number;
|
|
42
|
-
dirY?: number;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// ── Shake Presets ──
|
|
46
|
-
export declare const EXPLOSION: Readonly<ShakeProfile>;
|
|
47
|
-
export declare const EARTHQUAKE: Readonly<ShakeProfile>;
|
|
48
|
-
export declare const RECOIL: Readonly<ShakeProfile>;
|
|
49
|
-
export declare const IMPACT: Readonly<ShakeProfile>;
|
|
50
|
-
export declare const LANDING: Readonly<ShakeProfile>;
|
|
51
|
-
export declare const DAMAGE: Readonly<ShakeProfile>;
|
|
52
|
-
export declare const RUMBLE: Readonly<ShakeProfile>;
|
|
53
|
-
export declare const HEAVY_IMPACT: Readonly<ShakeProfile>;
|
|
54
|
-
|
|
55
|
-
export declare function getPreset(name: string): ShakeProfile | null;
|
|
56
|
-
export declare function registerPreset(name: string, profile: ShakeProfile): void;
|
|
57
|
-
export declare function listPresets(): string[];
|
|
58
|
-
|
|
59
|
-
// ── Vec2 ──
|
|
60
|
-
export interface Vec2 {
|
|
61
|
-
x: number;
|
|
62
|
-
y: number;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ── Debug HUD Config ──
|
|
9
|
+
// -- Functional layer (mirrors the ./shake, ./parallax, ./bounds, ./multi,
|
|
10
|
+
// ./follow, ./sequence subpaths; same runtime identities) --
|
|
11
|
+
export * from './Shake.js';
|
|
12
|
+
export * from './ParallaxManager.js';
|
|
13
|
+
export * from './BoundsSystem.js';
|
|
14
|
+
export * from './MultiTarget.js';
|
|
15
|
+
export * from './FollowMode.js';
|
|
16
|
+
export * from './CameraSequence.js';
|
|
17
|
+
|
|
18
|
+
// -- Types needed locally by the class declaration --
|
|
19
|
+
import type { ShakeProfile } from './Shake.js';
|
|
20
|
+
import type { Vec2, MultiTargetOptions } from './MultiTarget.js';
|
|
21
|
+
import type { BoundsEdgesConfig } from './BoundsSystem.js';
|
|
22
|
+
import type { CameraSequence, CameraSequenceOptions } from './CameraSequence.js';
|
|
23
|
+
|
|
24
|
+
// -- Debug HUD (DebugHUD.js) -- facade only, no dedicated subpath --
|
|
66
25
|
export interface DebugHUDConfig {
|
|
67
26
|
show: {
|
|
68
27
|
position: boolean;
|
|
@@ -83,66 +42,14 @@ export declare function createDebugHUDConfig(): DebugHUDConfig;
|
|
|
83
42
|
export declare function drawDebugHUD(cam: CinematicCameraPro, ctx: CanvasRenderingContext2D, config?: DebugHUDConfig): void;
|
|
84
43
|
export declare function drawDebugWorld(cam: CinematicCameraPro, ctx: CanvasRenderingContext2D, config?: DebugHUDConfig): void;
|
|
85
44
|
|
|
86
|
-
//
|
|
87
|
-
export
|
|
88
|
-
loop?: boolean;
|
|
89
|
-
onComplete?: () => void;
|
|
90
|
-
blendOutTime?: number;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export interface StepOptions {
|
|
94
|
-
ease?: (t: number) => number;
|
|
95
|
-
at?: string | number;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export interface CameraSequence {
|
|
99
|
-
moveTo(x: number, y: number, duration: number, opts?: StepOptions): CameraSequence;
|
|
100
|
-
zoomTo(level: number, duration: number, opts?: StepOptions): CameraSequence;
|
|
101
|
-
moveAndZoom(x: number, y: number, level: number, duration: number, opts?: StepOptions): CameraSequence;
|
|
102
|
-
shake(profileOrName: string | ShakeProfile, intensity?: number, opts?: StepOptions): CameraSequence;
|
|
103
|
-
wait(duration: number, opts?: StepOptions): CameraSequence;
|
|
104
|
-
call(fn: () => void, opts?: StepOptions): CameraSequence;
|
|
105
|
-
play(): CameraSequence;
|
|
106
|
-
pause(): CameraSequence;
|
|
107
|
-
resume(): CameraSequence;
|
|
108
|
-
stop(): CameraSequence;
|
|
109
|
-
seek(timeMs: number): CameraSequence;
|
|
110
|
-
destroy(): void;
|
|
111
|
-
readonly duration: number;
|
|
112
|
-
readonly progress: number;
|
|
113
|
-
readonly playing: boolean;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export declare function createCameraSequence(cam: CinematicCameraPro, options?: CameraSequenceOptions): CameraSequence;
|
|
117
|
-
export declare function panTo(cam: CinematicCameraPro, x: number, y: number, duration: number, opts?: CameraSequenceOptions & StepOptions): CameraSequence;
|
|
118
|
-
export declare function dramaticZoom(cam: CinematicCameraPro, x: number, y: number, zoom: number, duration: number, opts?: CameraSequenceOptions & StepOptions): CameraSequence;
|
|
119
|
-
export declare function bossReveal(cam: CinematicCameraPro, x: number, y: number, totalMs?: number, opts?: CameraSequenceOptions): CameraSequence;
|
|
120
|
-
export declare function timedShake(cam: CinematicCameraPro, presetOrProfile: string | ShakeProfile, holdMs?: number, opts?: CameraSequenceOptions): CameraSequence;
|
|
121
|
-
|
|
122
|
-
// ── Multi-Target Options ──
|
|
123
|
-
export interface MultiTargetOptions {
|
|
124
|
-
paddingX?: number;
|
|
125
|
-
paddingY?: number;
|
|
126
|
-
padding?: number;
|
|
127
|
-
minZoom?: number;
|
|
128
|
-
maxZoom?: number;
|
|
129
|
-
zoomSpeed?: number;
|
|
130
|
-
followSpeed?: number;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// ── Bounds Config ──
|
|
134
|
-
export interface BoundsEdgesConfig {
|
|
135
|
-
left?: number;
|
|
136
|
-
right?: number;
|
|
137
|
-
top?: number;
|
|
138
|
-
bottom?: number;
|
|
139
|
-
}
|
|
45
|
+
// -- Package version (bumped in lockstep with package.json + llms.txt) --
|
|
46
|
+
export declare const VERSION: string;
|
|
140
47
|
|
|
141
|
-
//
|
|
48
|
+
// -- Main camera class --
|
|
142
49
|
export declare class CinematicCameraPro {
|
|
143
50
|
constructor(viewW: number, viewH: number, worldW: number, worldH: number, seed?: number);
|
|
144
51
|
|
|
145
|
-
//
|
|
52
|
+
// Public state
|
|
146
53
|
readonly pos: Float32Array;
|
|
147
54
|
readonly target: Float32Array;
|
|
148
55
|
readonly look: Float32Array;
|
|
@@ -170,58 +77,58 @@ export declare class CinematicCameraPro {
|
|
|
170
77
|
|
|
171
78
|
debugConfig: DebugHUDConfig;
|
|
172
79
|
|
|
173
|
-
//
|
|
80
|
+
// Follow mode
|
|
174
81
|
setMode(mode: number): this;
|
|
175
82
|
|
|
176
|
-
//
|
|
83
|
+
// Multi-target
|
|
177
84
|
trackMultiple(targets: Vec2[], options?: MultiTargetOptions): this;
|
|
178
85
|
trackSingle(): this;
|
|
179
86
|
setTargetCount(count: number): this;
|
|
180
87
|
|
|
181
|
-
//
|
|
88
|
+
// Shake
|
|
182
89
|
addTrauma(amount: number): this;
|
|
183
90
|
shake(profile: ShakeProfile, intensity?: number): this;
|
|
184
91
|
shakePreset(name: string, intensity?: number): this;
|
|
185
92
|
clearShakes(): this;
|
|
186
93
|
|
|
187
|
-
//
|
|
94
|
+
// Sequences
|
|
188
95
|
createSequence(options?: CameraSequenceOptions): CameraSequence;
|
|
189
96
|
playSequence(seq: CameraSequence): this;
|
|
190
97
|
stopSequence(): this;
|
|
191
98
|
readonly sequencePlaying: boolean;
|
|
192
99
|
|
|
193
|
-
//
|
|
100
|
+
// Zoom
|
|
194
101
|
setZoom(level: number, duration?: number, ease?: (t: number) => number): this;
|
|
195
102
|
zoomAt(targetOrX: number | Vec2, yOrLevel: number, levelOrDur?: number, duration?: number, ease?: (t: number) => number): this;
|
|
196
103
|
|
|
197
|
-
//
|
|
104
|
+
// Coordinate conversion
|
|
198
105
|
screenToWorld(sx: number, sy: number, out: Vec2): Vec2;
|
|
199
106
|
worldToScreen(wx: number, wy: number, out: Vec2): Vec2;
|
|
200
107
|
|
|
201
|
-
//
|
|
108
|
+
// Parallax
|
|
202
109
|
addParallaxLayer(id: string, speedX: number, speedY?: number, opts?: { offsetX?: number; offsetY?: number; wrap?: number }): this;
|
|
203
110
|
removeParallaxLayer(id: string): this;
|
|
204
111
|
applyParallax(id: string, ctx: CanvasRenderingContext2D): boolean;
|
|
205
112
|
|
|
206
|
-
//
|
|
113
|
+
// Bounds
|
|
207
114
|
setBoundsType(type: number): this;
|
|
208
115
|
setBoundsEdges(config: BoundsEdgesConfig): this;
|
|
209
116
|
setBoundsRect(x: number, y: number, w: number, h: number): this;
|
|
210
117
|
clearBoundsRect(): this;
|
|
211
118
|
|
|
212
|
-
//
|
|
119
|
+
// Core
|
|
213
120
|
update(dt: number, px: number, py: number, pvx?: number, pvy?: number): void;
|
|
214
121
|
apply(ctx: CanvasRenderingContext2D): void;
|
|
215
122
|
|
|
216
|
-
//
|
|
123
|
+
// Debug
|
|
217
124
|
debug(ctx: CanvasRenderingContext2D): void;
|
|
218
125
|
debugHUD(ctx: CanvasRenderingContext2D): void;
|
|
219
126
|
|
|
220
|
-
//
|
|
127
|
+
// Save / load
|
|
221
128
|
getState(): { posX: number; posY: number; targetX: number; targetY: number; zoom: number; mode: number };
|
|
222
129
|
setState(snapshot: { posX?: number; posY?: number; targetX?: number; targetY?: number; zoom?: number; mode?: number }): this;
|
|
223
130
|
|
|
224
|
-
//
|
|
131
|
+
// Lifecycle
|
|
225
132
|
destroy(): void;
|
|
226
133
|
}
|
|
227
134
|
|
package/src/index.js
CHANGED
|
@@ -2,32 +2,34 @@
|
|
|
2
2
|
* @zakkster/lite-camera-pro
|
|
3
3
|
* Commercial cinematic camera system for Canvas2D games.
|
|
4
4
|
*
|
|
5
|
-
* Built on: lite-camera, lite-lerp, lite-ease, lite-
|
|
5
|
+
* Built on: lite-camera, lite-lerp, lite-ease, lite-noise, lite-timeline
|
|
6
6
|
* Zero external dependencies outside the @zakkster ecosystem.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
export const VERSION = "1.1.0";
|
|
10
|
+
|
|
11
|
+
// -- Core --
|
|
10
12
|
export { CinematicCameraPro } from './CinematicCameraPro.js';
|
|
11
13
|
export { default } from './CinematicCameraPro.js';
|
|
12
14
|
|
|
13
|
-
//
|
|
15
|
+
// -- Follow Modes --
|
|
14
16
|
export { FollowMode, FOLLOW_STRATEGIES } from './FollowMode.js';
|
|
15
17
|
|
|
16
|
-
//
|
|
17
|
-
export { updateMultiTarget } from './MultiTarget.js';
|
|
18
|
+
// -- Multi-Target --
|
|
19
|
+
export { createMultiTargetState, updateMultiTarget } from './MultiTarget.js';
|
|
18
20
|
|
|
19
|
-
//
|
|
20
|
-
export { addShake, addTraumaSimple, updateShake, computeShake, clearShakes } from './ShakeEngine.js';
|
|
21
|
+
// -- Shake Engine --
|
|
22
|
+
export { createShakeState, addShake, addTraumaSimple, updateShake, computeShake, clearShakes } from './ShakeEngine.js';
|
|
21
23
|
export { EXPLOSION, EARTHQUAKE, RECOIL, IMPACT, LANDING, DAMAGE, RUMBLE, HEAVY_IMPACT, getPreset, registerPreset, listPresets } from './ShakePresets.js';
|
|
22
24
|
|
|
23
|
-
//
|
|
25
|
+
// -- Sequences --
|
|
24
26
|
export { createCameraSequence, panTo, dramaticZoom, bossReveal, timedShake } from './CameraSequence.js';
|
|
25
27
|
|
|
26
|
-
//
|
|
28
|
+
// -- Parallax --
|
|
27
29
|
export { WrapMode, createParallaxState, addParallaxLayer, removeParallaxLayer, updateParallax, getLayerScroll, applyParallaxLayer } from './ParallaxManager.js';
|
|
28
30
|
|
|
29
|
-
//
|
|
31
|
+
// -- Bounds --
|
|
30
32
|
export { BoundsType, createBoundsState, setBoundsAll, setBoundsEdges, setBoundsRect, clearBoundsRect, applyBounds } from './BoundsSystem.js';
|
|
31
33
|
|
|
32
|
-
//
|
|
34
|
+
// -- Debug HUD --
|
|
33
35
|
export { createDebugHUDConfig, drawDebugHUD, drawDebugWorld } from './DebugHUD.js';
|