@guinetik/gcanvas 1.0.1 → 1.0.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/demos/coordinates.html +698 -0
- package/demos/cube3d.html +23 -0
- package/demos/demos.css +17 -3
- package/demos/dino.html +42 -0
- package/demos/gameobjects.html +626 -0
- package/demos/index.html +17 -7
- package/demos/js/coordinates.js +840 -0
- package/demos/js/cube3d.js +789 -0
- package/demos/js/dino.js +1420 -0
- package/demos/js/gameobjects.js +176 -0
- package/demos/js/plane3d.js +256 -0
- package/demos/js/platformer.js +1579 -0
- package/demos/js/sphere3d.js +229 -0
- package/demos/js/sprite.js +473 -0
- package/demos/js/tde/accretiondisk.js +3 -3
- package/demos/js/tde/tidalstream.js +2 -2
- package/demos/plane3d.html +24 -0
- package/demos/platformer.html +43 -0
- package/demos/sphere3d.html +24 -0
- package/demos/sprite.html +18 -0
- package/docs/concepts/coordinate-system.md +384 -0
- package/docs/concepts/shapes-vs-gameobjects.md +187 -0
- package/docs/fluid-dynamics.md +99 -97
- package/package.json +1 -1
- package/src/game/game.js +11 -5
- package/src/game/objects/index.js +3 -0
- package/src/game/objects/platformer-scene.js +411 -0
- package/src/game/objects/scene.js +14 -0
- package/src/game/objects/sprite.js +529 -0
- package/src/game/pipeline.js +20 -16
- package/src/game/ui/theme.js +123 -121
- package/src/io/input.js +75 -45
- package/src/io/mouse.js +44 -19
- package/src/io/touch.js +35 -12
- package/src/shapes/cube3d.js +599 -0
- package/src/shapes/index.js +2 -0
- package/src/shapes/plane3d.js +687 -0
- package/src/shapes/sphere3d.js +75 -6
- package/src/util/camera2d.js +315 -0
- package/src/util/index.js +1 -0
- package/src/webgl/shaders/plane-shaders.js +332 -0
- package/src/webgl/shaders/sphere-shaders.js +4 -2
package/src/game/ui/theme.js
CHANGED
|
@@ -1,121 +1,123 @@
|
|
|
1
|
-
/***************************************************************
|
|
2
|
-
* theme.js
|
|
3
|
-
*
|
|
4
|
-
* Terminal × Vercel design system for GCanvas UI components.
|
|
5
|
-
*
|
|
6
|
-
* Aesthetic principles:
|
|
7
|
-
* - Dark translucent backgrounds (depth, layering)
|
|
8
|
-
* - Neon green (#0f0) as primary accent
|
|
9
|
-
* - Monospace typography throughout
|
|
10
|
-
* - Inverted colors on hover for clear feedback
|
|
11
|
-
* - Minimal, clean lines inspired by terminal UIs
|
|
12
|
-
***************************************************************/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Core theme colors and values for UI components.
|
|
16
|
-
* Import this for consistent styling across custom UI elements.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```js
|
|
20
|
-
* import { UI_THEME } from "gcanvas";
|
|
21
|
-
*
|
|
22
|
-
* const myButton = new Button(game, {
|
|
23
|
-
* colorDefaultBg: UI_THEME.colors.darkBg,
|
|
24
|
-
* colorDefaultStroke: UI_THEME.colors.neonGreen,
|
|
25
|
-
* colorDefaultText: UI_THEME.colors.neonGreen,
|
|
26
|
-
* });
|
|
27
|
-
* ```
|
|
28
|
-
*
|
|
29
|
-
* @constant {Object}
|
|
30
|
-
*/
|
|
31
|
-
export const UI_THEME = {
|
|
32
|
-
/**
|
|
33
|
-
* Color palette
|
|
34
|
-
*/
|
|
35
|
-
colors: {
|
|
36
|
-
// Primary accent - terminal green
|
|
37
|
-
neonGreen: "#0f0",
|
|
38
|
-
terminalGreen: "#16F529",
|
|
39
|
-
|
|
40
|
-
// Secondary accent - cyan
|
|
41
|
-
cyanAccent: "#0ff",
|
|
42
|
-
|
|
43
|
-
// Backgrounds
|
|
44
|
-
darkBg: "rgba(0, 0, 0, 0.85)",
|
|
45
|
-
darkerBg: "rgba(0, 0, 0, 0.92)",
|
|
46
|
-
hoverBg: "#0f0",
|
|
47
|
-
pressedBg: "#0c0",
|
|
48
|
-
activeBg: "rgba(0, 255, 0, 0.15)",
|
|
49
|
-
|
|
50
|
-
// Text
|
|
51
|
-
lightText: "#0f0",
|
|
52
|
-
darkText: "#000",
|
|
53
|
-
dimText: "rgba(0, 255, 0, 0.7)",
|
|
54
|
-
|
|
55
|
-
// Borders
|
|
56
|
-
subtleBorder: "rgba(0, 255, 0, 0.4)",
|
|
57
|
-
activeBorder: "#0f0",
|
|
58
|
-
glowBorder: "rgba(0, 255, 0, 0.5)",
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Typography
|
|
63
|
-
*/
|
|
64
|
-
fonts: {
|
|
65
|
-
primary: "monospace",
|
|
66
|
-
small: "11px monospace",
|
|
67
|
-
medium: "14px monospace",
|
|
68
|
-
large: "18px monospace",
|
|
69
|
-
heading: "bold 24px monospace",
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Spacing values
|
|
74
|
-
*/
|
|
75
|
-
spacing: {
|
|
76
|
-
xs: 4,
|
|
77
|
-
sm: 8,
|
|
78
|
-
md: 12,
|
|
79
|
-
lg: 16,
|
|
80
|
-
xl: 24,
|
|
81
|
-
},
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Pre-configured button color schemes
|
|
85
|
-
*/
|
|
86
|
-
button: {
|
|
87
|
-
default: {
|
|
88
|
-
bg: "rgba(0, 0, 0, 0.85)",
|
|
89
|
-
stroke: "rgba(0, 255, 0, 0.4)",
|
|
90
|
-
text: "#0f0",
|
|
91
|
-
},
|
|
92
|
-
hover: {
|
|
93
|
-
bg: "#0f0",
|
|
94
|
-
stroke: "#0f0",
|
|
95
|
-
text: "#000",
|
|
96
|
-
},
|
|
97
|
-
pressed: {
|
|
98
|
-
bg: "#0c0",
|
|
99
|
-
stroke: "#0f0",
|
|
100
|
-
text: "#000",
|
|
101
|
-
},
|
|
102
|
-
active: {
|
|
103
|
-
bg: "rgba(0, 255, 0, 0.15)",
|
|
104
|
-
stroke: "#0f0",
|
|
105
|
-
text: "#0f0",
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Pre-configured tooltip styles
|
|
111
|
-
*/
|
|
112
|
-
tooltip: {
|
|
113
|
-
bg: "rgba(0, 0, 0, 0.92)",
|
|
114
|
-
border: "rgba(0, 255, 0, 0.5)",
|
|
115
|
-
text: "#0f0",
|
|
116
|
-
},
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
export default UI_THEME;
|
|
120
|
-
|
|
121
|
-
|
|
1
|
+
/***************************************************************
|
|
2
|
+
* theme.js
|
|
3
|
+
*
|
|
4
|
+
* Terminal × Vercel design system for GCanvas UI components.
|
|
5
|
+
*
|
|
6
|
+
* Aesthetic principles:
|
|
7
|
+
* - Dark translucent backgrounds (depth, layering)
|
|
8
|
+
* - Neon green (#0f0) as primary accent
|
|
9
|
+
* - Monospace typography throughout
|
|
10
|
+
* - Inverted colors on hover for clear feedback
|
|
11
|
+
* - Minimal, clean lines inspired by terminal UIs
|
|
12
|
+
***************************************************************/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Core theme colors and values for UI components.
|
|
16
|
+
* Import this for consistent styling across custom UI elements.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```js
|
|
20
|
+
* import { UI_THEME } from "gcanvas";
|
|
21
|
+
*
|
|
22
|
+
* const myButton = new Button(game, {
|
|
23
|
+
* colorDefaultBg: UI_THEME.colors.darkBg,
|
|
24
|
+
* colorDefaultStroke: UI_THEME.colors.neonGreen,
|
|
25
|
+
* colorDefaultText: UI_THEME.colors.neonGreen,
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @constant {Object}
|
|
30
|
+
*/
|
|
31
|
+
export const UI_THEME = {
|
|
32
|
+
/**
|
|
33
|
+
* Color palette
|
|
34
|
+
*/
|
|
35
|
+
colors: {
|
|
36
|
+
// Primary accent - terminal green
|
|
37
|
+
neonGreen: "#0f0",
|
|
38
|
+
terminalGreen: "#16F529",
|
|
39
|
+
|
|
40
|
+
// Secondary accent - cyan
|
|
41
|
+
cyanAccent: "#0ff",
|
|
42
|
+
|
|
43
|
+
// Backgrounds
|
|
44
|
+
darkBg: "rgba(0, 0, 0, 0.85)",
|
|
45
|
+
darkerBg: "rgba(0, 0, 0, 0.92)",
|
|
46
|
+
hoverBg: "#0f0",
|
|
47
|
+
pressedBg: "#0c0",
|
|
48
|
+
activeBg: "rgba(0, 255, 0, 0.15)",
|
|
49
|
+
|
|
50
|
+
// Text
|
|
51
|
+
lightText: "#0f0",
|
|
52
|
+
darkText: "#000",
|
|
53
|
+
dimText: "rgba(0, 255, 0, 0.7)",
|
|
54
|
+
|
|
55
|
+
// Borders
|
|
56
|
+
subtleBorder: "rgba(0, 255, 0, 0.4)",
|
|
57
|
+
activeBorder: "#0f0",
|
|
58
|
+
glowBorder: "rgba(0, 255, 0, 0.5)",
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Typography
|
|
63
|
+
*/
|
|
64
|
+
fonts: {
|
|
65
|
+
primary: "monospace",
|
|
66
|
+
small: "11px monospace",
|
|
67
|
+
medium: "14px monospace",
|
|
68
|
+
large: "18px monospace",
|
|
69
|
+
heading: "bold 24px monospace",
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Spacing values
|
|
74
|
+
*/
|
|
75
|
+
spacing: {
|
|
76
|
+
xs: 4,
|
|
77
|
+
sm: 8,
|
|
78
|
+
md: 12,
|
|
79
|
+
lg: 16,
|
|
80
|
+
xl: 24,
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Pre-configured button color schemes
|
|
85
|
+
*/
|
|
86
|
+
button: {
|
|
87
|
+
default: {
|
|
88
|
+
bg: "rgba(0, 0, 0, 0.85)",
|
|
89
|
+
stroke: "rgba(0, 255, 0, 0.4)",
|
|
90
|
+
text: "#0f0",
|
|
91
|
+
},
|
|
92
|
+
hover: {
|
|
93
|
+
bg: "#0f0",
|
|
94
|
+
stroke: "#0f0",
|
|
95
|
+
text: "#000",
|
|
96
|
+
},
|
|
97
|
+
pressed: {
|
|
98
|
+
bg: "#0c0",
|
|
99
|
+
stroke: "#0f0",
|
|
100
|
+
text: "#000",
|
|
101
|
+
},
|
|
102
|
+
active: {
|
|
103
|
+
bg: "rgba(0, 255, 0, 0.15)",
|
|
104
|
+
stroke: "#0f0",
|
|
105
|
+
text: "#0f0",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Pre-configured tooltip styles
|
|
111
|
+
*/
|
|
112
|
+
tooltip: {
|
|
113
|
+
bg: "rgba(0, 0, 0, 0.92)",
|
|
114
|
+
border: "rgba(0, 255, 0, 0.5)",
|
|
115
|
+
text: "#0f0",
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export default UI_THEME;
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
package/src/io/input.js
CHANGED
|
@@ -1,15 +1,38 @@
|
|
|
1
1
|
export class Input {
|
|
2
2
|
static init(game) {
|
|
3
|
+
// Set defaults for last initialized game (backwards compatibility)
|
|
3
4
|
Input.game = game;
|
|
4
5
|
Input.x = 0;
|
|
5
6
|
Input.y = 0;
|
|
6
7
|
Input.down = false;
|
|
7
|
-
|
|
8
|
-
game
|
|
9
|
-
game.events.on("
|
|
10
|
-
game.events.on("
|
|
11
|
-
game.events.on("
|
|
12
|
-
game.events.on("
|
|
8
|
+
|
|
9
|
+
// Create bound handlers that know which game they belong to
|
|
10
|
+
game.events.on("mousedown", (e) => Input._onDown(e, game));
|
|
11
|
+
game.events.on("mouseup", (e) => Input._onUp(e, game));
|
|
12
|
+
game.events.on("mousemove", (e) => Input._onMove(e, game));
|
|
13
|
+
game.events.on("touchstart", (e) => Input._onTouchStart(e, game));
|
|
14
|
+
game.events.on("touchend", (e) => Input._onTouchEnd(e, game));
|
|
15
|
+
game.events.on("touchmove", (e) => Input._onTouchMove(e, game));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Scales CSS pixel coordinates to canvas internal coordinates.
|
|
20
|
+
* This handles the case where the canvas is CSS-scaled (display size differs from resolution).
|
|
21
|
+
* @param {Object} game - The game instance
|
|
22
|
+
* @param {number} cssX - X coordinate in CSS pixels
|
|
23
|
+
* @param {number} cssY - Y coordinate in CSS pixels
|
|
24
|
+
* @returns {{x: number, y: number}} Scaled coordinates in canvas pixels
|
|
25
|
+
*/
|
|
26
|
+
static _scaleToCanvas(game, cssX, cssY) {
|
|
27
|
+
const canvas = game.canvas;
|
|
28
|
+
const rect = canvas.getBoundingClientRect();
|
|
29
|
+
// Scale from CSS pixels to canvas internal pixels
|
|
30
|
+
const scaleX = canvas.width / rect.width;
|
|
31
|
+
const scaleY = canvas.height / rect.height;
|
|
32
|
+
return {
|
|
33
|
+
x: cssX * scaleX,
|
|
34
|
+
y: cssY * scaleY
|
|
35
|
+
};
|
|
13
36
|
}
|
|
14
37
|
|
|
15
38
|
static _setPosition(x, y) {
|
|
@@ -17,54 +40,61 @@ export class Input {
|
|
|
17
40
|
Input.y = y;
|
|
18
41
|
}
|
|
19
42
|
|
|
20
|
-
static _onDown
|
|
43
|
+
static _onDown(e, game) {
|
|
21
44
|
Input.down = true;
|
|
22
|
-
Input.
|
|
23
|
-
|
|
24
|
-
Object.defineProperty(e, "
|
|
25
|
-
|
|
26
|
-
|
|
45
|
+
const scaled = Input._scaleToCanvas(game, e.offsetX, e.offsetY);
|
|
46
|
+
Input._setPosition(scaled.x, scaled.y);
|
|
47
|
+
Object.defineProperty(e, "x", { value: scaled.x, configurable: true });
|
|
48
|
+
Object.defineProperty(e, "y", { value: scaled.y, configurable: true });
|
|
49
|
+
game.events.emit("inputdown", e);
|
|
50
|
+
}
|
|
27
51
|
|
|
28
|
-
static _onUp
|
|
52
|
+
static _onUp(e, game) {
|
|
29
53
|
Input.down = false;
|
|
30
|
-
Input.
|
|
31
|
-
|
|
32
|
-
Object.defineProperty(e, "
|
|
33
|
-
|
|
34
|
-
|
|
54
|
+
const scaled = Input._scaleToCanvas(game, e.offsetX, e.offsetY);
|
|
55
|
+
Input._setPosition(scaled.x, scaled.y);
|
|
56
|
+
Object.defineProperty(e, "x", { value: scaled.x, configurable: true });
|
|
57
|
+
Object.defineProperty(e, "y", { value: scaled.y, configurable: true });
|
|
58
|
+
game.events.emit("inputup", e);
|
|
59
|
+
}
|
|
35
60
|
|
|
36
|
-
static _onMove
|
|
37
|
-
Input.
|
|
38
|
-
|
|
39
|
-
Object.defineProperty(e, "
|
|
40
|
-
|
|
41
|
-
|
|
61
|
+
static _onMove(e, game) {
|
|
62
|
+
const scaled = Input._scaleToCanvas(game, e.offsetX, e.offsetY);
|
|
63
|
+
Input._setPosition(scaled.x, scaled.y);
|
|
64
|
+
Object.defineProperty(e, "x", { value: scaled.x, configurable: true });
|
|
65
|
+
Object.defineProperty(e, "y", { value: scaled.y, configurable: true });
|
|
66
|
+
game.events.emit("inputmove", e);
|
|
67
|
+
}
|
|
42
68
|
|
|
43
|
-
static _onTouchStart
|
|
69
|
+
static _onTouchStart(e, game) {
|
|
44
70
|
const touch = e.touches[0];
|
|
45
|
-
const rect =
|
|
71
|
+
const rect = game.canvas.getBoundingClientRect();
|
|
46
72
|
Input.down = true;
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
73
|
+
// Calculate CSS-relative position, then scale to canvas coordinates
|
|
74
|
+
const cssX = touch.clientX - rect.left;
|
|
75
|
+
const cssY = touch.clientY - rect.top;
|
|
76
|
+
const scaled = Input._scaleToCanvas(game, cssX, cssY);
|
|
77
|
+
Input._setPosition(scaled.x, scaled.y);
|
|
78
|
+
Object.defineProperty(e, "x", { value: scaled.x, configurable: true });
|
|
79
|
+
Object.defineProperty(e, "y", { value: scaled.y, configurable: true });
|
|
80
|
+
game.events.emit("inputdown", e);
|
|
81
|
+
}
|
|
54
82
|
|
|
55
|
-
static _onTouchEnd
|
|
83
|
+
static _onTouchEnd(e, game) {
|
|
56
84
|
Input.down = false;
|
|
57
|
-
|
|
58
|
-
}
|
|
85
|
+
game.events.emit("inputup", e);
|
|
86
|
+
}
|
|
59
87
|
|
|
60
|
-
static _onTouchMove
|
|
88
|
+
static _onTouchMove(e, game) {
|
|
61
89
|
const touch = e.touches[0];
|
|
62
|
-
const rect =
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
90
|
+
const rect = game.canvas.getBoundingClientRect();
|
|
91
|
+
// Calculate CSS-relative position, then scale to canvas coordinates
|
|
92
|
+
const cssX = touch.clientX - rect.left;
|
|
93
|
+
const cssY = touch.clientY - rect.top;
|
|
94
|
+
const scaled = Input._scaleToCanvas(game, cssX, cssY);
|
|
95
|
+
Input._setPosition(scaled.x, scaled.y);
|
|
96
|
+
Object.defineProperty(e, "x", { value: scaled.x, configurable: true });
|
|
97
|
+
Object.defineProperty(e, "y", { value: scaled.y, configurable: true });
|
|
98
|
+
game.events.emit("inputmove", e);
|
|
99
|
+
}
|
|
70
100
|
}
|
package/src/io/mouse.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
export class Mouse {
|
|
2
|
+
// Map of canvas -> game for multi-game support
|
|
3
|
+
static _gameMap = new Map();
|
|
4
|
+
|
|
2
5
|
static init(game) {
|
|
6
|
+
// Store mapping from canvas to game
|
|
7
|
+
Mouse._gameMap.set(game.canvas, game);
|
|
8
|
+
|
|
9
|
+
// Set defaults for last initialized game (backwards compatibility)
|
|
3
10
|
Mouse.game = game;
|
|
4
11
|
Mouse.canvas = game.canvas;
|
|
5
12
|
Mouse.x = 0;
|
|
@@ -8,42 +15,59 @@ export class Mouse {
|
|
|
8
15
|
Mouse.middleDown = false;
|
|
9
16
|
Mouse.rightDown = false;
|
|
10
17
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
game.canvas.addEventListener("mousemove", Mouse._onMove);
|
|
19
|
+
game.canvas.addEventListener("mousedown", Mouse._onDown);
|
|
20
|
+
game.canvas.addEventListener("mouseup", Mouse._onUp);
|
|
21
|
+
game.canvas.addEventListener("click", Mouse._onClick);
|
|
22
|
+
game.canvas.addEventListener("wheel", Mouse._onWheel);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Get the game instance for a given event's target canvas
|
|
26
|
+
static _getGameForEvent(e) {
|
|
27
|
+
const canvas = e.currentTarget;
|
|
28
|
+
return Mouse._gameMap.get(canvas) || Mouse.game;
|
|
16
29
|
}
|
|
17
30
|
|
|
18
|
-
static _updatePosition(e) {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
31
|
+
static _updatePosition(e, game) {
|
|
32
|
+
const canvas = game.canvas;
|
|
33
|
+
const rect = canvas.getBoundingClientRect();
|
|
34
|
+
// Calculate CSS-relative position
|
|
35
|
+
const cssX = e.clientX - rect.left;
|
|
36
|
+
const cssY = e.clientY - rect.top;
|
|
37
|
+
// Scale to canvas internal coordinates (handles CSS scaling)
|
|
38
|
+
const scaleX = canvas.width / rect.width;
|
|
39
|
+
const scaleY = canvas.height / rect.height;
|
|
40
|
+
Mouse.x = cssX * scaleX;
|
|
41
|
+
Mouse.y = cssY * scaleY;
|
|
22
42
|
}
|
|
23
43
|
|
|
24
44
|
static _onMove = (e) => {
|
|
25
|
-
Mouse.
|
|
26
|
-
Mouse.
|
|
45
|
+
const game = Mouse._getGameForEvent(e);
|
|
46
|
+
Mouse._updatePosition(e, game);
|
|
47
|
+
game.events.emit("mousemove", e);
|
|
27
48
|
};
|
|
28
49
|
|
|
29
50
|
static _onDown = (e) => {
|
|
30
|
-
Mouse.
|
|
51
|
+
const game = Mouse._getGameForEvent(e);
|
|
52
|
+
Mouse._updatePosition(e, game);
|
|
31
53
|
if (e.button === 0) Mouse.leftDown = true;
|
|
32
54
|
if (e.button === 1) Mouse.middleDown = true;
|
|
33
55
|
if (e.button === 2) Mouse.rightDown = true;
|
|
34
|
-
|
|
56
|
+
game.events.emit("mousedown", e);
|
|
35
57
|
};
|
|
36
58
|
|
|
37
59
|
static _onUp = (e) => {
|
|
38
|
-
Mouse.
|
|
60
|
+
const game = Mouse._getGameForEvent(e);
|
|
61
|
+
Mouse._updatePosition(e, game);
|
|
39
62
|
if (e.button === 0) Mouse.leftDown = false;
|
|
40
63
|
if (e.button === 1) Mouse.middleDown = false;
|
|
41
64
|
if (e.button === 2) Mouse.rightDown = false;
|
|
42
|
-
|
|
65
|
+
game.events.emit("mouseup", e);
|
|
43
66
|
};
|
|
44
67
|
|
|
45
68
|
static _onClick = (e) => {
|
|
46
|
-
Mouse.
|
|
69
|
+
const game = Mouse._getGameForEvent(e);
|
|
70
|
+
Mouse._updatePosition(e, game);
|
|
47
71
|
// Emit enhanced event with canvas-relative coordinates
|
|
48
72
|
// Note: e is a MouseEvent, add canvas-relative x/y directly
|
|
49
73
|
e.canvasX = Mouse.x;
|
|
@@ -51,11 +75,12 @@ export class Mouse {
|
|
|
51
75
|
// Also set x/y for convenience (matches expected fluent API)
|
|
52
76
|
Object.defineProperty(e, 'x', { value: Mouse.x, writable: false });
|
|
53
77
|
Object.defineProperty(e, 'y', { value: Mouse.y, writable: false });
|
|
54
|
-
|
|
78
|
+
game.events.emit("click", e);
|
|
55
79
|
};
|
|
56
80
|
|
|
57
81
|
static _onWheel = (e) => {
|
|
58
|
-
Mouse.
|
|
59
|
-
Mouse.
|
|
82
|
+
const game = Mouse._getGameForEvent(e);
|
|
83
|
+
Mouse._updatePosition(e, game);
|
|
84
|
+
game.events.emit("wheel", e);
|
|
60
85
|
};
|
|
61
86
|
}
|
package/src/io/touch.js
CHANGED
|
@@ -1,39 +1,62 @@
|
|
|
1
1
|
export class Touch {
|
|
2
|
+
// Map of canvas -> game for multi-game support
|
|
3
|
+
static _gameMap = new Map();
|
|
4
|
+
|
|
2
5
|
static init(game) {
|
|
6
|
+
// Store mapping from canvas to game
|
|
7
|
+
Touch._gameMap.set(game.canvas, game);
|
|
8
|
+
|
|
9
|
+
// Set defaults for last initialized game (backwards compatibility)
|
|
3
10
|
Touch.game = game;
|
|
4
11
|
Touch.canvas = game.canvas;
|
|
5
12
|
Touch.x = 0;
|
|
6
13
|
Touch.y = 0;
|
|
7
14
|
Touch.active = false;
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
16
|
+
game.canvas.addEventListener("touchstart", Touch._onStart);
|
|
17
|
+
game.canvas.addEventListener("touchend", Touch._onEnd);
|
|
18
|
+
game.canvas.addEventListener("touchmove", Touch._onMove);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Get the game instance for a given event's target canvas
|
|
22
|
+
static _getGameForEvent(e) {
|
|
23
|
+
const canvas = e.currentTarget;
|
|
24
|
+
return Touch._gameMap.get(canvas) || Touch.game;
|
|
12
25
|
}
|
|
13
26
|
|
|
14
|
-
static _updatePosition(touch) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
static _updatePosition(touch, game) {
|
|
28
|
+
const canvas = game.canvas;
|
|
29
|
+
const rect = canvas.getBoundingClientRect();
|
|
30
|
+
// Calculate CSS-relative position
|
|
31
|
+
const cssX = touch.clientX - rect.left;
|
|
32
|
+
const cssY = touch.clientY - rect.top;
|
|
33
|
+
// Scale to canvas internal coordinates (handles CSS scaling)
|
|
34
|
+
const scaleX = canvas.width / rect.width;
|
|
35
|
+
const scaleY = canvas.height / rect.height;
|
|
36
|
+
Touch.x = cssX * scaleX;
|
|
37
|
+
Touch.y = cssY * scaleY;
|
|
18
38
|
}
|
|
19
39
|
|
|
20
40
|
static _onStart = (e) => {
|
|
21
41
|
if (e.touches.length > 0) {
|
|
42
|
+
const game = Touch._getGameForEvent(e);
|
|
22
43
|
Touch.active = true;
|
|
23
|
-
Touch._updatePosition(e.touches[0]);
|
|
24
|
-
|
|
44
|
+
Touch._updatePosition(e.touches[0], game);
|
|
45
|
+
game.events.emit("touchstart", e);
|
|
25
46
|
}
|
|
26
47
|
};
|
|
27
48
|
|
|
28
49
|
static _onEnd = (e) => {
|
|
50
|
+
const game = Touch._getGameForEvent(e);
|
|
29
51
|
Touch.active = false;
|
|
30
|
-
|
|
52
|
+
game.events.emit("touchend", e);
|
|
31
53
|
};
|
|
32
54
|
|
|
33
55
|
static _onMove = (e) => {
|
|
34
56
|
if (e.touches.length > 0) {
|
|
35
|
-
Touch.
|
|
36
|
-
Touch.
|
|
57
|
+
const game = Touch._getGameForEvent(e);
|
|
58
|
+
Touch._updatePosition(e.touches[0], game);
|
|
59
|
+
game.events.emit("touchmove", e);
|
|
37
60
|
}
|
|
38
61
|
};
|
|
39
62
|
}
|