@react-shimeji/core 0.2.1 → 0.2.2

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 CHANGED
@@ -47,42 +47,24 @@ module.exports = __toCommonJS(index_exports);
47
47
 
48
48
  // src/dom.ts
49
49
  var DomManager = class {
50
- /** Creates an isolated work area inside the supplied host element. */
51
- constructor(container, sprites, workAreaClassName) {
50
+ /** Uses the supplied element only as a mount point for independent mascots. */
51
+ constructor(container, sprites) {
52
52
  this.container = container;
53
53
  this.sprites = sprites;
54
- const workArea = document.createElement("div");
55
- workArea.dataset.reactShimejiWorkArea = "true";
56
- if (workAreaClassName) workArea.className = workAreaClassName;
57
- Object.assign(workArea.style, {
58
- position: "absolute",
59
- inset: "0",
60
- width: "100%",
61
- height: "100%",
62
- overflow: "hidden",
63
- pointerEvents: "none",
64
- zIndex: "2147483643"
65
- });
66
- this.workArea = workArea;
67
- this.ensureMounted();
68
54
  }
69
55
  container;
70
56
  sprites;
71
- /** Generated element that contains all mascots. */
72
- workArea;
73
- /** Reattaches the work area if application code temporarily removed it. */
57
+ handles = /* @__PURE__ */ new Set();
58
+ /** Reattaches mascot elements if application code temporarily removed them. */
74
59
  ensureMounted() {
75
- if (this.workArea.parentElement !== this.container) this.container.appendChild(this.workArea);
60
+ for (const handle of this.handles) {
61
+ if (handle.element.parentElement !== this.container) this.container.appendChild(handle.element);
62
+ }
76
63
  }
77
- /** Returns the current local work-area rectangle. */
64
+ /** Returns viewport bounds because fixed mascots use viewport coordinates. */
78
65
  getBounds() {
79
- return this.getBoundsFromClientRectangle(this.workArea.getBoundingClientRect());
80
- }
81
- /** Converts a previously-read work-area client rectangle into local bounds. */
82
- getBoundsFromClientRectangle(rectangle) {
83
- const width = rectangle.width || this.container.clientWidth || window.innerWidth;
84
- const height = rectangle.height || this.container.clientHeight || window.innerHeight;
85
- return { x: 0, y: 0, width, height };
66
+ const view = this.container.ownerDocument.defaultView ?? window;
67
+ return { x: 0, y: 0, width: view.innerWidth, height: view.innerHeight };
86
68
  }
87
69
  /** Creates a mascot node and acquires its spritesheet resource. */
88
70
  createMascot(spec, mascotId, mascotClassName) {
@@ -90,12 +72,14 @@ var DomManager = class {
90
72
  const element = document.createElement("div");
91
73
  element.dataset.shimejiId = mascotId;
92
74
  if (mascotClassName) element.className = mascotClassName;
93
- Object.assign(element.style, { position: "absolute", left: "0", top: "0", width: "0", height: "0", pointerEvents: "auto", touchAction: "none", userSelect: "none", willChange: "transform" });
75
+ Object.assign(element.style, { position: "fixed", left: "0", top: "0", width: "0", height: "0", pointerEvents: "none", zIndex: "9999", userSelect: "none", willChange: "transform" });
94
76
  const spriteElement = document.createElement("div");
95
- Object.assign(spriteElement.style, { position: "absolute", left: "0", top: "0", backgroundRepeat: "no-repeat", transformOrigin: "center center", pointerEvents: "none" });
77
+ Object.assign(spriteElement.style, { position: "absolute", left: "0", top: "0", backgroundRepeat: "no-repeat", transformOrigin: "center center", pointerEvents: "auto", touchAction: "none", userSelect: "none" });
96
78
  element.appendChild(spriteElement);
97
- this.workArea.appendChild(element);
98
- return { element, spriteElement, spriteLease };
79
+ const handle = { element, spriteElement, spriteLease };
80
+ this.handles.add(handle);
81
+ this.container.appendChild(element);
82
+ return handle;
99
83
  }
100
84
  /** Paints one mascot state into its existing DOM nodes. */
101
85
  render(handle, spec, state) {
@@ -127,12 +111,18 @@ var DomManager = class {
127
111
  }
128
112
  /** Removes one mascot node and releases its temporary image URL. */
129
113
  removeMascot(handle) {
114
+ if (!this.handles.delete(handle)) return;
130
115
  handle.element.remove();
131
116
  handle.spriteLease.release();
132
117
  }
133
- /** Removes the work area owned by this manager. */
118
+ /** Returns whether an element belongs to one of this manager's mascots. */
119
+ owns(element) {
120
+ for (const handle of this.handles) if (handle.element === element || handle.element.contains(element)) return true;
121
+ return false;
122
+ }
123
+ /** Removes every mascot element and releases its temporary image URL. */
134
124
  destroy() {
135
- this.workArea.remove();
125
+ for (const handle of [...this.handles]) this.removeMascot(handle);
136
126
  }
137
127
  };
138
128
 
@@ -1149,7 +1139,7 @@ var Mascot = class {
1149
1139
  return nearby;
1150
1140
  }
1151
1141
  installPointerHandlers() {
1152
- const element = this.domHandle.element;
1142
+ const element = this.domHandle.spriteElement;
1153
1143
  const listen = (target, type, listener) => {
1154
1144
  target.addEventListener(type, listener);
1155
1145
  this.disposers.push(() => target.removeEventListener(type, listener));
@@ -1158,8 +1148,7 @@ var Mascot = class {
1158
1148
  const pointerEvent = event;
1159
1149
  if (pointerEvent.button !== 0) return;
1160
1150
  event.preventDefault();
1161
- const bounds = this.dom.workArea.getBoundingClientRect();
1162
- const point = { x: pointerEvent.clientX - bounds.left, y: pointerEvent.clientY - bounds.top };
1151
+ const point = { x: pointerEvent.clientX, y: pointerEvent.clientY };
1163
1152
  this.pointerId = pointerEvent.pointerId;
1164
1153
  this.pointerDown = point;
1165
1154
  this.lastPointer = point;
@@ -1173,8 +1162,7 @@ var Mascot = class {
1173
1162
  listen(document, "pointermove", (event) => {
1174
1163
  const pointerEvent = event;
1175
1164
  if (!this.state.dragging || pointerEvent.pointerId !== this.pointerId) return;
1176
- const bounds = this.dom.workArea.getBoundingClientRect();
1177
- const point = { x: pointerEvent.clientX - bounds.left, y: pointerEvent.clientY - bounds.top };
1165
+ const point = { x: pointerEvent.clientX, y: pointerEvent.clientY };
1178
1166
  const previous = this.lastPointer ?? point;
1179
1167
  this.state.vx = (point.x - previous.x) * 0.8;
1180
1168
  this.state.vy = (point.y - previous.y) * 0.8;
@@ -1322,10 +1310,7 @@ var ShimejiEngine = class {
1322
1310
  if (!container) throw new TypeError("ShimejiEngine requires a container element");
1323
1311
  this.options = { ...defaults, ...options, random: options.random };
1324
1312
  this.platformSource = this.options.platforms;
1325
- this.originalContainerPosition = container.style.position;
1326
- this.adjustedContainerPosition = getComputedStyle(container).position === "static";
1327
- if (this.adjustedContainerPosition) container.style.position = "relative";
1328
- this.dom = new DomManager(container, this.sprites, this.options.workAreaClassName || void 0);
1313
+ this.dom = new DomManager(container, this.sprites);
1329
1314
  this.initialize();
1330
1315
  }
1331
1316
  container;
@@ -1337,8 +1322,6 @@ var ShimejiEngine = class {
1337
1322
  disposers = [];
1338
1323
  intervals = /* @__PURE__ */ new Set();
1339
1324
  options;
1340
- originalContainerPosition;
1341
- adjustedContainerPosition;
1342
1325
  pointer = { x: 0, y: 0, dx: 0, dy: 0 };
1343
1326
  animationFrame;
1344
1327
  lastFrameTime;
@@ -1353,9 +1336,8 @@ var ShimejiEngine = class {
1353
1336
  this.initialized = true;
1354
1337
  this.listen(document, "pointermove", (event) => {
1355
1338
  const pointerEvent = event;
1356
- const rectangle = this.dom.workArea.getBoundingClientRect();
1357
- const x = pointerEvent.clientX - rectangle.left;
1358
- const y = pointerEvent.clientY - rectangle.top;
1339
+ const x = pointerEvent.clientX;
1340
+ const y = pointerEvent.clientY;
1359
1341
  this.pointer = { x, y, dx: x - this.pointer.x, dy: y - this.pointer.y };
1360
1342
  });
1361
1343
  this.listen(window, "resize", () => this.renderAll());
@@ -1459,7 +1441,6 @@ var ShimejiEngine = class {
1459
1441
  this.sprites.destroy();
1460
1442
  this.specs.clear();
1461
1443
  this.events.clear();
1462
- if (this.adjustedContainerPosition && this.container.style.position === "relative") this.container.style.position = this.originalContainerPosition;
1463
1444
  this.destroyed = true;
1464
1445
  this.initialized = false;
1465
1446
  }
@@ -1482,10 +1463,9 @@ var ShimejiEngine = class {
1482
1463
  for (const mascot of this.mascots.values()) mascot.tick(0, bounds, platforms);
1483
1464
  }
1484
1465
  readFrameGeometry() {
1485
- const workAreaRectangle = this.dom.workArea.getBoundingClientRect();
1486
- const bounds = this.dom.getBoundsFromClientRectangle(workAreaRectangle);
1487
- const elements = resolvePlatformElements(this.platformSource, this.container.ownerDocument, this.dom.workArea);
1488
- return { bounds, platforms: readPlatformRectangles(elements, workAreaRectangle) };
1466
+ const bounds = this.dom.getBounds();
1467
+ const elements = resolvePlatformElements(this.platformSource, this.container.ownerDocument).filter((element) => !this.dom.owns(element));
1468
+ return { bounds, platforms: readPlatformRectangles(elements, { left: 0, top: 0 }) };
1489
1469
  }
1490
1470
  emitState() {
1491
1471
  this.events.emit("statechange", this.getState());