@blorkfield/overlay-core 0.8.6 → 0.8.8
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 +169 -5
- package/dist/index.cjs +46 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -4
- package/dist/index.d.ts +13 -4
- package/dist/index.js +46 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ Objects don't have fixed types. Instead, their behavior is determined by string
|
|
|
27
27
|
| Tag | Behavior |
|
|
28
28
|
|-----|----------|
|
|
29
29
|
| `falling` | Object is dynamic and affected by gravity |
|
|
30
|
-
| `
|
|
30
|
+
| `window_follow` | Object follows mouse position when grounded |
|
|
31
31
|
| `grabable` | Object can be dragged via mouse constraint |
|
|
32
32
|
|
|
33
33
|
Without the `falling` tag, objects are static and won't move.
|
|
@@ -153,6 +153,11 @@ When a DOM element collapses:
|
|
|
153
153
|
- The element's CSS transform is updated each frame to follow physics
|
|
154
154
|
- Shadow creates a cloned DOM element at the original position
|
|
155
155
|
|
|
156
|
+
```typescript
|
|
157
|
+
// Get the shadow element after collapse (if shadow was configured)
|
|
158
|
+
const shadowEl = scene.getDOMObstacleShadow(id);
|
|
159
|
+
```
|
|
160
|
+
|
|
156
161
|
### Pressure, Shadow, and Click Behavior
|
|
157
162
|
|
|
158
163
|
These options work on any spawned object (shapes, images, or DOM elements):
|
|
@@ -225,6 +230,25 @@ const result = await scene.addTTFTextObstacles({
|
|
|
225
230
|
});
|
|
226
231
|
```
|
|
227
232
|
|
|
233
|
+
### Managing Text Obstacles
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
// Spawn text that immediately falls (already has 'falling' tag)
|
|
237
|
+
const result = await scene.spawnFallingTextObstacles(config);
|
|
238
|
+
const result = await scene.spawnFallingTTFTextObstacles(config);
|
|
239
|
+
|
|
240
|
+
// Release text obstacles (add 'falling' tag)
|
|
241
|
+
scene.releaseTextObstacles(wordTag);
|
|
242
|
+
|
|
243
|
+
// Release letters one by one with delay
|
|
244
|
+
await scene.releaseTextObstaclesSequentially(wordTag, 100); // 100ms delay
|
|
245
|
+
await scene.releaseTextObstaclesSequentially(wordTag, 100, true); // reverse order
|
|
246
|
+
|
|
247
|
+
// Get debug info for letter positioning
|
|
248
|
+
const debugInfo = scene.getLetterDebugInfo(wordTag);
|
|
249
|
+
const allDebug = scene.getAllLetterDebugInfo();
|
|
250
|
+
```
|
|
251
|
+
|
|
228
252
|
## Effects
|
|
229
253
|
|
|
230
254
|
Effects are persistent spawning mechanisms that create objects over time.
|
|
@@ -294,22 +318,45 @@ scene.setEffect({
|
|
|
294
318
|
```typescript
|
|
295
319
|
// Release objects (make them fall)
|
|
296
320
|
scene.releaseObject(id);
|
|
321
|
+
scene.releaseObjects([id1, id2]);
|
|
297
322
|
scene.releaseObjectsByTag('my-text');
|
|
298
323
|
scene.releaseAllObjects();
|
|
299
324
|
|
|
300
325
|
// Remove objects
|
|
301
326
|
scene.removeObject(id);
|
|
327
|
+
scene.removeObjects([id1, id2]);
|
|
302
328
|
scene.removeObjectsByTag('welcome-text');
|
|
303
329
|
scene.removeAllObjects();
|
|
330
|
+
scene.removeAll(); // Alias for removeAllObjects
|
|
331
|
+
scene.removeAllByTag('tag'); // Alias for removeObjectsByTag
|
|
304
332
|
|
|
305
333
|
// Add or remove tags
|
|
306
334
|
scene.addTag(id, 'falling');
|
|
335
|
+
scene.addFallingTag(id); // Convenience for adding 'falling' tag
|
|
307
336
|
scene.removeTag(id, 'grabable');
|
|
308
337
|
|
|
309
338
|
// Get object info
|
|
310
339
|
const ids = scene.getObjectIds();
|
|
311
340
|
const tagged = scene.getObjectIdsByTag('falling');
|
|
312
341
|
const allTags = scene.getAllTags();
|
|
342
|
+
|
|
343
|
+
// Get full object state
|
|
344
|
+
const obj = scene.getObject(id); // Returns ObjectState or null
|
|
345
|
+
const objs = scene.getObjectsByTag('tag'); // Returns ObjectState[]
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Physics Manipulation
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
// Apply force to objects
|
|
352
|
+
scene.applyForce(id, { x: 0.01, y: -0.02 });
|
|
353
|
+
scene.applyForceToTag('falling', { x: 0.005, y: 0 });
|
|
354
|
+
|
|
355
|
+
// Set velocity directly
|
|
356
|
+
scene.setVelocity(id, { x: 5, y: -10 });
|
|
357
|
+
|
|
358
|
+
// Set position directly
|
|
359
|
+
scene.setPosition(id, { x: 100, y: 200 });
|
|
313
360
|
```
|
|
314
361
|
|
|
315
362
|
## Mouse Position and Grab API
|
|
@@ -352,9 +399,11 @@ const currentGrab = scene.getGrabbedObject(); // Returns ID or null
|
|
|
352
399
|
|
|
353
400
|
| Method | Returns | Description |
|
|
354
401
|
|--------|---------|-------------|
|
|
355
|
-
| `setFollowTarget(
|
|
402
|
+
| `setFollowTarget(key, x, y)` | void | Set a follow target position (e.g., 'mouse' for grab/follow behavior) |
|
|
403
|
+
| `removeFollowTarget(key)` | void | Remove a follow target |
|
|
404
|
+
| `getFollowTargetKeys()` | string[] | Get all active follow target keys |
|
|
356
405
|
| `startGrab()` | string \| null | Link entity at current mouse position to mouse, returns entity ID |
|
|
357
|
-
| `endGrab()` | void | Unlink currently grabbed entity |
|
|
406
|
+
| `endGrab()` | void | Unlink currently grabbed entity (applies release velocity) |
|
|
358
407
|
| `getGrabbedObject()` | string \| null | Get ID of currently grabbed entity |
|
|
359
408
|
|
|
360
409
|
## Configuration
|
|
@@ -393,6 +442,34 @@ const scene = new OverlayScene(canvas, {
|
|
|
393
442
|
| `floorConfig.segmentWidths` | none | Proportional widths for each segment (array that sums to 1.0) |
|
|
394
443
|
| `despawnBelowFloor` | 1.0 | Distance below floor to despawn objects (as fraction of height) |
|
|
395
444
|
|
|
445
|
+
### Background Configuration
|
|
446
|
+
|
|
447
|
+
The `background` option supports multiple formats:
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
// Simple color
|
|
451
|
+
background: '#16213e'
|
|
452
|
+
background: 'transparent'
|
|
453
|
+
|
|
454
|
+
// Full configuration with layers
|
|
455
|
+
background: {
|
|
456
|
+
color: '#16213e', // Base color layer
|
|
457
|
+
image: {
|
|
458
|
+
url: '/images/bg.png',
|
|
459
|
+
sizing: 'cover' // 'stretch' | 'center' | 'tile' | 'cover' | 'contain'
|
|
460
|
+
},
|
|
461
|
+
transparency: {
|
|
462
|
+
mode: 'checkerboard', // Visual indicator for transparent areas
|
|
463
|
+
color1: '#ffffff',
|
|
464
|
+
color2: '#cccccc',
|
|
465
|
+
size: 10
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Change background at runtime
|
|
470
|
+
await scene.setBackground({ color: '#000000' });
|
|
471
|
+
```
|
|
472
|
+
|
|
396
473
|
## Pressure Tracking
|
|
397
474
|
|
|
398
475
|
```typescript
|
|
@@ -409,7 +486,11 @@ const allPressure = scene.getAllPressure();
|
|
|
409
486
|
const summary = scene.getPressureSummary();
|
|
410
487
|
```
|
|
411
488
|
|
|
412
|
-
##
|
|
489
|
+
## Callbacks and Events
|
|
490
|
+
|
|
491
|
+
### Update Callback
|
|
492
|
+
|
|
493
|
+
Called every frame with all dynamic object states:
|
|
413
494
|
|
|
414
495
|
```typescript
|
|
415
496
|
scene.onUpdate((data) => {
|
|
@@ -420,6 +501,30 @@ scene.onUpdate((data) => {
|
|
|
420
501
|
});
|
|
421
502
|
```
|
|
422
503
|
|
|
504
|
+
### Lifecycle Events
|
|
505
|
+
|
|
506
|
+
Subscribe to object lifecycle events:
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
// Object spawned
|
|
510
|
+
scene.on('objectSpawned', (obj) => {
|
|
511
|
+
console.log(`Spawned: ${obj.id}`, obj.x, obj.y);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
// Object removed
|
|
515
|
+
scene.on('objectRemoved', (obj) => {
|
|
516
|
+
console.log(`Removed: ${obj.id}`);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
// Objects collided
|
|
520
|
+
scene.on('objectCollision', (a, b) => {
|
|
521
|
+
console.log(`Collision: ${a.id} hit ${b.id}`);
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
// Unsubscribe
|
|
525
|
+
scene.off('objectSpawned', myCallback);
|
|
526
|
+
```
|
|
527
|
+
|
|
423
528
|
## Font Setup
|
|
424
529
|
|
|
425
530
|
### Bundled Fonts
|
|
@@ -496,6 +601,22 @@ Add the font file and reference it in the manifest:
|
|
|
496
601
|
}
|
|
497
602
|
```
|
|
498
603
|
|
|
604
|
+
### Font API
|
|
605
|
+
|
|
606
|
+
```typescript
|
|
607
|
+
// Initialize fonts from a directory
|
|
608
|
+
await scene.initializeFonts('/fonts/');
|
|
609
|
+
|
|
610
|
+
// Check initialization status
|
|
611
|
+
if (scene.areFontsInitialized()) {
|
|
612
|
+
// Get available fonts
|
|
613
|
+
const fonts = scene.getAvailableFonts(); // Returns FontInfo[]
|
|
614
|
+
const font = scene.getFontByName('Roboto'); // Returns FontInfo | undefined
|
|
615
|
+
const font = scene.getFontByIndex(0); // Returns FontInfo | undefined
|
|
616
|
+
const defaultFont = scene.getDefaultFont(); // Returns first font or undefined
|
|
617
|
+
}
|
|
618
|
+
```
|
|
619
|
+
|
|
499
620
|
## Logging
|
|
500
621
|
|
|
501
622
|
```typescript
|
|
@@ -536,18 +657,61 @@ The package is written in TypeScript and ships with full type definitions. All c
|
|
|
536
657
|
|
|
537
658
|
```typescript
|
|
538
659
|
import type {
|
|
660
|
+
// Scene configuration
|
|
539
661
|
OverlaySceneConfig,
|
|
662
|
+
Bounds,
|
|
663
|
+
ContainerOptions,
|
|
664
|
+
FloorConfig,
|
|
665
|
+
|
|
666
|
+
// Object types
|
|
540
667
|
ObjectConfig,
|
|
668
|
+
DynamicObject,
|
|
669
|
+
ObjectState,
|
|
670
|
+
ShapeConfig,
|
|
671
|
+
ShapePreset,
|
|
672
|
+
DespawnEffectConfig,
|
|
673
|
+
|
|
674
|
+
// Text obstacle types
|
|
541
675
|
TextObstacleConfig,
|
|
676
|
+
TextObstacleResult,
|
|
542
677
|
TTFTextObstacleConfig,
|
|
678
|
+
TextAlign,
|
|
679
|
+
TextBounds,
|
|
680
|
+
|
|
681
|
+
// Effect types
|
|
543
682
|
EffectConfig,
|
|
683
|
+
EffectType,
|
|
684
|
+
EffectObjectConfig,
|
|
685
|
+
BaseEffectConfig,
|
|
544
686
|
BurstEffectConfig,
|
|
545
687
|
RainEffectConfig,
|
|
546
688
|
StreamEffectConfig,
|
|
689
|
+
|
|
690
|
+
// Pressure, weight, shadow, click types
|
|
547
691
|
PressureThresholdConfig,
|
|
548
692
|
WeightConfig,
|
|
549
693
|
ShadowConfig,
|
|
550
694
|
ClickToFallConfig,
|
|
551
|
-
|
|
695
|
+
|
|
696
|
+
// Background types
|
|
697
|
+
BackgroundConfig,
|
|
698
|
+
BackgroundImageConfig,
|
|
699
|
+
BackgroundImageSizing,
|
|
700
|
+
BackgroundTransparencyConfig,
|
|
701
|
+
|
|
702
|
+
// Font types
|
|
703
|
+
FontInfo,
|
|
704
|
+
FontManifest,
|
|
705
|
+
LoadedFont,
|
|
706
|
+
GlyphData,
|
|
707
|
+
|
|
708
|
+
// Lifecycle types
|
|
709
|
+
LifecycleEvent,
|
|
710
|
+
LifecycleCallback,
|
|
711
|
+
UpdateCallback,
|
|
712
|
+
UpdateCallbackData,
|
|
713
|
+
|
|
714
|
+
// Logging
|
|
715
|
+
LogLevel
|
|
552
716
|
} from '@blorkfield/overlay-core';
|
|
553
717
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -1442,9 +1442,30 @@ var OverlayScene = class {
|
|
|
1442
1442
|
};
|
|
1443
1443
|
// Follow targets for follow-{key} tagged objects
|
|
1444
1444
|
this.followTargets = /* @__PURE__ */ new Map();
|
|
1445
|
-
// Delta-based grab tracking
|
|
1445
|
+
// Delta-based grab tracking
|
|
1446
1446
|
this.grabbedObjectId = null;
|
|
1447
1447
|
this.lastGrabMousePosition = null;
|
|
1448
|
+
this.grabbedWasDynamic = false;
|
|
1449
|
+
this.grabVelocity = { x: 0, y: 0 };
|
|
1450
|
+
/** Handle mouse down - start grab via programmatic API */
|
|
1451
|
+
this.handleMouseDown = (event) => {
|
|
1452
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
1453
|
+
const x = event.clientX - rect.left;
|
|
1454
|
+
const y = event.clientY - rect.top;
|
|
1455
|
+
this.followTargets.set("mouse", { x, y });
|
|
1456
|
+
this.startGrab();
|
|
1457
|
+
};
|
|
1458
|
+
/** Handle mouse move - update follow target for grabbed entity */
|
|
1459
|
+
this.handleMouseMove = (event) => {
|
|
1460
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
1461
|
+
const x = event.clientX - rect.left;
|
|
1462
|
+
const y = event.clientY - rect.top;
|
|
1463
|
+
this.followTargets.set("mouse", { x, y });
|
|
1464
|
+
};
|
|
1465
|
+
/** Handle mouse up - release grab */
|
|
1466
|
+
this.handleMouseUp = () => {
|
|
1467
|
+
this.endGrab();
|
|
1468
|
+
};
|
|
1448
1469
|
/** Handle canvas clicks for click-to-fall behavior */
|
|
1449
1470
|
this.handleCanvasClick = (event) => {
|
|
1450
1471
|
const rect = this.canvas.getBoundingClientRect();
|
|
@@ -1518,6 +1539,7 @@ var OverlayScene = class {
|
|
|
1518
1539
|
if (dx !== 0 || dy !== 0) {
|
|
1519
1540
|
import_matter_js5.default.Body.translate(entry.body, { x: dx, y: dy });
|
|
1520
1541
|
}
|
|
1542
|
+
this.grabVelocity = { x: dx * 0.5 + this.grabVelocity.x * 0.5, y: dy * 0.5 + this.grabVelocity.y * 0.5 };
|
|
1521
1543
|
this.lastGrabMousePosition = { x: mouseTarget.x, y: mouseTarget.y };
|
|
1522
1544
|
}
|
|
1523
1545
|
}
|
|
@@ -1525,7 +1547,7 @@ var OverlayScene = class {
|
|
|
1525
1547
|
const isDragging = this.grabbedObjectId === entry.id;
|
|
1526
1548
|
if (!isDragging) {
|
|
1527
1549
|
for (const tag of entry.tags) {
|
|
1528
|
-
const key = tag === "
|
|
1550
|
+
const key = tag === "window_follow" ? "mouse" : tag.startsWith("follow-") ? tag.slice(7) : null;
|
|
1529
1551
|
if (key) {
|
|
1530
1552
|
const target = this.followTargets.get(key);
|
|
1531
1553
|
if (target) {
|
|
@@ -1570,6 +1592,9 @@ var OverlayScene = class {
|
|
|
1570
1592
|
import_matter_js5.default.Composite.add(this.engine.world, this.boundaries);
|
|
1571
1593
|
this.checkInitialFloorIntegrity();
|
|
1572
1594
|
this.mouse = import_matter_js5.default.Mouse.create(canvas);
|
|
1595
|
+
canvas.addEventListener("mousedown", this.handleMouseDown);
|
|
1596
|
+
canvas.addEventListener("mousemove", this.handleMouseMove);
|
|
1597
|
+
canvas.addEventListener("mouseup", this.handleMouseUp);
|
|
1573
1598
|
canvas.addEventListener("click", this.handleCanvasClick);
|
|
1574
1599
|
this.effectManager = new EffectManager(
|
|
1575
1600
|
this.config.bounds,
|
|
@@ -1992,6 +2017,9 @@ var OverlayScene = class {
|
|
|
1992
2017
|
}
|
|
1993
2018
|
destroy() {
|
|
1994
2019
|
this.stop();
|
|
2020
|
+
this.canvas.removeEventListener("mousedown", this.handleMouseDown);
|
|
2021
|
+
this.canvas.removeEventListener("mousemove", this.handleMouseMove);
|
|
2022
|
+
this.canvas.removeEventListener("mouseup", this.handleMouseUp);
|
|
1995
2023
|
this.canvas.removeEventListener("click", this.handleCanvasClick);
|
|
1996
2024
|
import_matter_js5.default.Events.off(this.render, "beforeRender", this.handleBeforeRender);
|
|
1997
2025
|
import_matter_js5.default.Events.off(this.render, "afterRender", this.handleAfterRender);
|
|
@@ -2054,8 +2082,8 @@ var OverlayScene = class {
|
|
|
2054
2082
|
* Spawn an object synchronously.
|
|
2055
2083
|
* Object behavior is determined by tags:
|
|
2056
2084
|
* - 'falling': Object is dynamic (affected by gravity)
|
|
2057
|
-
* - '
|
|
2058
|
-
* - 'grabable': Object can be
|
|
2085
|
+
* - 'window_follow': Object follows mouse when grounded (walks toward mouse)
|
|
2086
|
+
* - 'grabable': Object can be grabbed and moved with mouse
|
|
2059
2087
|
* Without 'falling' tag, object is static.
|
|
2060
2088
|
*/
|
|
2061
2089
|
spawnObject(config) {
|
|
@@ -2355,6 +2383,9 @@ var OverlayScene = class {
|
|
|
2355
2383
|
if (entry && entry.tags.includes("grabable")) {
|
|
2356
2384
|
this.grabbedObjectId = entry.id;
|
|
2357
2385
|
this.lastGrabMousePosition = { x: position.x, y: position.y };
|
|
2386
|
+
this.grabVelocity = { x: 0, y: 0 };
|
|
2387
|
+
this.grabbedWasDynamic = !entry.body.isStatic;
|
|
2388
|
+
import_matter_js5.default.Body.setStatic(entry.body, true);
|
|
2358
2389
|
return entry.id;
|
|
2359
2390
|
}
|
|
2360
2391
|
}
|
|
@@ -2364,8 +2395,19 @@ var OverlayScene = class {
|
|
|
2364
2395
|
* Release any currently grabbed object.
|
|
2365
2396
|
*/
|
|
2366
2397
|
endGrab() {
|
|
2398
|
+
if (this.grabbedObjectId && this.grabbedWasDynamic) {
|
|
2399
|
+
const entry = this.objects.get(this.grabbedObjectId);
|
|
2400
|
+
if (entry) {
|
|
2401
|
+
import_matter_js5.default.Body.setStatic(entry.body, false);
|
|
2402
|
+
import_matter_js5.default.Sleeping.set(entry.body, false);
|
|
2403
|
+
import_matter_js5.default.Body.setVelocity(entry.body, this.grabVelocity);
|
|
2404
|
+
import_matter_js5.default.Body.setAngularVelocity(entry.body, 0);
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2367
2407
|
this.grabbedObjectId = null;
|
|
2368
2408
|
this.lastGrabMousePosition = null;
|
|
2409
|
+
this.grabbedWasDynamic = false;
|
|
2410
|
+
this.grabVelocity = { x: 0, y: 0 };
|
|
2369
2411
|
}
|
|
2370
2412
|
/**
|
|
2371
2413
|
* Get the ID of the currently grabbed object.
|