@blorkfield/overlay-core 0.4.2 → 0.5.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/dist/index.cjs +113 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -1
- package/dist/index.d.ts +54 -1
- package/dist/index.js +113 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1204,6 +1204,9 @@ var OverlayScene = class {
|
|
|
1204
1204
|
if (this.config.wrapHorizontal && entry.tags.includes("falling")) {
|
|
1205
1205
|
wrapHorizontal(entry.body, this.config.bounds);
|
|
1206
1206
|
}
|
|
1207
|
+
if (entry.domElement && entry.tags.includes("falling")) {
|
|
1208
|
+
this.updateDOMElementTransform(entry);
|
|
1209
|
+
}
|
|
1207
1210
|
}
|
|
1208
1211
|
if (!this.config.debug) {
|
|
1209
1212
|
this.drawTTFGlyphs();
|
|
@@ -1238,6 +1241,13 @@ var OverlayScene = class {
|
|
|
1238
1241
|
}
|
|
1239
1242
|
});
|
|
1240
1243
|
import_matter_js5.default.Composite.add(this.engine.world, this.mouseConstraint);
|
|
1244
|
+
const wheelHandler = this.mouse.mousewheel;
|
|
1245
|
+
if (wheelHandler) {
|
|
1246
|
+
canvas.removeEventListener("mousewheel", wheelHandler);
|
|
1247
|
+
canvas.removeEventListener("DOMMouseScroll", wheelHandler);
|
|
1248
|
+
canvas.removeEventListener("wheel", wheelHandler);
|
|
1249
|
+
}
|
|
1250
|
+
canvas.style.touchAction = "pan-x pan-y";
|
|
1241
1251
|
import_matter_js5.default.Events.on(this.mouseConstraint, "startdrag", this.handleStartDrag);
|
|
1242
1252
|
canvas.addEventListener("click", this.handleCanvasClick);
|
|
1243
1253
|
this.render.mouse = this.mouse;
|
|
@@ -1491,6 +1501,15 @@ var OverlayScene = class {
|
|
|
1491
1501
|
if (!entry.originalPosition) return;
|
|
1492
1502
|
const opacity = entry.shadow?.opacity ?? 0.3;
|
|
1493
1503
|
const shadowId = `shadow-${entry.id}`;
|
|
1504
|
+
if (entry.domElement) {
|
|
1505
|
+
const shadowElement = entry.domElement.cloneNode(true);
|
|
1506
|
+
shadowElement.style.opacity = String(opacity);
|
|
1507
|
+
shadowElement.style.pointerEvents = "none";
|
|
1508
|
+
shadowElement.style.transform = entry.domOriginalTransform || "";
|
|
1509
|
+
entry.domElement.parentNode?.insertBefore(shadowElement, entry.domElement);
|
|
1510
|
+
entry.domShadowElement = shadowElement;
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1494
1513
|
if (entry.ttfGlyph) {
|
|
1495
1514
|
const body = import_matter_js5.default.Bodies.circle(entry.originalPosition.x, entry.originalPosition.y, 1, {
|
|
1496
1515
|
isStatic: true,
|
|
@@ -1964,6 +1983,84 @@ var OverlayScene = class {
|
|
|
1964
1983
|
areFontsInitialized() {
|
|
1965
1984
|
return this.fontsInitialized;
|
|
1966
1985
|
}
|
|
1986
|
+
// ==================== DOM OBSTACLE METHODS ====================
|
|
1987
|
+
/**
|
|
1988
|
+
* Attach a DOM element to physics. The element will follow the physics body
|
|
1989
|
+
* and can have pressure threshold, shadow, and click-to-fall behavior.
|
|
1990
|
+
*
|
|
1991
|
+
* When the element collapses (becomes dynamic), its CSS transform will be
|
|
1992
|
+
* updated each frame to match the physics body position and rotation.
|
|
1993
|
+
*
|
|
1994
|
+
* Shadow creates a cloned DOM element that stays at the original position.
|
|
1995
|
+
*/
|
|
1996
|
+
addDOMObstacle(config) {
|
|
1997
|
+
const { element, x, y } = config;
|
|
1998
|
+
const width = config.width ?? element.offsetWidth;
|
|
1999
|
+
const height = config.height ?? element.offsetHeight;
|
|
2000
|
+
const tags = config.tags ?? [];
|
|
2001
|
+
const isStatic = !tags.includes("falling");
|
|
2002
|
+
const body = import_matter_js5.default.Bodies.rectangle(x, y, width, height, {
|
|
2003
|
+
isStatic,
|
|
2004
|
+
label: `dom-${crypto.randomUUID().slice(0, 8)}`,
|
|
2005
|
+
render: { visible: false }
|
|
2006
|
+
// Don't render the body, DOM element is the visual
|
|
2007
|
+
});
|
|
2008
|
+
const id = body.label;
|
|
2009
|
+
let pressureThreshold;
|
|
2010
|
+
if (config.pressureThreshold) {
|
|
2011
|
+
pressureThreshold = typeof config.pressureThreshold.value === "number" ? config.pressureThreshold.value : config.pressureThreshold.value[0];
|
|
2012
|
+
}
|
|
2013
|
+
const shadow = config.shadow ? { opacity: config.shadow.opacity ?? 0.3 } : void 0;
|
|
2014
|
+
const clicksRemaining = config.clickToFall?.clicks;
|
|
2015
|
+
const originalTransform = element.style.transform || "";
|
|
2016
|
+
element.style.position = "absolute";
|
|
2017
|
+
element.style.transformOrigin = "center center";
|
|
2018
|
+
const entry = {
|
|
2019
|
+
id,
|
|
2020
|
+
body,
|
|
2021
|
+
tags,
|
|
2022
|
+
spawnTime: performance.now(),
|
|
2023
|
+
pressureThreshold,
|
|
2024
|
+
weight: config.weight ?? 1,
|
|
2025
|
+
shadow,
|
|
2026
|
+
originalPosition: shadow || clicksRemaining !== void 0 ? { x, y } : void 0,
|
|
2027
|
+
clicksRemaining,
|
|
2028
|
+
domElement: element,
|
|
2029
|
+
domOriginalTransform: originalTransform
|
|
2030
|
+
};
|
|
2031
|
+
this.objects.set(id, entry);
|
|
2032
|
+
import_matter_js5.default.Composite.add(this.engine.world, body);
|
|
2033
|
+
if (isStatic && pressureThreshold !== void 0) {
|
|
2034
|
+
this.obstaclePressure.set(id, /* @__PURE__ */ new Set());
|
|
2035
|
+
}
|
|
2036
|
+
if (clicksRemaining !== void 0) {
|
|
2037
|
+
const clickHandler = () => {
|
|
2038
|
+
const currentEntry = this.objects.get(id);
|
|
2039
|
+
if (!currentEntry) return;
|
|
2040
|
+
if (currentEntry.tags.includes("falling")) return;
|
|
2041
|
+
if (currentEntry.clicksRemaining === void 0) return;
|
|
2042
|
+
currentEntry.clicksRemaining--;
|
|
2043
|
+
logger.debug("OverlayScene", `Click on DOM element: ${currentEntry.clicksRemaining} clicks remaining`);
|
|
2044
|
+
if (currentEntry.clicksRemaining <= 0) {
|
|
2045
|
+
this.collapseObstacle(currentEntry);
|
|
2046
|
+
element.removeEventListener("click", clickHandler);
|
|
2047
|
+
}
|
|
2048
|
+
};
|
|
2049
|
+
element.addEventListener("click", clickHandler);
|
|
2050
|
+
}
|
|
2051
|
+
return {
|
|
2052
|
+
id,
|
|
2053
|
+
shadowElement: null
|
|
2054
|
+
// Will be populated on collapse
|
|
2055
|
+
};
|
|
2056
|
+
}
|
|
2057
|
+
/**
|
|
2058
|
+
* Get the shadow element for a DOM obstacle (available after collapse).
|
|
2059
|
+
*/
|
|
2060
|
+
getDOMObstacleShadow(id) {
|
|
2061
|
+
const entry = this.objects.get(id);
|
|
2062
|
+
return entry?.domShadowElement ?? null;
|
|
2063
|
+
}
|
|
1967
2064
|
// ==================== TEXT OBSTACLE METHODS ====================
|
|
1968
2065
|
/**
|
|
1969
2066
|
* Create text obstacles from a string. Each character becomes an individual obstacle
|
|
@@ -2460,6 +2557,22 @@ var OverlayScene = class {
|
|
|
2460
2557
|
ctx.restore();
|
|
2461
2558
|
}
|
|
2462
2559
|
}
|
|
2560
|
+
/**
|
|
2561
|
+
* Update a DOM element's CSS transform to match its physics body position and rotation.
|
|
2562
|
+
*/
|
|
2563
|
+
updateDOMElementTransform(entry) {
|
|
2564
|
+
if (!entry.domElement) return;
|
|
2565
|
+
const body = entry.body;
|
|
2566
|
+
const x = body.position.x;
|
|
2567
|
+
const y = body.position.y;
|
|
2568
|
+
const angle = body.angle;
|
|
2569
|
+
const angleDeg = angle * (180 / Math.PI);
|
|
2570
|
+
const width = entry.domElement.offsetWidth;
|
|
2571
|
+
const height = entry.domElement.offsetHeight;
|
|
2572
|
+
entry.domElement.style.left = `${x - width / 2}px`;
|
|
2573
|
+
entry.domElement.style.top = `${y - height / 2}px`;
|
|
2574
|
+
entry.domElement.style.transform = `rotate(${angleDeg}deg)`;
|
|
2575
|
+
}
|
|
2463
2576
|
checkTTLExpiration() {
|
|
2464
2577
|
const now = performance.now();
|
|
2465
2578
|
const expiredObjects = [];
|