star-canvas 0.1.7 → 0.1.8
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/PROMPT.md +154 -106
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +32 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.mjs +2 -2
- package/dist/legacy.cjs +2 -2
- package/dist/legacy.mjs +2 -2
- package/package.json +1 -1
package/PROMPT.md
CHANGED
|
@@ -44,40 +44,26 @@ Import `game` and wrap your code in it. The `game` function handles DOM readines
|
|
|
44
44
|
```ts
|
|
45
45
|
import { game } from 'star-canvas';
|
|
46
46
|
|
|
47
|
-
game((
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// on: Safe, delegated event listener
|
|
51
|
-
// loop: Stable game loop (with dt)
|
|
52
|
-
// ui: Safe overlay for HTML
|
|
53
|
-
// canvas: The <canvas> element
|
|
54
|
-
|
|
55
|
-
// 1. Draw on the canvas
|
|
56
|
-
loop((dt) => {
|
|
57
|
-
ctx.clearRect(0, 0, width, height);
|
|
58
|
-
ctx.fillStyle = '#3b82f6'; // blue-500
|
|
59
|
-
ctx.fillRect(width / 2 - 25, height / 2 - 25, 50, 50);
|
|
60
|
-
});
|
|
47
|
+
game((g) => {
|
|
48
|
+
const { ctx, width, height } = g;
|
|
49
|
+
let score = 0;
|
|
61
50
|
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
</div>
|
|
69
|
-
`);
|
|
70
|
-
|
|
71
|
-
// 3. Listen for button clicks — on() auto-enables pointer-events for the target
|
|
72
|
-
on('click', '#start-btn', () => {
|
|
73
|
-
console.log('Button clicked!');
|
|
74
|
-
});
|
|
51
|
+
// Game loop — input, update, draw
|
|
52
|
+
g.loop((dt) => {
|
|
53
|
+
// Input: check g.tap each frame (null if no tap)
|
|
54
|
+
if (g.tap) {
|
|
55
|
+
score++;
|
|
56
|
+
}
|
|
75
57
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
58
|
+
// Draw
|
|
59
|
+
ctx.fillStyle = '#0f172a';
|
|
60
|
+
ctx.fillRect(0, 0, width, height);
|
|
61
|
+
ctx.fillStyle = '#3b82f6';
|
|
62
|
+
ctx.font = 'bold 32px sans-serif';
|
|
63
|
+
ctx.textAlign = 'center';
|
|
64
|
+
ctx.fillText(`Score: ${score}`, width / 2, height / 2);
|
|
65
|
+
ctx.fillText('TAP ANYWHERE', width / 2, height / 2 + 40);
|
|
66
|
+
ctx.textAlign = 'left';
|
|
81
67
|
});
|
|
82
68
|
});
|
|
83
69
|
```
|
|
@@ -105,7 +91,8 @@ The 2D drawing context. Its transform is already scaled for DPR. You **always dr
|
|
|
105
91
|
|
|
106
92
|
The `<canvas>` element itself.
|
|
107
93
|
|
|
108
|
-
-
|
|
94
|
+
- For drawing, use `ctx` (the 2D context).
|
|
95
|
+
- For input, use `g.tap` / `g.pointer` / `g.released` in the game loop (see Input Polling below).
|
|
109
96
|
|
|
110
97
|
### `width: number` (getter)
|
|
111
98
|
|
|
@@ -140,7 +127,60 @@ A safe manager for your HTML overlay, stacked on top of the canvas.
|
|
|
140
127
|
- `ui.el(selector)`: Scoped `querySelector` for the UI root.
|
|
141
128
|
- `ui.all(selector)`: Scoped `querySelectorAll` for the UI root.
|
|
142
129
|
|
|
143
|
-
|
|
130
|
+
### Input Polling: `tap`, `pointer`, `released`
|
|
131
|
+
|
|
132
|
+
**The standard way to handle input.** Read these in your game loop — no event handlers needed.
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
g.loop((dt) => {
|
|
136
|
+
if (g.tap) {
|
|
137
|
+
// Tap/click happened this frame. g.tap.x, g.tap.y are canvas-space.
|
|
138
|
+
}
|
|
139
|
+
if (g.pointer.down) {
|
|
140
|
+
// Pointer is held. g.pointer.x, g.pointer.y track current position.
|
|
141
|
+
}
|
|
142
|
+
if (g.released) {
|
|
143
|
+
// Pointer released this frame.
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
- **`g.tap`** — `{ x, y, time } | null`. Non-null on the frame a pointerdown occurs. Cleared next frame.
|
|
149
|
+
- **`g.pointer`** — `{ x, y, down }`. Always available. Tracks current pointer position and held state.
|
|
150
|
+
- **`g.released`** — `{ x, y, time } | null`. Non-null on the frame a pointerup occurs. Cleared next frame.
|
|
151
|
+
- **`g.taps`** — `Array<{ x, y, id, time }>`. All taps this frame (multi-touch).
|
|
152
|
+
- **`g.pointers`** — `Array<{ x, y, id, down }>`. All active pointers (multi-touch).
|
|
153
|
+
|
|
154
|
+
**Why polling?** One code path with if/else for priority. No conflicting event handlers, no registration order bugs. This is how Unity, Godot, and every game engine handles input.
|
|
155
|
+
|
|
156
|
+
**Button priority pattern:**
|
|
157
|
+
```ts
|
|
158
|
+
if (g.tap) {
|
|
159
|
+
if (state === 'gameover' && inRect(g.tap, leaderboardBtn)) {
|
|
160
|
+
leaderboard.show(); // Checked first — highest priority
|
|
161
|
+
} else if (state === 'gameover') {
|
|
162
|
+
startGame(); // Generic tap — lower priority
|
|
163
|
+
} else if (state === 'playing') {
|
|
164
|
+
jump();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Hold-to-fire with cooldown:**
|
|
170
|
+
```ts
|
|
171
|
+
let fireCooldown = 0;
|
|
172
|
+
const FIRE_RATE = 0.15; // seconds between shots
|
|
173
|
+
|
|
174
|
+
g.loop((dt) => {
|
|
175
|
+
fireCooldown -= dt;
|
|
176
|
+
if (g.pointer.down && fireCooldown <= 0) {
|
|
177
|
+
spawnBullet();
|
|
178
|
+
fireCooldown = FIRE_RATE; // Slow shotgun: 0.8, fast minigun: 0.05
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Taps on interactive UI elements (from `on()` or native `<button>`) are automatically suppressed from `g.tap`.
|
|
144
184
|
|
|
145
185
|
### Cursor Management
|
|
146
186
|
|
|
@@ -285,7 +325,8 @@ For puzzle games, card games, match-3, mobile-style games - use portrait preset.
|
|
|
285
325
|
```ts
|
|
286
326
|
import { game } from 'star-canvas';
|
|
287
327
|
|
|
288
|
-
game((
|
|
328
|
+
game((g) => {
|
|
329
|
+
const { ctx, width, height } = g;
|
|
289
330
|
// width = 360, height = 640 (always, with letterboxing)
|
|
290
331
|
const cellSize = 40;
|
|
291
332
|
const gridCols = 8;
|
|
@@ -296,12 +337,17 @@ game(({ ctx, width, height, loop, canvas, toStagePoint }) => {
|
|
|
296
337
|
const gridX = (width - gridWidth) / 2;
|
|
297
338
|
const gridY = 80;
|
|
298
339
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
340
|
+
g.loop((dt) => {
|
|
341
|
+
// Input
|
|
342
|
+
if (g.tap) {
|
|
343
|
+
const col = Math.floor((g.tap.x - gridX) / cellSize);
|
|
344
|
+
const row = Math.floor((g.tap.y - gridY) / cellSize);
|
|
345
|
+
if (col >= 0 && col < gridCols && row >= 0 && row < gridRows) {
|
|
346
|
+
// Handle tap on grid cell...
|
|
347
|
+
}
|
|
348
|
+
}
|
|
303
349
|
|
|
304
|
-
|
|
350
|
+
// Draw
|
|
305
351
|
ctx.fillStyle = '#111827';
|
|
306
352
|
ctx.fillRect(0, 0, width, height);
|
|
307
353
|
|
|
@@ -326,16 +372,19 @@ For games that need different dimensions (e.g., pixel art at 320×180).
|
|
|
326
372
|
```ts
|
|
327
373
|
import { game } from 'star-canvas';
|
|
328
374
|
|
|
329
|
-
game((
|
|
375
|
+
game((g) => {
|
|
376
|
+
const { ctx, width, height } = g;
|
|
330
377
|
// Custom 320×180 resolution (retro pixel art style)
|
|
331
378
|
const player = { x: 160, y: 90 }; // Center
|
|
332
379
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
380
|
+
g.loop((dt) => {
|
|
381
|
+
// Input — g.tap coords are already in canvas-space (0-320, 0-180)
|
|
382
|
+
if (g.tap) {
|
|
383
|
+
player.x = g.tap.x;
|
|
384
|
+
player.y = g.tap.y;
|
|
385
|
+
}
|
|
337
386
|
|
|
338
|
-
|
|
387
|
+
// Draw
|
|
339
388
|
ctx.fillStyle = '#111827';
|
|
340
389
|
ctx.fillRect(0, 0, width, height);
|
|
341
390
|
|
|
@@ -345,7 +394,9 @@ game(({ ctx, width, height, loop, toStagePoint, canvas }) => {
|
|
|
345
394
|
}, { width: 320, height: 180 }); // Custom resolution with letterboxing
|
|
346
395
|
```
|
|
347
396
|
|
|
348
|
-
### Recipe 5: Complex Game with Canvas +
|
|
397
|
+
### Recipe 5: Complex Game with Canvas + Leaderboard
|
|
398
|
+
|
|
399
|
+
**Key pattern:** All input via `g.tap` in the loop. if/else for priority — check buttons first.
|
|
349
400
|
|
|
350
401
|
```ts
|
|
351
402
|
import { game } from 'star-canvas';
|
|
@@ -353,78 +404,75 @@ import { createLeaderboard } from 'star-leaderboard';
|
|
|
353
404
|
|
|
354
405
|
const leaderboard = createLeaderboard({ gameId: '<gameId from .starrc>' });
|
|
355
406
|
|
|
356
|
-
game((
|
|
407
|
+
game((g) => {
|
|
408
|
+
const { ctx, width, height } = g;
|
|
357
409
|
let score = 0;
|
|
358
410
|
let state = 'menu';
|
|
359
411
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
}
|
|
412
|
+
const lbBtn = { x: width / 2 - 120, y: height / 2 + 20, w: 240, h: 50 };
|
|
413
|
+
const restartBtn = { x: width / 2 - 120, y: height / 2 + 90, w: 240, h: 50 };
|
|
414
|
+
|
|
415
|
+
function inRect(pt, r) {
|
|
416
|
+
return pt.x >= r.x && pt.x <= r.x + r.w && pt.y >= r.y && pt.y <= r.y + r.h;
|
|
366
417
|
}
|
|
367
418
|
|
|
368
|
-
|
|
369
|
-
|
|
419
|
+
function drawButton(text, r, color) {
|
|
420
|
+
ctx.fillStyle = color || '#7c3aed';
|
|
421
|
+
ctx.fillRect(r.x, r.y, r.w, r.h);
|
|
422
|
+
ctx.fillStyle = '#fff';
|
|
423
|
+
ctx.font = 'bold 16px sans-serif';
|
|
424
|
+
ctx.textAlign = 'center';
|
|
425
|
+
ctx.fillText(text, r.x + r.w / 2, r.y + r.h / 2 + 6);
|
|
426
|
+
}
|
|
370
427
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
428
|
+
function startGame() { state = 'playing'; score = 0; }
|
|
429
|
+
|
|
430
|
+
function endGame() {
|
|
431
|
+
state = 'gameover';
|
|
432
|
+
leaderboard.submit(score);
|
|
433
|
+
}
|
|
376
434
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
435
|
+
g.loop((dt) => {
|
|
436
|
+
// --- Input (polling) ---
|
|
437
|
+
if (g.tap) {
|
|
438
|
+
if (state === 'gameover') {
|
|
439
|
+
if (inRect(g.tap, lbBtn)) leaderboard.show();
|
|
440
|
+
else if (inRect(g.tap, restartBtn)) startGame();
|
|
441
|
+
} else if (state === 'menu') {
|
|
442
|
+
startGame();
|
|
443
|
+
} else if (state === 'playing') {
|
|
444
|
+
// ... (player jump/action logic) ...
|
|
445
|
+
}
|
|
446
|
+
}
|
|
380
447
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (state === lastState && score === lastScore) return;
|
|
385
|
-
lastState = state;
|
|
386
|
-
lastScore = score;
|
|
448
|
+
// --- Draw ---
|
|
449
|
+
ctx.fillStyle = '#111827';
|
|
450
|
+
ctx.fillRect(0, 0, width, height);
|
|
387
451
|
|
|
388
452
|
if (state === 'menu') {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
453
|
+
ctx.fillStyle = '#fff';
|
|
454
|
+
ctx.font = 'bold 48px sans-serif';
|
|
455
|
+
ctx.textAlign = 'center';
|
|
456
|
+
ctx.fillText('FLOW', width / 2, height / 2 - 20);
|
|
457
|
+
ctx.font = '24px sans-serif';
|
|
458
|
+
ctx.fillText('TAP TO START', width / 2, height / 2 + 30);
|
|
394
459
|
} else if (state === 'playing') {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
460
|
+
ctx.fillStyle = '#fff';
|
|
461
|
+
ctx.font = 'bold 48px sans-serif';
|
|
462
|
+
ctx.textAlign = 'center';
|
|
463
|
+
ctx.fillText(String(score), width / 2, 60);
|
|
399
464
|
} else if (state === 'gameover') {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
</div>`);
|
|
465
|
+
ctx.fillStyle = '#fff';
|
|
466
|
+
ctx.font = '24px sans-serif';
|
|
467
|
+
ctx.textAlign = 'center';
|
|
468
|
+
ctx.fillText('GAME OVER', width / 2, height / 2 - 60);
|
|
469
|
+
ctx.font = 'bold 48px sans-serif';
|
|
470
|
+
ctx.fillText(String(score), width / 2, height / 2 - 10);
|
|
471
|
+
drawButton('VIEW LEADERBOARD', lbBtn, '#7c3aed');
|
|
472
|
+
drawButton('PLAY AGAIN', restartBtn, '#374151');
|
|
409
473
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
// 4. Call updateUI when state changes (NOT every frame)
|
|
413
|
-
updateUI();
|
|
414
|
-
|
|
415
|
-
// Update when state transitions happen
|
|
416
|
-
function startGame() {
|
|
417
|
-
state = 'playing';
|
|
418
|
-
score = 0;
|
|
419
|
-
updateUI();
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
function endGame() {
|
|
423
|
-
state = 'gameover';
|
|
424
|
-
// Submit score to leaderboard
|
|
425
|
-
leaderboard.submit(score);
|
|
426
|
-
updateUI();
|
|
427
|
-
}
|
|
474
|
+
ctx.textAlign = 'left';
|
|
475
|
+
});
|
|
428
476
|
});
|
|
429
477
|
```
|
|
430
478
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var P=Object.defineProperty;var ee=Object.getOwnPropertyDescriptor;var te=Object.getOwnPropertyNames;var ne=Object.prototype.hasOwnProperty;var re=(a,o)=>{for(var s in o)P(a,s,{get:o[s],enumerable:!0})},oe=(a,o,s,n)=>{if(o&&typeof o=="object"||typeof o=="function")for(let l of te(o))!ne.call(a,l)&&l!==s&&P(a,l,{get:()=>o[l],enumerable:!(n=ee(o,l))||n.enumerable});return a};var ie=a=>oe(P({},"__esModule",{value:!0}),a);var de={};re(de,{createDragState:()=>U,game:()=>le,version:()=>ae});module.exports=ie(de);var ae="0.8.0",W={landscape:{width:640,height:360},portrait:{width:360,height:640},responsive:{}};function U(a){let o=null,s=0,n=0;return{point(l){return a(l)},grab(l,d){let{x,y:I}=a(l);o=d,s=x-d.x,n=I-d.y},move(l){if(o){let{x:d,y:x}=a(l);o.x=d-s,o.y=x-n}},release(){let l=o;return o=null,l},get dragging(){return o}}}function j(){return typeof window<"u"&&typeof document<"u"}function se(){if(!j()||document.getElementById("star-canvas-base"))return;let a=document.createElement("style");a.id="star-canvas-base",a.textContent=`
|
|
2
2
|
html, body { height: 100%; }
|
|
3
3
|
body {
|
|
4
4
|
margin: 0; min-height: 100dvh; overflow: hidden; background: #000;
|
|
@@ -29,4 +29,4 @@
|
|
|
29
29
|
.star-ui textarea, .star-ui [data-interactive] {
|
|
30
30
|
pointer-events: auto;
|
|
31
31
|
}
|
|
32
|
-
`,document.head.appendChild(a)}function
|
|
32
|
+
`,document.head.appendChild(a)}function le(a,o={}){j()&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>B(a,o),{once:!0}):queueMicrotask(()=>B(a,o)))}function B(a,o){if(se(),window.__STAR_DOM__?.destroy)try{window.__STAR_DOM__.destroy()}catch{}document.querySelectorAll(".star-ui, .star-canvas, .star-input").forEach(e=>e.remove());let s=document.createElement("div");s.className="star-ui",document.body.appendChild(s);let n=document.createElement("canvas");n.className="star-canvas",document.body.appendChild(n);let l=n.getContext("2d",o.contextAttributes??{alpha:!0});if(!l)throw new Error("[star-canvas] Failed to get 2D context");if(o.preventContextMenu!==!1){let e=t=>t.preventDefault();n.addEventListener("contextmenu",e)}let d=document.createElement("div");d.className="star-input",document.body.appendChild(d);let x=[],I=[],G=[],A=null,w={x:0,y:0,down:!1},S=null,R=[],L=new Map,z=null,M=null;Object.defineProperty(n,"onclick",{get:()=>z,set:e=>{M&&(d.removeEventListener("click",M),M=null),z=e,e&&(M=t=>{O(t)||e.call(n,t)},d.addEventListener("click",M))}});let V=n.addEventListener.bind(n),J=n.removeEventListener.bind(n),H=new WeakMap;function O(e){let t=e;if(t.clientX==null||t.clientY==null)return!1;let r=document.elementFromPoint(t.clientX,t.clientY);return r!==null&&r!==s&&s.contains(r)}let K=/^(click|pointerdown|mousedown|touchstart)$/;n.addEventListener=function(e,t,r){if(/^(click|pointer|mouse|touch)/.test(e)&&typeof t=="function"){let i=K.test(e),c=(u=>{i&&O(u)||t.call(n,u)});H.set(t,c),d.addEventListener(e,c,r)}else V(e,t,r)},n.removeEventListener=function(e,t,r){if(/^(click|pointer|mouse|touch)/.test(e)&&typeof t=="function"){let i=H.get(t);i&&(d.removeEventListener(e,i,r),H.delete(t))}else J(e,t,r)};let Q=o.preset??"landscape",X=W[Q]??W.landscape,T=o.width??X.width,C=o.height??X.height,Y=o.maxPixelRatio??2,$=o.pixelRatio??"device",D=o.fit??"contain",g=1,m=T??1,p=C??1,q=new Set,_=null,b=[];function Z(){return Math.min(typeof $=="number"?Math.max(1,$):Math.max(1,window.devicePixelRatio||1),Y)}function f(){g=Z();let e=document.body.getBoundingClientRect(),t=Math.max(1,Math.floor(e.width||window.innerWidth||800)),r=Math.max(1,Math.floor(e.height||window.innerHeight||600));if(T&&C){m=T,p=C;let u=m/p,v=t/r,h=1;if(D==="contain"?h=v>u?r/p:t/m:D==="cover"&&(h=v>u?t/m:r/p),D==="stretch")n.style.width="100%",n.style.height="100%";else{let y=Math.floor(m*h),N=Math.floor(p*h);n.style.width=`${y}px`,n.style.height=`${N}px`,n.style.position="absolute",n.style.left=`${Math.floor((t-y)/2)}px`,n.style.top=`${Math.floor((r-N)/2)}px`}}else if(C){p=C;let u=t/r;m=Math.floor(p*u),n.style.width="100%",n.style.height="100%",n.style.position="absolute",n.style.left="0",n.style.top="0"}else if(T){m=T;let u=t/r;p=Math.floor(m/u),n.style.width="100%",n.style.height="100%",n.style.position="absolute",n.style.left="0",n.style.top="0"}else m=t,p=r,n.style.width=`${m}px`,n.style.height=`${p}px`,n.style.position="absolute",n.style.left="0",n.style.top="0";let i=Math.max(1,Math.floor(m*g)),c=Math.max(1,Math.floor(p*g));n.width!==i&&(n.width=i),n.height!==c&&(n.height=c),l.setTransform(g,0,0,g,0,0),q.forEach(u=>u())}let F=new ResizeObserver(f);F.observe(document.body),b.push(()=>F.disconnect()),window.addEventListener("resize",f),b.push(()=>window.removeEventListener("resize",f));let k=window.visualViewport;k&&(k.addEventListener("resize",f),b.push(()=>k.removeEventListener("resize",f)));let E={stage:document.body,canvas:n,ctx:l,get width(){return m},get height(){return p},get dpr(){return g},resize:f,toStagePoint:e=>{let t=n.getBoundingClientRect(),r=(e.clientX-t.left)*(m/t.width),i=(e.clientY-t.top)*(p/t.height);return r=Math.max(0,Math.min(m,r)),i=Math.max(0,Math.min(p,i)),{x:r,y:i}},createDrag:()=>U(E.toStagePoint),onTap:e=>{x.push(e)},onMove:e=>{I.push(e)},onRelease:e=>{G.push(e)},get tap(){return A},get pointer(){return w},get released(){return S},get taps(){return R},get pointers(){return Array.from(L.values())},on:(e,t,r,i)=>{if(/^(click|pointer|mouse|touch)/.test(e))try{let v=t.split(",").map(y=>`.star-ui ${y.trim()}`).join(", "),h=document.createElement("style");h.textContent=`${v} { pointer-events: auto; }`,document.head.appendChild(h),b.push(()=>h.remove())}catch{}let c=v=>{let y=v.target?.closest?.(t);y&&r.call(y,v)};document.addEventListener(e,c,i);let u=()=>document.removeEventListener(e,c,i);return b.push(u),u},loop:e=>{let r=0,i=!1,c=0,u=v=>{if(!i)return;let h=c?Math.min((v-c)/1e3,.1):0;c=v;try{e(h,v)}catch(y){console.error("[star-canvas] Game loop error:",y)}A=null,S=null,R=[],r=requestAnimationFrame(u)};return _={get running(){return i},start(){i||(i=!0,c=performance.now(),r=requestAnimationFrame(u))},stop(){i&&(i=!1,cancelAnimationFrame(r))}},_.start(),_},ui:{root:s,render:e=>{s.innerHTML!==e&&(s.innerHTML=e)},el:e=>s.querySelector(e),all:e=>s.querySelectorAll(e)},destroy:()=>{_?.stop(),b.forEach(e=>e()),b=[],q.clear(),n.parentElement&&n.parentElement.removeChild(n),s.parentElement&&s.parentElement.removeChild(s),d.parentElement&&d.parentElement.removeChild(d)},scoped:e=>{l.save();try{e()}finally{l.restore()}}};d.addEventListener("pointerdown",e=>{let t=E.toStagePoint(e);if(w={x:t.x,y:t.y,down:!0},L.set(e.pointerId,{x:t.x,y:t.y,id:e.pointerId,down:!0}),O(e))return;A={x:t.x,y:t.y,time:e.timeStamp},R.push({x:t.x,y:t.y,id:e.pointerId,time:e.timeStamp});let r={...t,event:e};x.forEach(i=>i(r))}),d.addEventListener("pointermove",e=>{let t=E.toStagePoint(e);w={x:t.x,y:t.y,down:w.down};let r=L.get(e.pointerId);r&&L.set(e.pointerId,{...r,x:t.x,y:t.y});let i={...t,event:e};I.forEach(c=>c(i))}),d.addEventListener("pointerup",e=>{let t=E.toStagePoint(e);w={x:t.x,y:t.y,down:!1},S={x:t.x,y:t.y,time:e.timeStamp},L.delete(e.pointerId);let r={...t,event:e};G.forEach(i=>i(r))}),window.__STAR_DOM__={destroy:E.destroy},requestAnimationFrame(()=>requestAnimationFrame(()=>{f();try{a(E)}catch(e){console.error("[star-canvas] Game initialization failed:",e)}}))}0&&(module.exports={createDragState,game,version});
|
package/dist/index.d.cts
CHANGED
|
@@ -83,6 +83,38 @@ interface GameContext {
|
|
|
83
83
|
onMove: (handler: InputHandler) => void;
|
|
84
84
|
/** Register handler for pointer release (fires on pointerup). Works anywhere on screen including letterbox. */
|
|
85
85
|
onRelease: (handler: InputHandler) => void;
|
|
86
|
+
/** Tap this frame (pointerdown). Null if none. Canvas-space coords. Read in your game loop. */
|
|
87
|
+
readonly tap: {
|
|
88
|
+
x: number;
|
|
89
|
+
y: number;
|
|
90
|
+
time: number;
|
|
91
|
+
} | null;
|
|
92
|
+
/** Current primary pointer state. Always available, updated on every move. */
|
|
93
|
+
readonly pointer: {
|
|
94
|
+
x: number;
|
|
95
|
+
y: number;
|
|
96
|
+
down: boolean;
|
|
97
|
+
};
|
|
98
|
+
/** Release this frame (pointerup). Null if none. Canvas-space coords. */
|
|
99
|
+
readonly released: {
|
|
100
|
+
x: number;
|
|
101
|
+
y: number;
|
|
102
|
+
time: number;
|
|
103
|
+
} | null;
|
|
104
|
+
/** All new taps this frame (multi-touch). Empty array if none. */
|
|
105
|
+
readonly taps: ReadonlyArray<{
|
|
106
|
+
x: number;
|
|
107
|
+
y: number;
|
|
108
|
+
id: number;
|
|
109
|
+
time: number;
|
|
110
|
+
}>;
|
|
111
|
+
/** All active pointers (multi-touch). Empty array if none. */
|
|
112
|
+
readonly pointers: ReadonlyArray<{
|
|
113
|
+
x: number;
|
|
114
|
+
y: number;
|
|
115
|
+
id: number;
|
|
116
|
+
down: boolean;
|
|
117
|
+
}>;
|
|
86
118
|
/** Re-calculates stage size (rarely needed). */
|
|
87
119
|
resize: () => void;
|
|
88
120
|
/** Cleans up all listeners and observers. */
|
package/dist/index.d.ts
CHANGED
|
@@ -83,6 +83,38 @@ interface GameContext {
|
|
|
83
83
|
onMove: (handler: InputHandler) => void;
|
|
84
84
|
/** Register handler for pointer release (fires on pointerup). Works anywhere on screen including letterbox. */
|
|
85
85
|
onRelease: (handler: InputHandler) => void;
|
|
86
|
+
/** Tap this frame (pointerdown). Null if none. Canvas-space coords. Read in your game loop. */
|
|
87
|
+
readonly tap: {
|
|
88
|
+
x: number;
|
|
89
|
+
y: number;
|
|
90
|
+
time: number;
|
|
91
|
+
} | null;
|
|
92
|
+
/** Current primary pointer state. Always available, updated on every move. */
|
|
93
|
+
readonly pointer: {
|
|
94
|
+
x: number;
|
|
95
|
+
y: number;
|
|
96
|
+
down: boolean;
|
|
97
|
+
};
|
|
98
|
+
/** Release this frame (pointerup). Null if none. Canvas-space coords. */
|
|
99
|
+
readonly released: {
|
|
100
|
+
x: number;
|
|
101
|
+
y: number;
|
|
102
|
+
time: number;
|
|
103
|
+
} | null;
|
|
104
|
+
/** All new taps this frame (multi-touch). Empty array if none. */
|
|
105
|
+
readonly taps: ReadonlyArray<{
|
|
106
|
+
x: number;
|
|
107
|
+
y: number;
|
|
108
|
+
id: number;
|
|
109
|
+
time: number;
|
|
110
|
+
}>;
|
|
111
|
+
/** All active pointers (multi-touch). Empty array if none. */
|
|
112
|
+
readonly pointers: ReadonlyArray<{
|
|
113
|
+
x: number;
|
|
114
|
+
y: number;
|
|
115
|
+
id: number;
|
|
116
|
+
down: boolean;
|
|
117
|
+
}>;
|
|
86
118
|
/** Re-calculates stage size (rarely needed). */
|
|
87
119
|
resize: () => void;
|
|
88
120
|
/** Cleans up all listeners and observers. */
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
1
|
+
var ee="0.8.0",N={landscape:{width:640,height:360},portrait:{width:360,height:640},responsive:{}};function Q(p){let i=null,l=0,n=0;return{point(m){return p(m)},grab(m,a){let{x,y:I}=p(m);i=a,l=x-a.x,n=I-a.y},move(m){if(i){let{x:a,y:x}=p(m);i.x=a-l,i.y=x-n}},release(){let m=i;return i=null,m},get dragging(){return i}}}function B(){return typeof window<"u"&&typeof document<"u"}function Z(){if(!B()||document.getElementById("star-canvas-base"))return;let p=document.createElement("style");p.id="star-canvas-base",p.textContent=`
|
|
2
2
|
html, body { height: 100%; }
|
|
3
3
|
body {
|
|
4
4
|
margin: 0; min-height: 100dvh; overflow: hidden; background: #000;
|
|
@@ -29,4 +29,4 @@ var V="0.8.0",X={landscape:{width:640,height:360},portrait:{width:360,height:640
|
|
|
29
29
|
.star-ui textarea, .star-ui [data-interactive] {
|
|
30
30
|
pointer-events: auto;
|
|
31
31
|
}
|
|
32
|
-
`,document.head.appendChild(p)}function
|
|
32
|
+
`,document.head.appendChild(p)}function te(p,i={}){B()&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>W(p,i),{once:!0}):queueMicrotask(()=>W(p,i)))}function W(p,i){if(Z(),window.__STAR_DOM__?.destroy)try{window.__STAR_DOM__.destroy()}catch{}document.querySelectorAll(".star-ui, .star-canvas, .star-input").forEach(e=>e.remove());let l=document.createElement("div");l.className="star-ui",document.body.appendChild(l);let n=document.createElement("canvas");n.className="star-canvas",document.body.appendChild(n);let m=n.getContext("2d",i.contextAttributes??{alpha:!0});if(!m)throw new Error("[star-canvas] Failed to get 2D context");if(i.preventContextMenu!==!1){let e=t=>t.preventDefault();n.addEventListener("contextmenu",e)}let a=document.createElement("div");a.className="star-input",document.body.appendChild(a);let x=[],I=[],P=[],A=null,w={x:0,y:0,down:!1},S=null,R=[],L=new Map,G=null,M=null;Object.defineProperty(n,"onclick",{get:()=>G,set:e=>{M&&(a.removeEventListener("click",M),M=null),G=e,e&&(M=t=>{O(t)||e.call(n,t)},a.addEventListener("click",M))}});let U=n.addEventListener.bind(n),j=n.removeEventListener.bind(n),H=new WeakMap;function O(e){let t=e;if(t.clientX==null||t.clientY==null)return!1;let r=document.elementFromPoint(t.clientX,t.clientY);return r!==null&&r!==l&&l.contains(r)}let V=/^(click|pointerdown|mousedown|touchstart)$/;n.addEventListener=function(e,t,r){if(/^(click|pointer|mouse|touch)/.test(e)&&typeof t=="function"){let o=V.test(e),d=(s=>{o&&O(s)||t.call(n,s)});H.set(t,d),a.addEventListener(e,d,r)}else U(e,t,r)},n.removeEventListener=function(e,t,r){if(/^(click|pointer|mouse|touch)/.test(e)&&typeof t=="function"){let o=H.get(t);o&&(a.removeEventListener(e,o,r),H.delete(t))}else j(e,t,r)};let J=i.preset??"landscape",z=N[J]??N.landscape,T=i.width??z.width,C=i.height??z.height,X=i.maxPixelRatio??2,Y=i.pixelRatio??"device",D=i.fit??"contain",g=1,u=T??1,c=C??1,$=new Set,_=null,b=[];function K(){return Math.min(typeof Y=="number"?Math.max(1,Y):Math.max(1,window.devicePixelRatio||1),X)}function f(){g=K();let e=document.body.getBoundingClientRect(),t=Math.max(1,Math.floor(e.width||window.innerWidth||800)),r=Math.max(1,Math.floor(e.height||window.innerHeight||600));if(T&&C){u=T,c=C;let s=u/c,v=t/r,h=1;if(D==="contain"?h=v>s?r/c:t/u:D==="cover"&&(h=v>s?t/u:r/c),D==="stretch")n.style.width="100%",n.style.height="100%";else{let y=Math.floor(u*h),F=Math.floor(c*h);n.style.width=`${y}px`,n.style.height=`${F}px`,n.style.position="absolute",n.style.left=`${Math.floor((t-y)/2)}px`,n.style.top=`${Math.floor((r-F)/2)}px`}}else if(C){c=C;let s=t/r;u=Math.floor(c*s),n.style.width="100%",n.style.height="100%",n.style.position="absolute",n.style.left="0",n.style.top="0"}else if(T){u=T;let s=t/r;c=Math.floor(u/s),n.style.width="100%",n.style.height="100%",n.style.position="absolute",n.style.left="0",n.style.top="0"}else u=t,c=r,n.style.width=`${u}px`,n.style.height=`${c}px`,n.style.position="absolute",n.style.left="0",n.style.top="0";let o=Math.max(1,Math.floor(u*g)),d=Math.max(1,Math.floor(c*g));n.width!==o&&(n.width=o),n.height!==d&&(n.height=d),m.setTransform(g,0,0,g,0,0),$.forEach(s=>s())}let q=new ResizeObserver(f);q.observe(document.body),b.push(()=>q.disconnect()),window.addEventListener("resize",f),b.push(()=>window.removeEventListener("resize",f));let k=window.visualViewport;k&&(k.addEventListener("resize",f),b.push(()=>k.removeEventListener("resize",f)));let E={stage:document.body,canvas:n,ctx:m,get width(){return u},get height(){return c},get dpr(){return g},resize:f,toStagePoint:e=>{let t=n.getBoundingClientRect(),r=(e.clientX-t.left)*(u/t.width),o=(e.clientY-t.top)*(c/t.height);return r=Math.max(0,Math.min(u,r)),o=Math.max(0,Math.min(c,o)),{x:r,y:o}},createDrag:()=>Q(E.toStagePoint),onTap:e=>{x.push(e)},onMove:e=>{I.push(e)},onRelease:e=>{P.push(e)},get tap(){return A},get pointer(){return w},get released(){return S},get taps(){return R},get pointers(){return Array.from(L.values())},on:(e,t,r,o)=>{if(/^(click|pointer|mouse|touch)/.test(e))try{let v=t.split(",").map(y=>`.star-ui ${y.trim()}`).join(", "),h=document.createElement("style");h.textContent=`${v} { pointer-events: auto; }`,document.head.appendChild(h),b.push(()=>h.remove())}catch{}let d=v=>{let y=v.target?.closest?.(t);y&&r.call(y,v)};document.addEventListener(e,d,o);let s=()=>document.removeEventListener(e,d,o);return b.push(s),s},loop:e=>{let r=0,o=!1,d=0,s=v=>{if(!o)return;let h=d?Math.min((v-d)/1e3,.1):0;d=v;try{e(h,v)}catch(y){console.error("[star-canvas] Game loop error:",y)}A=null,S=null,R=[],r=requestAnimationFrame(s)};return _={get running(){return o},start(){o||(o=!0,d=performance.now(),r=requestAnimationFrame(s))},stop(){o&&(o=!1,cancelAnimationFrame(r))}},_.start(),_},ui:{root:l,render:e=>{l.innerHTML!==e&&(l.innerHTML=e)},el:e=>l.querySelector(e),all:e=>l.querySelectorAll(e)},destroy:()=>{_?.stop(),b.forEach(e=>e()),b=[],$.clear(),n.parentElement&&n.parentElement.removeChild(n),l.parentElement&&l.parentElement.removeChild(l),a.parentElement&&a.parentElement.removeChild(a)},scoped:e=>{m.save();try{e()}finally{m.restore()}}};a.addEventListener("pointerdown",e=>{let t=E.toStagePoint(e);if(w={x:t.x,y:t.y,down:!0},L.set(e.pointerId,{x:t.x,y:t.y,id:e.pointerId,down:!0}),O(e))return;A={x:t.x,y:t.y,time:e.timeStamp},R.push({x:t.x,y:t.y,id:e.pointerId,time:e.timeStamp});let r={...t,event:e};x.forEach(o=>o(r))}),a.addEventListener("pointermove",e=>{let t=E.toStagePoint(e);w={x:t.x,y:t.y,down:w.down};let r=L.get(e.pointerId);r&&L.set(e.pointerId,{...r,x:t.x,y:t.y});let o={...t,event:e};I.forEach(d=>d(o))}),a.addEventListener("pointerup",e=>{let t=E.toStagePoint(e);w={x:t.x,y:t.y,down:!1},S={x:t.x,y:t.y,time:e.timeStamp},L.delete(e.pointerId);let r={...t,event:e};P.forEach(o=>o(r))}),window.__STAR_DOM__={destroy:E.destroy},requestAnimationFrame(()=>requestAnimationFrame(()=>{f();try{p(E)}catch(e){console.error("[star-canvas] Game initialization failed:",e)}}))}export{Q as createDragState,te as game,ee as version};
|
package/dist/legacy.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var k=Object.defineProperty;var ne=Object.getOwnPropertyDescriptor;var re=Object.getOwnPropertyNames;var oe=Object.prototype.hasOwnProperty;var ie=(a,o)=>{for(var s in o)k(a,s,{get:o[s],enumerable:!0})},ae=(a,o,s,n)=>{if(o&&typeof o=="object"||typeof o=="function")for(let l of re(o))!oe.call(a,l)&&l!==s&&k(a,l,{get:()=>o[l],enumerable:!(n=ne(o,l))||n.enumerable});return a};var se=a=>ae(k({},"__esModule",{value:!0}),a);var ue={};ie(ue,{createDragState:()=>P,game:()=>de,version:()=>j});module.exports=se(ue);var j="0.8.0",U={landscape:{width:640,height:360},portrait:{width:360,height:640},responsive:{}};function P(a){let o=null,s=0,n=0;return{point(l){return a(l)},grab(l,d){let{x,y:I}=a(l);o=d,s=x-d.x,n=I-d.y},move(l){if(o){let{x:d,y:x}=a(l);o.x=d-s,o.y=x-n}},release(){let l=o;return o=null,l},get dragging(){return o}}}function V(){return typeof window<"u"&&typeof document<"u"}function le(){if(!V()||document.getElementById("star-canvas-base"))return;let a=document.createElement("style");a.id="star-canvas-base",a.textContent=`
|
|
2
2
|
html, body { height: 100%; }
|
|
3
3
|
body {
|
|
4
4
|
margin: 0; min-height: 100dvh; overflow: hidden; background: #000;
|
|
@@ -29,4 +29,4 @@
|
|
|
29
29
|
.star-ui textarea, .star-ui [data-interactive] {
|
|
30
30
|
pointer-events: auto;
|
|
31
31
|
}
|
|
32
|
-
`,document.head.appendChild(
|
|
32
|
+
`,document.head.appendChild(a)}function J(a,o={}){V()&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>B(a,o),{once:!0}):queueMicrotask(()=>B(a,o)))}function B(a,o){if(le(),window.__STAR_DOM__?.destroy)try{window.__STAR_DOM__.destroy()}catch{}document.querySelectorAll(".star-ui, .star-canvas, .star-input").forEach(e=>e.remove());let s=document.createElement("div");s.className="star-ui",document.body.appendChild(s);let n=document.createElement("canvas");n.className="star-canvas",document.body.appendChild(n);let l=n.getContext("2d",o.contextAttributes??{alpha:!0});if(!l)throw new Error("[star-canvas] Failed to get 2D context");if(o.preventContextMenu!==!1){let e=t=>t.preventDefault();n.addEventListener("contextmenu",e)}let d=document.createElement("div");d.className="star-input",document.body.appendChild(d);let x=[],I=[],z=[],_=null,w={x:0,y:0,down:!1},A=null,G=[],L=new Map,X=null,M=null;Object.defineProperty(n,"onclick",{get:()=>X,set:e=>{M&&(d.removeEventListener("click",M),M=null),X=e,e&&(M=t=>{R(t)||e.call(n,t)},d.addEventListener("click",M))}});let K=n.addEventListener.bind(n),Q=n.removeEventListener.bind(n),O=new WeakMap;function R(e){let t=e;if(t.clientX==null||t.clientY==null)return!1;let r=document.elementFromPoint(t.clientX,t.clientY);return r!==null&&r!==s&&s.contains(r)}let Z=/^(click|pointerdown|mousedown|touchstart)$/;n.addEventListener=function(e,t,r){if(/^(click|pointer|mouse|touch)/.test(e)&&typeof t=="function"){let i=Z.test(e),c=(u=>{i&&R(u)||t.call(n,u)});O.set(t,c),d.addEventListener(e,c,r)}else K(e,t,r)},n.removeEventListener=function(e,t,r){if(/^(click|pointer|mouse|touch)/.test(e)&&typeof t=="function"){let i=O.get(t);i&&(d.removeEventListener(e,i,r),O.delete(t))}else Q(e,t,r)};let ee=o.preset??"landscape",Y=U[ee]??U.landscape,T=o.width??Y.width,C=o.height??Y.height,$=o.maxPixelRatio??2,q=o.pixelRatio??"device",H=o.fit??"contain",g=1,m=T??1,p=C??1,F=new Set,S=null,b=[];function te(){return Math.min(typeof q=="number"?Math.max(1,q):Math.max(1,window.devicePixelRatio||1),$)}function f(){g=te();let e=document.body.getBoundingClientRect(),t=Math.max(1,Math.floor(e.width||window.innerWidth||800)),r=Math.max(1,Math.floor(e.height||window.innerHeight||600));if(T&&C){m=T,p=C;let u=m/p,v=t/r,h=1;if(H==="contain"?h=v>u?r/p:t/m:H==="cover"&&(h=v>u?t/m:r/p),H==="stretch")n.style.width="100%",n.style.height="100%";else{let y=Math.floor(m*h),W=Math.floor(p*h);n.style.width=`${y}px`,n.style.height=`${W}px`,n.style.position="absolute",n.style.left=`${Math.floor((t-y)/2)}px`,n.style.top=`${Math.floor((r-W)/2)}px`}}else if(C){p=C;let u=t/r;m=Math.floor(p*u),n.style.width="100%",n.style.height="100%",n.style.position="absolute",n.style.left="0",n.style.top="0"}else if(T){m=T;let u=t/r;p=Math.floor(m/u),n.style.width="100%",n.style.height="100%",n.style.position="absolute",n.style.left="0",n.style.top="0"}else m=t,p=r,n.style.width=`${m}px`,n.style.height=`${p}px`,n.style.position="absolute",n.style.left="0",n.style.top="0";let i=Math.max(1,Math.floor(m*g)),c=Math.max(1,Math.floor(p*g));n.width!==i&&(n.width=i),n.height!==c&&(n.height=c),l.setTransform(g,0,0,g,0,0),F.forEach(u=>u())}let N=new ResizeObserver(f);N.observe(document.body),b.push(()=>N.disconnect()),window.addEventListener("resize",f),b.push(()=>window.removeEventListener("resize",f));let D=window.visualViewport;D&&(D.addEventListener("resize",f),b.push(()=>D.removeEventListener("resize",f)));let E={stage:document.body,canvas:n,ctx:l,get width(){return m},get height(){return p},get dpr(){return g},resize:f,toStagePoint:e=>{let t=n.getBoundingClientRect(),r=(e.clientX-t.left)*(m/t.width),i=(e.clientY-t.top)*(p/t.height);return r=Math.max(0,Math.min(m,r)),i=Math.max(0,Math.min(p,i)),{x:r,y:i}},createDrag:()=>P(E.toStagePoint),onTap:e=>{x.push(e)},onMove:e=>{I.push(e)},onRelease:e=>{z.push(e)},get tap(){return _},get pointer(){return w},get released(){return A},get taps(){return G},get pointers(){return Array.from(L.values())},on:(e,t,r,i)=>{if(/^(click|pointer|mouse|touch)/.test(e))try{let v=t.split(",").map(y=>`.star-ui ${y.trim()}`).join(", "),h=document.createElement("style");h.textContent=`${v} { pointer-events: auto; }`,document.head.appendChild(h),b.push(()=>h.remove())}catch{}let c=v=>{let y=v.target?.closest?.(t);y&&r.call(y,v)};document.addEventListener(e,c,i);let u=()=>document.removeEventListener(e,c,i);return b.push(u),u},loop:e=>{let r=0,i=!1,c=0,u=v=>{if(!i)return;let h=c?Math.min((v-c)/1e3,.1):0;c=v;try{e(h,v)}catch(y){console.error("[star-canvas] Game loop error:",y)}_=null,A=null,G=[],r=requestAnimationFrame(u)};return S={get running(){return i},start(){i||(i=!0,c=performance.now(),r=requestAnimationFrame(u))},stop(){i&&(i=!1,cancelAnimationFrame(r))}},S.start(),S},ui:{root:s,render:e=>{s.innerHTML!==e&&(s.innerHTML=e)},el:e=>s.querySelector(e),all:e=>s.querySelectorAll(e)},destroy:()=>{S?.stop(),b.forEach(e=>e()),b=[],F.clear(),n.parentElement&&n.parentElement.removeChild(n),s.parentElement&&s.parentElement.removeChild(s),d.parentElement&&d.parentElement.removeChild(d)},scoped:e=>{l.save();try{e()}finally{l.restore()}}};d.addEventListener("pointerdown",e=>{let t=E.toStagePoint(e);if(w={x:t.x,y:t.y,down:!0},L.set(e.pointerId,{x:t.x,y:t.y,id:e.pointerId,down:!0}),R(e))return;_={x:t.x,y:t.y,time:e.timeStamp},G.push({x:t.x,y:t.y,id:e.pointerId,time:e.timeStamp});let r={...t,event:e};x.forEach(i=>i(r))}),d.addEventListener("pointermove",e=>{let t=E.toStagePoint(e);w={x:t.x,y:t.y,down:w.down};let r=L.get(e.pointerId);r&&L.set(e.pointerId,{...r,x:t.x,y:t.y});let i={...t,event:e};I.forEach(c=>c(i))}),d.addEventListener("pointerup",e=>{let t=E.toStagePoint(e);w={x:t.x,y:t.y,down:!1},A={x:t.x,y:t.y,time:e.timeStamp},L.delete(e.pointerId);let r={...t,event:e};z.forEach(i=>i(r))}),window.__STAR_DOM__={destroy:E.destroy},requestAnimationFrame(()=>requestAnimationFrame(()=>{f();try{a(E)}catch(e){console.error("[star-canvas] Game initialization failed:",e)}}))}function de(a,o={}){let s={preset:"responsive",...o};return J(a,s)}0&&(module.exports={createDragState,game,version});
|
package/dist/legacy.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
1
|
+
var ee="0.8.0",N={landscape:{width:640,height:360},portrait:{width:360,height:640},responsive:{}};function U(u){let i=null,s=0,n=0;return{point(p){return u(p)},grab(p,a){let{x,y:I}=u(p);i=a,s=x-a.x,n=I-a.y},move(p){if(i){let{x:a,y:x}=u(p);i.x=a-s,i.y=x-n}},release(){let p=i;return i=null,p},get dragging(){return i}}}function B(){return typeof window<"u"&&typeof document<"u"}function te(){if(!B()||document.getElementById("star-canvas-base"))return;let u=document.createElement("style");u.id="star-canvas-base",u.textContent=`
|
|
2
2
|
html, body { height: 100%; }
|
|
3
3
|
body {
|
|
4
4
|
margin: 0; min-height: 100dvh; overflow: hidden; background: #000;
|
|
@@ -29,4 +29,4 @@ var V="0.8.0",X={landscape:{width:640,height:360},portrait:{width:360,height:640
|
|
|
29
29
|
.star-ui textarea, .star-ui [data-interactive] {
|
|
30
30
|
pointer-events: auto;
|
|
31
31
|
}
|
|
32
|
-
`,document.head.appendChild(
|
|
32
|
+
`,document.head.appendChild(u)}function j(u,i={}){B()&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>W(u,i),{once:!0}):queueMicrotask(()=>W(u,i)))}function W(u,i){if(te(),window.__STAR_DOM__?.destroy)try{window.__STAR_DOM__.destroy()}catch{}document.querySelectorAll(".star-ui, .star-canvas, .star-input").forEach(e=>e.remove());let s=document.createElement("div");s.className="star-ui",document.body.appendChild(s);let n=document.createElement("canvas");n.className="star-canvas",document.body.appendChild(n);let p=n.getContext("2d",i.contextAttributes??{alpha:!0});if(!p)throw new Error("[star-canvas] Failed to get 2D context");if(i.preventContextMenu!==!1){let e=t=>t.preventDefault();n.addEventListener("contextmenu",e)}let a=document.createElement("div");a.className="star-input",document.body.appendChild(a);let x=[],I=[],k=[],_=null,w={x:0,y:0,down:!1},A=null,G=[],L=new Map,P=null,M=null;Object.defineProperty(n,"onclick",{get:()=>P,set:e=>{M&&(a.removeEventListener("click",M),M=null),P=e,e&&(M=t=>{R(t)||e.call(n,t)},a.addEventListener("click",M))}});let V=n.addEventListener.bind(n),J=n.removeEventListener.bind(n),O=new WeakMap;function R(e){let t=e;if(t.clientX==null||t.clientY==null)return!1;let r=document.elementFromPoint(t.clientX,t.clientY);return r!==null&&r!==s&&s.contains(r)}let K=/^(click|pointerdown|mousedown|touchstart)$/;n.addEventListener=function(e,t,r){if(/^(click|pointer|mouse|touch)/.test(e)&&typeof t=="function"){let o=K.test(e),d=(l=>{o&&R(l)||t.call(n,l)});O.set(t,d),a.addEventListener(e,d,r)}else V(e,t,r)},n.removeEventListener=function(e,t,r){if(/^(click|pointer|mouse|touch)/.test(e)&&typeof t=="function"){let o=O.get(t);o&&(a.removeEventListener(e,o,r),O.delete(t))}else J(e,t,r)};let Q=i.preset??"landscape",z=N[Q]??N.landscape,T=i.width??z.width,C=i.height??z.height,X=i.maxPixelRatio??2,Y=i.pixelRatio??"device",H=i.fit??"contain",g=1,c=T??1,m=C??1,$=new Set,S=null,b=[];function Z(){return Math.min(typeof Y=="number"?Math.max(1,Y):Math.max(1,window.devicePixelRatio||1),X)}function f(){g=Z();let e=document.body.getBoundingClientRect(),t=Math.max(1,Math.floor(e.width||window.innerWidth||800)),r=Math.max(1,Math.floor(e.height||window.innerHeight||600));if(T&&C){c=T,m=C;let l=c/m,v=t/r,h=1;if(H==="contain"?h=v>l?r/m:t/c:H==="cover"&&(h=v>l?t/c:r/m),H==="stretch")n.style.width="100%",n.style.height="100%";else{let y=Math.floor(c*h),F=Math.floor(m*h);n.style.width=`${y}px`,n.style.height=`${F}px`,n.style.position="absolute",n.style.left=`${Math.floor((t-y)/2)}px`,n.style.top=`${Math.floor((r-F)/2)}px`}}else if(C){m=C;let l=t/r;c=Math.floor(m*l),n.style.width="100%",n.style.height="100%",n.style.position="absolute",n.style.left="0",n.style.top="0"}else if(T){c=T;let l=t/r;m=Math.floor(c/l),n.style.width="100%",n.style.height="100%",n.style.position="absolute",n.style.left="0",n.style.top="0"}else c=t,m=r,n.style.width=`${c}px`,n.style.height=`${m}px`,n.style.position="absolute",n.style.left="0",n.style.top="0";let o=Math.max(1,Math.floor(c*g)),d=Math.max(1,Math.floor(m*g));n.width!==o&&(n.width=o),n.height!==d&&(n.height=d),p.setTransform(g,0,0,g,0,0),$.forEach(l=>l())}let q=new ResizeObserver(f);q.observe(document.body),b.push(()=>q.disconnect()),window.addEventListener("resize",f),b.push(()=>window.removeEventListener("resize",f));let D=window.visualViewport;D&&(D.addEventListener("resize",f),b.push(()=>D.removeEventListener("resize",f)));let E={stage:document.body,canvas:n,ctx:p,get width(){return c},get height(){return m},get dpr(){return g},resize:f,toStagePoint:e=>{let t=n.getBoundingClientRect(),r=(e.clientX-t.left)*(c/t.width),o=(e.clientY-t.top)*(m/t.height);return r=Math.max(0,Math.min(c,r)),o=Math.max(0,Math.min(m,o)),{x:r,y:o}},createDrag:()=>U(E.toStagePoint),onTap:e=>{x.push(e)},onMove:e=>{I.push(e)},onRelease:e=>{k.push(e)},get tap(){return _},get pointer(){return w},get released(){return A},get taps(){return G},get pointers(){return Array.from(L.values())},on:(e,t,r,o)=>{if(/^(click|pointer|mouse|touch)/.test(e))try{let v=t.split(",").map(y=>`.star-ui ${y.trim()}`).join(", "),h=document.createElement("style");h.textContent=`${v} { pointer-events: auto; }`,document.head.appendChild(h),b.push(()=>h.remove())}catch{}let d=v=>{let y=v.target?.closest?.(t);y&&r.call(y,v)};document.addEventListener(e,d,o);let l=()=>document.removeEventListener(e,d,o);return b.push(l),l},loop:e=>{let r=0,o=!1,d=0,l=v=>{if(!o)return;let h=d?Math.min((v-d)/1e3,.1):0;d=v;try{e(h,v)}catch(y){console.error("[star-canvas] Game loop error:",y)}_=null,A=null,G=[],r=requestAnimationFrame(l)};return S={get running(){return o},start(){o||(o=!0,d=performance.now(),r=requestAnimationFrame(l))},stop(){o&&(o=!1,cancelAnimationFrame(r))}},S.start(),S},ui:{root:s,render:e=>{s.innerHTML!==e&&(s.innerHTML=e)},el:e=>s.querySelector(e),all:e=>s.querySelectorAll(e)},destroy:()=>{S?.stop(),b.forEach(e=>e()),b=[],$.clear(),n.parentElement&&n.parentElement.removeChild(n),s.parentElement&&s.parentElement.removeChild(s),a.parentElement&&a.parentElement.removeChild(a)},scoped:e=>{p.save();try{e()}finally{p.restore()}}};a.addEventListener("pointerdown",e=>{let t=E.toStagePoint(e);if(w={x:t.x,y:t.y,down:!0},L.set(e.pointerId,{x:t.x,y:t.y,id:e.pointerId,down:!0}),R(e))return;_={x:t.x,y:t.y,time:e.timeStamp},G.push({x:t.x,y:t.y,id:e.pointerId,time:e.timeStamp});let r={...t,event:e};x.forEach(o=>o(r))}),a.addEventListener("pointermove",e=>{let t=E.toStagePoint(e);w={x:t.x,y:t.y,down:w.down};let r=L.get(e.pointerId);r&&L.set(e.pointerId,{...r,x:t.x,y:t.y});let o={...t,event:e};I.forEach(d=>d(o))}),a.addEventListener("pointerup",e=>{let t=E.toStagePoint(e);w={x:t.x,y:t.y,down:!1},A={x:t.x,y:t.y,time:e.timeStamp},L.delete(e.pointerId);let r={...t,event:e};k.forEach(o=>o(r))}),window.__STAR_DOM__={destroy:E.destroy},requestAnimationFrame(()=>requestAnimationFrame(()=>{f();try{u(E)}catch(e){console.error("[star-canvas] Game initialization failed:",e)}}))}function oe(u,i={}){let s={preset:"responsive",...i};return j(u,s)}export{U as createDragState,oe as game,ee as version};
|