graphico 0.0.2 → 0.0.4
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/canvas.js +176 -53
- package/package.json +1 -1
- package/types/canvas.d.ts +70 -21
package/dist/canvas.js
CHANGED
|
@@ -13,13 +13,9 @@ var Canvas = /** @class */ (function () {
|
|
|
13
13
|
if (options === void 0) { options = {}; }
|
|
14
14
|
var _this = this;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* Can be used to render 2D graphics onto the canvas
|
|
17
17
|
*/
|
|
18
|
-
this.
|
|
19
|
-
/**
|
|
20
|
-
* Frame counter, increments every frame
|
|
21
|
-
*/
|
|
22
|
-
this.frame = 0;
|
|
18
|
+
this.graphics = [];
|
|
23
19
|
/**
|
|
24
20
|
* Contains a list of current keys pressed
|
|
25
21
|
*/
|
|
@@ -36,41 +32,55 @@ var Canvas = /** @class */ (function () {
|
|
|
36
32
|
* Contains the mouse Y-coordinate, in pixels
|
|
37
33
|
*/
|
|
38
34
|
this.mouseY = 0;
|
|
35
|
+
/**
|
|
36
|
+
* Represents the animation ID handle to cancel the animation
|
|
37
|
+
*/
|
|
38
|
+
this.animation = 0;
|
|
39
|
+
/**
|
|
40
|
+
* The last frame's high resolution timestamp
|
|
41
|
+
*/
|
|
42
|
+
this.lastFrame = 0;
|
|
43
|
+
/**
|
|
44
|
+
* Determine whether the client is focused or not
|
|
45
|
+
*/
|
|
46
|
+
this.focused = false;
|
|
39
47
|
this.config = Canvas.setDefaults(options, Canvas.defaults);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
this.config.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
48
|
+
this.width = this.config.width;
|
|
49
|
+
this.height = this.config.height;
|
|
50
|
+
// Create the container <div> element and set properties
|
|
51
|
+
var container = document.createElement('div');
|
|
52
|
+
container.tabIndex = 1; // For element focus
|
|
53
|
+
container.style.outline = 'none';
|
|
54
|
+
container.style.width = "".concat(this.config.width * this.config.scale, "px");
|
|
55
|
+
container.style.height = "".concat(this.config.height * this.config.scale, "px");
|
|
56
|
+
container.style.border = "".concat(this.config.scale, "px solid ").concat(this.config.border);
|
|
57
|
+
container.style.background = this.config.background;
|
|
58
|
+
container.style.cursor = this.config.showMouse ? 'default' : 'none';
|
|
59
|
+
container.style.position = 'relative';
|
|
60
|
+
this.config.parent.appendChild(container);
|
|
61
|
+
// Create canvas layers
|
|
62
|
+
for (var i = 0; i < this.config.numLayers; i++) {
|
|
63
|
+
var canvas = document.createElement('canvas');
|
|
64
|
+
var graphics = canvas.getContext('2d', { 'willReadFrequently': this.config.acceleration === 'software' });
|
|
65
|
+
if (graphics) {
|
|
66
|
+
this.graphics.push(graphics);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
throw new Error('Could not initialize canvas graphics.');
|
|
70
|
+
}
|
|
71
|
+
// Set properties for canvas
|
|
72
|
+
canvas.style.imageRendering = 'pixelated';
|
|
73
|
+
canvas.style.boxSizing = 'border-box';
|
|
74
|
+
canvas.width = this.config.width;
|
|
75
|
+
canvas.height = this.config.height;
|
|
76
|
+
canvas.style.width = '100%';
|
|
77
|
+
canvas.style.height = '100%';
|
|
78
|
+
canvas.style.position = 'absolute';
|
|
79
|
+
graphics.imageSmoothingEnabled = false;
|
|
80
|
+
container.appendChild(canvas);
|
|
71
81
|
}
|
|
72
82
|
// Event listeners
|
|
73
|
-
|
|
83
|
+
container.addEventListener('mousemove', function (e) {
|
|
74
84
|
if (!_this.focused) {
|
|
75
85
|
return;
|
|
76
86
|
}
|
|
@@ -79,7 +89,7 @@ var Canvas = /** @class */ (function () {
|
|
|
79
89
|
_this.log(e.type, _this.mouseX, _this.mouseY);
|
|
80
90
|
_this.config.mousemove(_this.mouseX, _this.mouseY);
|
|
81
91
|
});
|
|
82
|
-
|
|
92
|
+
container.addEventListener('keydown', function (e) {
|
|
83
93
|
if (!_this.focused) {
|
|
84
94
|
return;
|
|
85
95
|
}
|
|
@@ -91,7 +101,7 @@ var Canvas = /** @class */ (function () {
|
|
|
91
101
|
_this.config.keydown(key);
|
|
92
102
|
}
|
|
93
103
|
});
|
|
94
|
-
|
|
104
|
+
container.addEventListener('keyup', function (e) {
|
|
95
105
|
if (!_this.focused) {
|
|
96
106
|
return;
|
|
97
107
|
}
|
|
@@ -103,7 +113,7 @@ var Canvas = /** @class */ (function () {
|
|
|
103
113
|
_this.config.keyup(key);
|
|
104
114
|
}
|
|
105
115
|
});
|
|
106
|
-
|
|
116
|
+
container.addEventListener('mousedown', function (e) {
|
|
107
117
|
if (!_this.focused) {
|
|
108
118
|
return;
|
|
109
119
|
}
|
|
@@ -114,7 +124,7 @@ var Canvas = /** @class */ (function () {
|
|
|
114
124
|
_this.config.mousedown(button);
|
|
115
125
|
}
|
|
116
126
|
});
|
|
117
|
-
|
|
127
|
+
container.addEventListener('mouseup', function (e) {
|
|
118
128
|
if (!_this.focused) {
|
|
119
129
|
return;
|
|
120
130
|
}
|
|
@@ -126,20 +136,52 @@ var Canvas = /** @class */ (function () {
|
|
|
126
136
|
_this.config.mouseup(button);
|
|
127
137
|
}
|
|
128
138
|
});
|
|
129
|
-
|
|
139
|
+
container.addEventListener('focusin', function (e) {
|
|
130
140
|
_this.focused = true;
|
|
131
|
-
|
|
141
|
+
container.style.borderColor = _this.config.border;
|
|
132
142
|
_this.log(e.type, _this.focused);
|
|
143
|
+
_this.animation = requestAnimationFrame(function (time) { return _this.startAnimate(time); });
|
|
133
144
|
});
|
|
134
|
-
|
|
145
|
+
container.addEventListener('focusout', function (e) {
|
|
135
146
|
_this.focused = false;
|
|
136
|
-
|
|
147
|
+
container.style.borderColor = _this.config.borderBlur;
|
|
137
148
|
_this.log(e.type, _this.focused);
|
|
149
|
+
cancelAnimationFrame(_this.animation);
|
|
150
|
+
});
|
|
151
|
+
window.addEventListener('blur', function (e) {
|
|
152
|
+
_this.log(e.type);
|
|
153
|
+
cancelAnimationFrame(_this.animation);
|
|
138
154
|
});
|
|
139
|
-
|
|
155
|
+
container.addEventListener('contextmenu', function (e) { return e.preventDefault(); });
|
|
140
156
|
// Focus on the canvas
|
|
141
|
-
|
|
157
|
+
container.focus();
|
|
142
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Start the animation.
|
|
161
|
+
*/
|
|
162
|
+
Canvas.prototype.startAnimate = function (time) {
|
|
163
|
+
var _this = this;
|
|
164
|
+
this.log('startAnimate', time);
|
|
165
|
+
this.lastFrame = time;
|
|
166
|
+
this.animation = requestAnimationFrame(function (time) { return _this.animate(time); });
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Run the main animation loop.
|
|
170
|
+
*/
|
|
171
|
+
Canvas.prototype.animate = function (time) {
|
|
172
|
+
var _this = this;
|
|
173
|
+
var _a;
|
|
174
|
+
var currentFrame = time;
|
|
175
|
+
var dt = currentFrame - this.lastFrame;
|
|
176
|
+
this.lastFrame = currentFrame;
|
|
177
|
+
this.log('animate', dt, currentFrame);
|
|
178
|
+
var drawables = (_a = this.config.loop(dt)) !== null && _a !== void 0 ? _a : [];
|
|
179
|
+
for (var _i = 0, drawables_1 = drawables; _i < drawables_1.length; _i++) {
|
|
180
|
+
var drawable = drawables_1[_i];
|
|
181
|
+
this.draw(drawable);
|
|
182
|
+
}
|
|
183
|
+
this.animation = requestAnimationFrame(function (time) { return _this.animate(time); });
|
|
184
|
+
};
|
|
143
185
|
/**
|
|
144
186
|
* Determine whether a key is currently pressed.
|
|
145
187
|
* @param key The key to check
|
|
@@ -156,18 +198,98 @@ var Canvas = /** @class */ (function () {
|
|
|
156
198
|
Canvas.prototype.isMouseButtonDown = function (button) {
|
|
157
199
|
return this.mouseButtons.includes(button);
|
|
158
200
|
};
|
|
201
|
+
/**
|
|
202
|
+
* Get the current cursor position.
|
|
203
|
+
* @returns Cursor position as `[x, y]`
|
|
204
|
+
*/
|
|
205
|
+
Canvas.prototype.getMousePosition = function () {
|
|
206
|
+
return [this.mouseX, this.mouseY];
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Get the color of the selected pixel.
|
|
210
|
+
* @param x The pixel's x-coordinate
|
|
211
|
+
* @param y The pixel's y-coordinate
|
|
212
|
+
* @param layer The zero-indexed layer to get data from
|
|
213
|
+
* @returns `[red, green, blue, alpha]`
|
|
214
|
+
*/
|
|
215
|
+
Canvas.prototype.getPixel = function (x, y, layer) {
|
|
216
|
+
if (layer === void 0) { layer = 0; }
|
|
217
|
+
var data = this.graphics[layer].getImageData(x, y, 1, 1).data;
|
|
218
|
+
this.log(this.graphics[layer].getImageData(x, y, 2, 2));
|
|
219
|
+
;
|
|
220
|
+
return [data[0], data[1], data[2], data[3]];
|
|
221
|
+
};
|
|
222
|
+
/**
|
|
223
|
+
* Set the color of the selected pixel.
|
|
224
|
+
* @param x The pixel's x-coordinate
|
|
225
|
+
* @param y The pixel's y-coordinate
|
|
226
|
+
* @param color `[red, green, blue, alpha]`
|
|
227
|
+
* @param layer The zero-indexed layer to set data to
|
|
228
|
+
*/
|
|
229
|
+
Canvas.prototype.setPixel = function (x, y, color, layer) {
|
|
230
|
+
if (layer === void 0) { layer = 0; }
|
|
231
|
+
var data = this.graphics[layer].getImageData(x, y, 1, 1);
|
|
232
|
+
data.data[0] = color[0];
|
|
233
|
+
data.data[1] = color[1];
|
|
234
|
+
data.data[2] = color[2];
|
|
235
|
+
data.data[3] = color[3];
|
|
236
|
+
this.log(data);
|
|
237
|
+
this.graphics[layer].putImageData(data, x, y);
|
|
238
|
+
};
|
|
239
|
+
/**
|
|
240
|
+
* Take a screenshot of the canvas contents and save to a .png file.
|
|
241
|
+
* @param name The file name of the screenshot
|
|
242
|
+
*/
|
|
243
|
+
Canvas.prototype.screenshot = function (name) {
|
|
244
|
+
if (name === void 0) { name = 'screenshot'; }
|
|
245
|
+
// Create an offscreen canvas
|
|
246
|
+
var screen = new Canvas({
|
|
247
|
+
parent: document.createElement('div'),
|
|
248
|
+
height: this.config.height,
|
|
249
|
+
width: this.config.width,
|
|
250
|
+
scale: this.config.scale,
|
|
251
|
+
});
|
|
252
|
+
// Draw the background and each layer
|
|
253
|
+
screen.graphics[0].fillStyle = this.config.background;
|
|
254
|
+
screen.graphics[0].fillRect(0, 0, screen.width, screen.height);
|
|
255
|
+
for (var _i = 0, _a = this.graphics; _i < _a.length; _i++) {
|
|
256
|
+
var graphic = _a[_i];
|
|
257
|
+
screen.graphics[0].drawImage(graphic.canvas, 0, 0);
|
|
258
|
+
}
|
|
259
|
+
// Generate a data URL and set it as the download parameter for <a>
|
|
260
|
+
var dataURL = screen.graphics[0].canvas.toDataURL();
|
|
261
|
+
var downloadLink = document.createElement('a');
|
|
262
|
+
downloadLink.setAttribute('href', dataURL);
|
|
263
|
+
downloadLink.setAttribute('download', name);
|
|
264
|
+
downloadLink.click();
|
|
265
|
+
// Remove all keys and mouse buttons down because we lose focus
|
|
266
|
+
this.keys.splice(0);
|
|
267
|
+
this.mouseButtons.splice(0);
|
|
268
|
+
};
|
|
159
269
|
/**
|
|
160
270
|
* Draw an object onto the canvas.
|
|
161
271
|
* @param drawable Any drawable object
|
|
272
|
+
* @param layer The zero-indexed layer to draw to
|
|
162
273
|
*/
|
|
163
|
-
Canvas.prototype.draw = function (drawable) {
|
|
164
|
-
|
|
274
|
+
Canvas.prototype.draw = function (drawable, layer) {
|
|
275
|
+
if (layer === void 0) { layer = 0; }
|
|
276
|
+
drawable.draw(this.graphics[layer]);
|
|
165
277
|
};
|
|
166
278
|
/**
|
|
167
279
|
* Completely clears the canvas.
|
|
280
|
+
* @param layer The zero-indexed layer to clear, if unset, will clear all layers
|
|
168
281
|
*/
|
|
169
|
-
Canvas.prototype.clear = function () {
|
|
170
|
-
|
|
282
|
+
Canvas.prototype.clear = function (layer) {
|
|
283
|
+
if (layer === void 0) { layer = -1; }
|
|
284
|
+
if (layer < 0) {
|
|
285
|
+
for (var _i = 0, _a = this.graphics; _i < _a.length; _i++) {
|
|
286
|
+
var graphic = _a[_i];
|
|
287
|
+
graphic.clearRect(0, 0, this.config.width, this.config.height);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
this.graphics[layer].clearRect(0, 0, this.config.width, this.config.height);
|
|
292
|
+
}
|
|
171
293
|
};
|
|
172
294
|
/**
|
|
173
295
|
* Log a message to the debug console.
|
|
@@ -206,7 +328,8 @@ var Canvas = /** @class */ (function () {
|
|
|
206
328
|
border: 'transparent',
|
|
207
329
|
borderBlur: 'transparent',
|
|
208
330
|
showMouse: true,
|
|
209
|
-
|
|
331
|
+
numLayers: 1,
|
|
332
|
+
acceleration: 'hardware',
|
|
210
333
|
keydown: function () { return; },
|
|
211
334
|
keyup: function () { return; },
|
|
212
335
|
mousemove: function () { return; },
|
package/package.json
CHANGED
package/types/canvas.d.ts
CHANGED
|
@@ -14,14 +14,6 @@ export declare class Canvas {
|
|
|
14
14
|
* Can be used to render 2D graphics onto the canvas
|
|
15
15
|
*/
|
|
16
16
|
private readonly graphics;
|
|
17
|
-
/**
|
|
18
|
-
* Whether or not the control is focused
|
|
19
|
-
*/
|
|
20
|
-
private focused;
|
|
21
|
-
/**
|
|
22
|
-
* Frame counter, increments every frame
|
|
23
|
-
*/
|
|
24
|
-
private frame;
|
|
25
17
|
/**
|
|
26
18
|
* Contains a list of current keys pressed
|
|
27
19
|
*/
|
|
@@ -30,6 +22,14 @@ export declare class Canvas {
|
|
|
30
22
|
* Contains a list of current mouse buttons pressed
|
|
31
23
|
*/
|
|
32
24
|
private readonly mouseButtons;
|
|
25
|
+
/**
|
|
26
|
+
* The width of the canvas, in pixels
|
|
27
|
+
*/
|
|
28
|
+
readonly width: number;
|
|
29
|
+
/**
|
|
30
|
+
* The height of the canvas, in pixels
|
|
31
|
+
*/
|
|
32
|
+
readonly height: number;
|
|
33
33
|
/**
|
|
34
34
|
* Contains the mouse X-coordinate, in pixels
|
|
35
35
|
*/
|
|
@@ -38,11 +38,31 @@ export declare class Canvas {
|
|
|
38
38
|
* Contains the mouse Y-coordinate, in pixels
|
|
39
39
|
*/
|
|
40
40
|
private mouseY;
|
|
41
|
+
/**
|
|
42
|
+
* Represents the animation ID handle to cancel the animation
|
|
43
|
+
*/
|
|
44
|
+
private animation;
|
|
45
|
+
/**
|
|
46
|
+
* The last frame's high resolution timestamp
|
|
47
|
+
*/
|
|
48
|
+
private lastFrame;
|
|
49
|
+
/**
|
|
50
|
+
* Determine whether the client is focused or not
|
|
51
|
+
*/
|
|
52
|
+
private focused;
|
|
41
53
|
/**
|
|
42
54
|
* Create a new canvas with the provided options
|
|
43
55
|
* @param options Configuration options
|
|
44
56
|
*/
|
|
45
57
|
constructor(options?: Partial<Options>);
|
|
58
|
+
/**
|
|
59
|
+
* Start the animation.
|
|
60
|
+
*/
|
|
61
|
+
private startAnimate;
|
|
62
|
+
/**
|
|
63
|
+
* Run the main animation loop.
|
|
64
|
+
*/
|
|
65
|
+
private animate;
|
|
46
66
|
/**
|
|
47
67
|
* Determine whether a key is currently pressed.
|
|
48
68
|
* @param key The key to check
|
|
@@ -55,15 +75,43 @@ export declare class Canvas {
|
|
|
55
75
|
* @returns True if `button` is down
|
|
56
76
|
*/
|
|
57
77
|
isMouseButtonDown(button: number): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Get the current cursor position.
|
|
80
|
+
* @returns Cursor position as `[x, y]`
|
|
81
|
+
*/
|
|
82
|
+
getMousePosition(): [number, number];
|
|
83
|
+
/**
|
|
84
|
+
* Get the color of the selected pixel.
|
|
85
|
+
* @param x The pixel's x-coordinate
|
|
86
|
+
* @param y The pixel's y-coordinate
|
|
87
|
+
* @param layer The zero-indexed layer to get data from
|
|
88
|
+
* @returns `[red, green, blue, alpha]`
|
|
89
|
+
*/
|
|
90
|
+
getPixel(x: number, y: number, layer?: number): [number, number, number, number];
|
|
91
|
+
/**
|
|
92
|
+
* Set the color of the selected pixel.
|
|
93
|
+
* @param x The pixel's x-coordinate
|
|
94
|
+
* @param y The pixel's y-coordinate
|
|
95
|
+
* @param color `[red, green, blue, alpha]`
|
|
96
|
+
* @param layer The zero-indexed layer to set data to
|
|
97
|
+
*/
|
|
98
|
+
setPixel(x: number, y: number, color: [number, number, number, number], layer?: number): void;
|
|
99
|
+
/**
|
|
100
|
+
* Take a screenshot of the canvas contents and save to a .png file.
|
|
101
|
+
* @param name The file name of the screenshot
|
|
102
|
+
*/
|
|
103
|
+
screenshot(name?: string): void;
|
|
58
104
|
/**
|
|
59
105
|
* Draw an object onto the canvas.
|
|
60
106
|
* @param drawable Any drawable object
|
|
107
|
+
* @param layer The zero-indexed layer to draw to
|
|
61
108
|
*/
|
|
62
|
-
draw(drawable: Drawable): void;
|
|
109
|
+
draw(drawable: Drawable, layer?: number): void;
|
|
63
110
|
/**
|
|
64
111
|
* Completely clears the canvas.
|
|
112
|
+
* @param layer The zero-indexed layer to clear, if unset, will clear all layers
|
|
65
113
|
*/
|
|
66
|
-
clear(): void;
|
|
114
|
+
clear(layer?: number): void;
|
|
67
115
|
/**
|
|
68
116
|
* Log a message to the debug console.
|
|
69
117
|
*/
|
|
@@ -114,9 +162,15 @@ export interface Options {
|
|
|
114
162
|
*/
|
|
115
163
|
readonly showMouse: boolean;
|
|
116
164
|
/**
|
|
117
|
-
* The number of
|
|
165
|
+
* The number of layers in this canvas
|
|
118
166
|
*/
|
|
119
|
-
readonly
|
|
167
|
+
readonly numLayers: number;
|
|
168
|
+
/**
|
|
169
|
+
* Uses hardware (GPU) or software (CPU) acceleration
|
|
170
|
+
* - For pixel manipulation, software acceleration is recommended
|
|
171
|
+
* - Otherwise, hardware acceleration is recommended (default)
|
|
172
|
+
*/
|
|
173
|
+
readonly acceleration: 'hardware' | 'software';
|
|
120
174
|
/**
|
|
121
175
|
* Event listener for when a key is pressed
|
|
122
176
|
* @param key The key that was pressed
|
|
@@ -144,16 +198,11 @@ export interface Options {
|
|
|
144
198
|
*/
|
|
145
199
|
readonly mouseup: (button: number) => void;
|
|
146
200
|
/**
|
|
147
|
-
* Event listener for a the main loop
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
* @param frame The frame sequence number
|
|
151
|
-
* @param keys A list of keys that are currently pressed
|
|
152
|
-
* @param mouseButtons A list of buttons that are currently pressed
|
|
153
|
-
* @param x Cursor X-coordinate
|
|
154
|
-
* @param y Cursor Y-coordinate
|
|
201
|
+
* Event listener for a the main animation loop
|
|
202
|
+
* @param dt The number of milliseconds in between frames
|
|
203
|
+
* @returns An array of `Drawable` to render on layer 0, or void
|
|
155
204
|
*/
|
|
156
|
-
readonly loop: (
|
|
205
|
+
readonly loop: (dt: number) => Drawable[] | (void | never);
|
|
157
206
|
}
|
|
158
207
|
/**
|
|
159
208
|
* Represents an object that can be drawn on the canvas.
|