@react-shimeji/core 0.2.1 → 0.2.3

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