minimojs 1.0.0-alpha.25 → 1.0.0-alpha.27
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/internal/InputSystem.js +51 -17
- package/dist/minimo-cards.d.ts +4 -0
- package/dist/minimo-cards.js +16 -6
- package/dist/minimo.js +9 -0
- package/package.json +1 -1
|
@@ -29,6 +29,16 @@ export class InputSystem {
|
|
|
29
29
|
this._touchPointers = new Map();
|
|
30
30
|
/** @internal */
|
|
31
31
|
this._primaryTouchId = null;
|
|
32
|
+
/**
|
|
33
|
+
* The primary touch after it lifted, kept until the mouse or a new touch
|
|
34
|
+
* takes over, so the pointer stays where the finger left the screen instead
|
|
35
|
+
* of jumping to the mouse's last position.
|
|
36
|
+
*
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
this._releasedTouch = null;
|
|
40
|
+
/** @internal True on the frame the primary touch ended in a `touchcancel`. */
|
|
41
|
+
this._pointerCancelled = false;
|
|
32
42
|
}
|
|
33
43
|
get pointerX() {
|
|
34
44
|
return this._pointerX;
|
|
@@ -52,16 +62,19 @@ export class InputSystem {
|
|
|
52
62
|
this._mouseY = p.y;
|
|
53
63
|
this._mouseDown = true;
|
|
54
64
|
this._mousePressed = true;
|
|
65
|
+
this._releasedTouch = null;
|
|
55
66
|
this.syncPrimaryPointer();
|
|
56
67
|
});
|
|
57
68
|
this.canvas.addEventListener("mouseup", () => {
|
|
58
69
|
this._mouseDown = false;
|
|
70
|
+
this._releasedTouch = null;
|
|
59
71
|
this.syncPrimaryPointer();
|
|
60
72
|
});
|
|
61
73
|
this.canvas.addEventListener("mousemove", (e) => {
|
|
62
74
|
const p = this.getCanvasPoint(e.clientX, e.clientY);
|
|
63
75
|
this._mouseX = p.x;
|
|
64
76
|
this._mouseY = p.y;
|
|
77
|
+
this._releasedTouch = null;
|
|
65
78
|
this.syncPrimaryPointer();
|
|
66
79
|
});
|
|
67
80
|
this.canvas.addEventListener("touchstart", (e) => {
|
|
@@ -76,27 +89,17 @@ export class InputSystem {
|
|
|
76
89
|
this._primaryTouchId = touch.identifier;
|
|
77
90
|
}
|
|
78
91
|
}
|
|
92
|
+
this._releasedTouch = null;
|
|
93
|
+
this._pointerCancelled = false;
|
|
79
94
|
this.syncPrimaryPointer();
|
|
80
95
|
e.preventDefault();
|
|
81
96
|
}, { passive: false });
|
|
82
97
|
this.canvas.addEventListener("touchend", (e) => {
|
|
83
|
-
|
|
84
|
-
this._touchPointers.delete(touch.identifier);
|
|
85
|
-
if (this._primaryTouchId === touch.identifier) {
|
|
86
|
-
this._primaryTouchId = null;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
this.syncPrimaryPointer();
|
|
98
|
+
this.endTouches(e, false);
|
|
90
99
|
e.preventDefault();
|
|
91
100
|
}, { passive: false });
|
|
92
101
|
this.canvas.addEventListener("touchcancel", (e) => {
|
|
93
|
-
|
|
94
|
-
this._touchPointers.delete(touch.identifier);
|
|
95
|
-
if (this._primaryTouchId === touch.identifier) {
|
|
96
|
-
this._primaryTouchId = null;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
this.syncPrimaryPointer();
|
|
102
|
+
this.endTouches(e, true);
|
|
100
103
|
e.preventDefault();
|
|
101
104
|
}, { passive: false });
|
|
102
105
|
this.canvas.addEventListener("touchmove", (e) => {
|
|
@@ -131,6 +134,9 @@ export class InputSystem {
|
|
|
131
134
|
isPointerPressed() {
|
|
132
135
|
return this._pointerPressed;
|
|
133
136
|
}
|
|
137
|
+
isPointerCancelled() {
|
|
138
|
+
return this._pointerCancelled;
|
|
139
|
+
}
|
|
134
140
|
isPointerDownOverSprite(sprite) {
|
|
135
141
|
if (!sprite)
|
|
136
142
|
return false;
|
|
@@ -185,11 +191,38 @@ export class InputSystem {
|
|
|
185
191
|
for (const pointer of this._touchPointers.values()) {
|
|
186
192
|
pointer.pressed = false;
|
|
187
193
|
}
|
|
194
|
+
if (this._releasedTouch)
|
|
195
|
+
this._releasedTouch.pressed = false;
|
|
196
|
+
this._pointerCancelled = false;
|
|
188
197
|
this.syncPrimaryPointer();
|
|
189
198
|
}
|
|
190
199
|
resetPressedState() {
|
|
191
200
|
this.clearFramePressedState();
|
|
192
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* Removes the touches an event ended. When the primary one lifts, it is kept
|
|
204
|
+
* as the released touch at the point it lifted, carrying its frame-press flag
|
|
205
|
+
* so a tap that starts and ends between two frames still reads as pressed.
|
|
206
|
+
*
|
|
207
|
+
* @internal
|
|
208
|
+
*/
|
|
209
|
+
endTouches(e, cancelled) {
|
|
210
|
+
for (const touch of Array.from(e.changedTouches)) {
|
|
211
|
+
const pointer = this._touchPointers.get(touch.identifier);
|
|
212
|
+
this._touchPointers.delete(touch.identifier);
|
|
213
|
+
if (this._primaryTouchId !== touch.identifier || !pointer)
|
|
214
|
+
continue;
|
|
215
|
+
this._primaryTouchId = null;
|
|
216
|
+
if (!cancelled) {
|
|
217
|
+
const p = this.getCanvasPoint(touch.clientX, touch.clientY);
|
|
218
|
+
pointer.x = p.x;
|
|
219
|
+
pointer.y = p.y;
|
|
220
|
+
}
|
|
221
|
+
this._releasedTouch = pointer;
|
|
222
|
+
this._pointerCancelled = cancelled;
|
|
223
|
+
}
|
|
224
|
+
this.syncPrimaryPointer();
|
|
225
|
+
}
|
|
193
226
|
/** @internal */
|
|
194
227
|
getCanvasPoint(clientX, clientY) {
|
|
195
228
|
const rect = this.canvas.getBoundingClientRect();
|
|
@@ -257,9 +290,10 @@ export class InputSystem {
|
|
|
257
290
|
}
|
|
258
291
|
}
|
|
259
292
|
this._primaryTouchId = null;
|
|
260
|
-
|
|
261
|
-
this.
|
|
293
|
+
const released = this._releasedTouch;
|
|
294
|
+
this._pointerX = released ? released.x : this._mouseX;
|
|
295
|
+
this._pointerY = released ? released.y : this._mouseY;
|
|
262
296
|
this._pointerDown = this._mouseDown;
|
|
263
|
-
this._pointerPressed = this._mousePressed;
|
|
297
|
+
this._pointerPressed = this._mousePressed || (released?.pressed ?? false);
|
|
264
298
|
}
|
|
265
299
|
}
|
package/dist/minimo-cards.d.ts
CHANGED
|
@@ -242,6 +242,10 @@ export declare class CardPile {
|
|
|
242
242
|
*
|
|
243
243
|
* Call {@link update} once per frame.
|
|
244
244
|
*
|
|
245
|
+
* Piles and cards live in world space and scroll with the game. The table
|
|
246
|
+
* reads the pointer itself and adds `game.scrollX` and `game.scrollY`, so a
|
|
247
|
+
* scrolled board needs no conversion from the game.
|
|
248
|
+
*
|
|
245
249
|
* @example
|
|
246
250
|
* ```ts
|
|
247
251
|
* const table = new CardTable(game, {
|
package/dist/minimo-cards.js
CHANGED
|
@@ -187,6 +187,10 @@ let nextTableId = 1;
|
|
|
187
187
|
*
|
|
188
188
|
* Call {@link update} once per frame.
|
|
189
189
|
*
|
|
190
|
+
* Piles and cards live in world space and scroll with the game. The table
|
|
191
|
+
* reads the pointer itself and adds `game.scrollX` and `game.scrollY`, so a
|
|
192
|
+
* scrolled board needs no conversion from the game.
|
|
193
|
+
*
|
|
190
194
|
* @example
|
|
191
195
|
* ```ts
|
|
192
196
|
* const table = new CardTable(game, {
|
|
@@ -638,12 +642,15 @@ export class CardTable {
|
|
|
638
642
|
this.cancelDrag();
|
|
639
643
|
return;
|
|
640
644
|
}
|
|
645
|
+
// The pointer is in screen space and the piles and cards are in world space.
|
|
641
646
|
const x = game.pointerX;
|
|
642
647
|
const y = game.pointerY;
|
|
648
|
+
const worldX = x + game.scrollX;
|
|
649
|
+
const worldY = y + game.scrollY;
|
|
643
650
|
if (game.isPointerPressed()) {
|
|
644
651
|
this.cancelDrag();
|
|
645
|
-
const hit = this.hitTest(
|
|
646
|
-
this._press = hit ? { x, y, card: hit.card, pile: hit.pile, dragTried: false } : null;
|
|
652
|
+
const hit = this.hitTest(worldX, worldY);
|
|
653
|
+
this._press = hit ? { x, y, worldX, worldY, card: hit.card, pile: hit.pile, dragTried: false } : null;
|
|
647
654
|
return;
|
|
648
655
|
}
|
|
649
656
|
const press = this._press;
|
|
@@ -654,11 +661,14 @@ export class CardTable {
|
|
|
654
661
|
this.startDrag(press);
|
|
655
662
|
}
|
|
656
663
|
if (this._drag)
|
|
657
|
-
this.followPointer(
|
|
664
|
+
this.followPointer(worldX, worldY);
|
|
658
665
|
return;
|
|
659
666
|
}
|
|
660
667
|
this._press = null;
|
|
661
|
-
|
|
668
|
+
// A cancelled touch was interrupted, not released: return the cards, tap nothing.
|
|
669
|
+
if (game.isPointerCancelled())
|
|
670
|
+
this.cancelDrag();
|
|
671
|
+
else if (this._drag)
|
|
662
672
|
this.finishDrag();
|
|
663
673
|
else if (Math.hypot(x - press.x, y - press.y) < this.dragThreshold)
|
|
664
674
|
this.onTap?.(press.card, press.pile);
|
|
@@ -715,8 +725,8 @@ export class CardTable {
|
|
|
715
725
|
view.moving = false;
|
|
716
726
|
view.waitMs = 0;
|
|
717
727
|
view.dragging = true;
|
|
718
|
-
view.grabX = press.
|
|
719
|
-
view.grabY = press.
|
|
728
|
+
view.grabX = press.worldX - view.sprite.x;
|
|
729
|
+
view.grabY = press.worldY - view.sprite.y;
|
|
720
730
|
view.sprite.layer = this.layer + 1000 + i * 0.01;
|
|
721
731
|
this.applyScale(view);
|
|
722
732
|
});
|
package/dist/minimo.js
CHANGED
|
@@ -1816,6 +1816,15 @@ export class Game {
|
|
|
1816
1816
|
isPointerPressed() {
|
|
1817
1817
|
return this._inputSystem.isPointerPressed();
|
|
1818
1818
|
}
|
|
1819
|
+
/**
|
|
1820
|
+
* True on the frame the primary touch ended in a `touchcancel` rather than
|
|
1821
|
+
* lifting, so a module can drop the gesture instead of reading it as a tap.
|
|
1822
|
+
*
|
|
1823
|
+
* @internal
|
|
1824
|
+
*/
|
|
1825
|
+
isPointerCancelled() {
|
|
1826
|
+
return this._inputSystem.isPointerCancelled();
|
|
1827
|
+
}
|
|
1819
1828
|
/**
|
|
1820
1829
|
* Returns `true` while any active pointer is held down over the target sprite.
|
|
1821
1830
|
* Works with both mouse input and multiple simultaneous touches.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "minimojs",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.27",
|
|
4
4
|
"description": "MinimoJS v1 — ultra-minimal, flat, deterministic 2D web game engine. Image-first sprites with emoji as a zero-asset alternative, rAF loop, TypeScript-first, LLM-friendly.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/minimo.js",
|