@blorkfield/overlay-core 0.4.3 → 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 +106 -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 +106 -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();
|
|
@@ -1498,6 +1501,15 @@ var OverlayScene = class {
|
|
|
1498
1501
|
if (!entry.originalPosition) return;
|
|
1499
1502
|
const opacity = entry.shadow?.opacity ?? 0.3;
|
|
1500
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
|
+
}
|
|
1501
1513
|
if (entry.ttfGlyph) {
|
|
1502
1514
|
const body = import_matter_js5.default.Bodies.circle(entry.originalPosition.x, entry.originalPosition.y, 1, {
|
|
1503
1515
|
isStatic: true,
|
|
@@ -1971,6 +1983,84 @@ var OverlayScene = class {
|
|
|
1971
1983
|
areFontsInitialized() {
|
|
1972
1984
|
return this.fontsInitialized;
|
|
1973
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
|
+
}
|
|
1974
2064
|
// ==================== TEXT OBSTACLE METHODS ====================
|
|
1975
2065
|
/**
|
|
1976
2066
|
* Create text obstacles from a string. Each character becomes an individual obstacle
|
|
@@ -2467,6 +2557,22 @@ var OverlayScene = class {
|
|
|
2467
2557
|
ctx.restore();
|
|
2468
2558
|
}
|
|
2469
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
|
+
}
|
|
2470
2576
|
checkTTLExpiration() {
|
|
2471
2577
|
const now = performance.now();
|
|
2472
2578
|
const expiredObjects = [];
|