@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.d.cts CHANGED
@@ -219,7 +219,7 @@ interface ShimejiEngineOptions {
219
219
  gravity?: number;
220
220
  /** Maximum elapsed time applied to one animation update. Defaults to 100 ms. */
221
221
  maxDeltaTime?: number;
222
- /** CSS class added to the generated work area. */
222
+ /** @deprecated There is no shared work area; retained for source compatibility. */
223
223
  workAreaClassName?: string;
224
224
  /** CSS class added to each generated mascot element. */
225
225
  mascotClassName?: string;
@@ -326,8 +326,6 @@ declare class ShimejiEngine {
326
326
  private readonly disposers;
327
327
  private readonly intervals;
328
328
  private readonly options;
329
- private readonly originalContainerPosition;
330
- private readonly adjustedContainerPosition;
331
329
  private pointer;
332
330
  private animationFrame;
333
331
  private lastFrameTime;
@@ -464,7 +462,7 @@ interface PlatformRectangle extends Rectangle {
464
462
  }
465
463
  /** Resolves a platform option into DOM elements, excluding engine-owned nodes. */
466
464
  declare function resolvePlatformElements(source: string | readonly HTMLElement[], document: Document, excludedRoot?: HTMLElement): HTMLElement[];
467
- /** Reads platform bounds once and converts viewport coordinates to work-area coordinates. */
465
+ /** Reads platform bounds once and converts viewport coordinates to the supplied origin. */
468
466
  declare function readPlatformRectangles(elements: readonly HTMLElement[], workAreaRectangle: Pick<DOMRect, "left" | "top">): PlatformRectangle[];
469
467
 
470
468
  /** A resolved image and optional atlas crop for one sprite frame. */
@@ -496,9 +494,9 @@ declare function isIndividualSprite(sprite: SpriteRectangle | IndividualSprite |
496
494
 
497
495
  /** DOM nodes and resources owned by one mascot. */
498
496
  interface MascotDomHandle {
499
- /** Pointer-interactive mascot wrapper. */
497
+ /** Fixed, pointer-transparent mascot wrapper. */
500
498
  element: HTMLDivElement;
501
- /** Child element on which sprite images are painted. */
499
+ /** Pointer-interactive child element on which sprite images are painted. */
502
500
  spriteElement: HTMLDivElement;
503
501
  /** Per-mascot spritesheet URL lease. */
504
502
  spriteLease: SpriteLease;
package/dist/index.d.ts CHANGED
@@ -219,7 +219,7 @@ interface ShimejiEngineOptions {
219
219
  gravity?: number;
220
220
  /** Maximum elapsed time applied to one animation update. Defaults to 100 ms. */
221
221
  maxDeltaTime?: number;
222
- /** CSS class added to the generated work area. */
222
+ /** @deprecated There is no shared work area; retained for source compatibility. */
223
223
  workAreaClassName?: string;
224
224
  /** CSS class added to each generated mascot element. */
225
225
  mascotClassName?: string;
@@ -326,8 +326,6 @@ declare class ShimejiEngine {
326
326
  private readonly disposers;
327
327
  private readonly intervals;
328
328
  private readonly options;
329
- private readonly originalContainerPosition;
330
- private readonly adjustedContainerPosition;
331
329
  private pointer;
332
330
  private animationFrame;
333
331
  private lastFrameTime;
@@ -464,7 +462,7 @@ interface PlatformRectangle extends Rectangle {
464
462
  }
465
463
  /** Resolves a platform option into DOM elements, excluding engine-owned nodes. */
466
464
  declare function resolvePlatformElements(source: string | readonly HTMLElement[], document: Document, excludedRoot?: HTMLElement): HTMLElement[];
467
- /** Reads platform bounds once and converts viewport coordinates to work-area coordinates. */
465
+ /** Reads platform bounds once and converts viewport coordinates to the supplied origin. */
468
466
  declare function readPlatformRectangles(elements: readonly HTMLElement[], workAreaRectangle: Pick<DOMRect, "left" | "top">): PlatformRectangle[];
469
467
 
470
468
  /** A resolved image and optional atlas crop for one sprite frame. */
@@ -496,9 +494,9 @@ declare function isIndividualSprite(sprite: SpriteRectangle | IndividualSprite |
496
494
 
497
495
  /** DOM nodes and resources owned by one mascot. */
498
496
  interface MascotDomHandle {
499
- /** Pointer-interactive mascot wrapper. */
497
+ /** Fixed, pointer-transparent mascot wrapper. */
500
498
  element: HTMLDivElement;
501
- /** Child element on which sprite images are painted. */
499
+ /** Pointer-interactive child element on which sprite images are painted. */
502
500
  spriteElement: HTMLDivElement;
503
501
  /** Per-mascot spritesheet URL lease. */
504
502
  spriteLease: SpriteLease;
package/dist/index.js CHANGED
@@ -1,41 +1,24 @@
1
1
  // src/dom.ts
2
2
  var DomManager = class {
3
- /** Creates an isolated work area inside the supplied host element. */
4
- constructor(container, sprites, workAreaClassName) {
3
+ /** Uses the supplied element only as a mount point for independent mascots. */
4
+ constructor(container, sprites) {
5
5
  this.container = container;
6
6
  this.sprites = sprites;
7
- const workArea = document.createElement("div");
8
- workArea.dataset.reactShimejiWorkArea = "true";
9
- if (workAreaClassName) workArea.className = workAreaClassName;
10
- Object.assign(workArea.style, {
11
- position: "absolute",
12
- inset: "0",
13
- width: "100%",
14
- height: "100%",
15
- overflow: "hidden",
16
- pointerEvents: "none",
17
- zIndex: "2147483643"
18
- });
19
- this.workArea = workArea;
20
- this.ensureMounted();
21
7
  }
22
8
  container;
23
9
  sprites;
24
- /** Generated element that contains all mascots. */
25
- workArea;
26
- /** Reattaches the work area if application code temporarily removed it. */
10
+ handles = /* @__PURE__ */ new Set();
11
+ /** Reattaches mascot elements if application code temporarily removed them. */
27
12
  ensureMounted() {
28
- if (this.workArea.parentElement !== this.container) this.container.appendChild(this.workArea);
13
+ const target = this.container.ownerDocument.body;
14
+ for (const handle of this.handles) {
15
+ if (handle.element.parentElement !== target) target.appendChild(handle.element);
16
+ }
29
17
  }
30
- /** Returns the current local work-area rectangle. */
18
+ /** Returns viewport bounds because fixed mascots use viewport coordinates. */
31
19
  getBounds() {
32
- return this.getBoundsFromClientRectangle(this.workArea.getBoundingClientRect());
33
- }
34
- /** Converts a previously-read work-area client rectangle into local bounds. */
35
- getBoundsFromClientRectangle(rectangle) {
36
- const width = rectangle.width || this.container.clientWidth || window.innerWidth;
37
- const height = rectangle.height || this.container.clientHeight || window.innerHeight;
38
- return { x: 0, y: 0, width, height };
20
+ const view = this.container.ownerDocument.defaultView ?? window;
21
+ return { x: 0, y: 0, width: view.innerWidth, height: view.innerHeight };
39
22
  }
40
23
  /** Creates a mascot node and acquires its spritesheet resource. */
41
24
  createMascot(spec, mascotId, mascotClassName) {
@@ -43,12 +26,14 @@ var DomManager = class {
43
26
  const element = document.createElement("div");
44
27
  element.dataset.shimejiId = mascotId;
45
28
  if (mascotClassName) element.className = mascotClassName;
46
- Object.assign(element.style, { position: "absolute", left: "0", top: "0", width: "0", height: "0", pointerEvents: "auto", touchAction: "none", userSelect: "none", willChange: "transform" });
29
+ Object.assign(element.style, { position: "fixed", left: "0", top: "0", width: "0", height: "0", pointerEvents: "none", zIndex: "9999", userSelect: "none", willChange: "transform" });
47
30
  const spriteElement = document.createElement("div");
48
- Object.assign(spriteElement.style, { position: "absolute", left: "0", top: "0", backgroundRepeat: "no-repeat", transformOrigin: "center center", pointerEvents: "none" });
31
+ Object.assign(spriteElement.style, { position: "absolute", left: "0", top: "0", backgroundRepeat: "no-repeat", transformOrigin: "center center", pointerEvents: "auto", touchAction: "none", userSelect: "none" });
49
32
  element.appendChild(spriteElement);
50
- this.workArea.appendChild(element);
51
- return { element, spriteElement, spriteLease };
33
+ const handle = { element, spriteElement, spriteLease };
34
+ this.handles.add(handle);
35
+ this.container.ownerDocument.body.appendChild(element);
36
+ return handle;
52
37
  }
53
38
  /** Paints one mascot state into its existing DOM nodes. */
54
39
  render(handle, spec, state) {
@@ -80,12 +65,18 @@ var DomManager = class {
80
65
  }
81
66
  /** Removes one mascot node and releases its temporary image URL. */
82
67
  removeMascot(handle) {
68
+ if (!this.handles.delete(handle)) return;
83
69
  handle.element.remove();
84
70
  handle.spriteLease.release();
85
71
  }
86
- /** Removes the work area owned by this manager. */
72
+ /** Returns whether an element belongs to one of this manager's mascots. */
73
+ owns(element) {
74
+ for (const handle of this.handles) if (handle.element === element || handle.element.contains(element)) return true;
75
+ return false;
76
+ }
77
+ /** Removes every mascot element and releases its temporary image URL. */
87
78
  destroy() {
88
- this.workArea.remove();
79
+ for (const handle of [...this.handles]) this.removeMascot(handle);
89
80
  }
90
81
  };
91
82
 
@@ -1102,7 +1093,7 @@ var Mascot = class {
1102
1093
  return nearby;
1103
1094
  }
1104
1095
  installPointerHandlers() {
1105
- const element = this.domHandle.element;
1096
+ const element = this.domHandle.spriteElement;
1106
1097
  const listen = (target, type, listener) => {
1107
1098
  target.addEventListener(type, listener);
1108
1099
  this.disposers.push(() => target.removeEventListener(type, listener));
@@ -1111,8 +1102,7 @@ var Mascot = class {
1111
1102
  const pointerEvent = event;
1112
1103
  if (pointerEvent.button !== 0) return;
1113
1104
  event.preventDefault();
1114
- const bounds = this.dom.workArea.getBoundingClientRect();
1115
- const point = { x: pointerEvent.clientX - bounds.left, y: pointerEvent.clientY - bounds.top };
1105
+ const point = { x: pointerEvent.clientX, y: pointerEvent.clientY };
1116
1106
  this.pointerId = pointerEvent.pointerId;
1117
1107
  this.pointerDown = point;
1118
1108
  this.lastPointer = point;
@@ -1126,8 +1116,7 @@ var Mascot = class {
1126
1116
  listen(document, "pointermove", (event) => {
1127
1117
  const pointerEvent = event;
1128
1118
  if (!this.state.dragging || pointerEvent.pointerId !== this.pointerId) return;
1129
- const bounds = this.dom.workArea.getBoundingClientRect();
1130
- const point = { x: pointerEvent.clientX - bounds.left, y: pointerEvent.clientY - bounds.top };
1119
+ const point = { x: pointerEvent.clientX, y: pointerEvent.clientY };
1131
1120
  const previous = this.lastPointer ?? point;
1132
1121
  this.state.vx = (point.x - previous.x) * 0.8;
1133
1122
  this.state.vy = (point.y - previous.y) * 0.8;
@@ -1275,10 +1264,7 @@ var ShimejiEngine = class {
1275
1264
  if (!container) throw new TypeError("ShimejiEngine requires a container element");
1276
1265
  this.options = { ...defaults, ...options, random: options.random };
1277
1266
  this.platformSource = this.options.platforms;
1278
- this.originalContainerPosition = container.style.position;
1279
- this.adjustedContainerPosition = getComputedStyle(container).position === "static";
1280
- if (this.adjustedContainerPosition) container.style.position = "relative";
1281
- this.dom = new DomManager(container, this.sprites, this.options.workAreaClassName || void 0);
1267
+ this.dom = new DomManager(container, this.sprites);
1282
1268
  this.initialize();
1283
1269
  }
1284
1270
  container;
@@ -1290,8 +1276,6 @@ var ShimejiEngine = class {
1290
1276
  disposers = [];
1291
1277
  intervals = /* @__PURE__ */ new Set();
1292
1278
  options;
1293
- originalContainerPosition;
1294
- adjustedContainerPosition;
1295
1279
  pointer = { x: 0, y: 0, dx: 0, dy: 0 };
1296
1280
  animationFrame;
1297
1281
  lastFrameTime;
@@ -1306,9 +1290,8 @@ var ShimejiEngine = class {
1306
1290
  this.initialized = true;
1307
1291
  this.listen(document, "pointermove", (event) => {
1308
1292
  const pointerEvent = event;
1309
- const rectangle = this.dom.workArea.getBoundingClientRect();
1310
- const x = pointerEvent.clientX - rectangle.left;
1311
- const y = pointerEvent.clientY - rectangle.top;
1293
+ const x = pointerEvent.clientX;
1294
+ const y = pointerEvent.clientY;
1312
1295
  this.pointer = { x, y, dx: x - this.pointer.x, dy: y - this.pointer.y };
1313
1296
  });
1314
1297
  this.listen(window, "resize", () => this.renderAll());
@@ -1412,7 +1395,6 @@ var ShimejiEngine = class {
1412
1395
  this.sprites.destroy();
1413
1396
  this.specs.clear();
1414
1397
  this.events.clear();
1415
- if (this.adjustedContainerPosition && this.container.style.position === "relative") this.container.style.position = this.originalContainerPosition;
1416
1398
  this.destroyed = true;
1417
1399
  this.initialized = false;
1418
1400
  }
@@ -1435,10 +1417,9 @@ var ShimejiEngine = class {
1435
1417
  for (const mascot of this.mascots.values()) mascot.tick(0, bounds, platforms);
1436
1418
  }
1437
1419
  readFrameGeometry() {
1438
- const workAreaRectangle = this.dom.workArea.getBoundingClientRect();
1439
- const bounds = this.dom.getBoundsFromClientRectangle(workAreaRectangle);
1440
- const elements = resolvePlatformElements(this.platformSource, this.container.ownerDocument, this.dom.workArea);
1441
- return { bounds, platforms: readPlatformRectangles(elements, workAreaRectangle) };
1420
+ const bounds = this.dom.getBounds();
1421
+ const elements = resolvePlatformElements(this.platformSource, this.container.ownerDocument).filter((element) => !this.dom.owns(element));
1422
+ return { bounds, platforms: readPlatformRectangles(elements, { left: 0, top: 0 }) };
1442
1423
  }
1443
1424
  emitState() {
1444
1425
  this.events.emit("statechange", this.getState());