graphico 1.2.0 → 1.3.0
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 +255 -282
- package/dist/index.js +2 -18
- package/dist/types.js +1 -2
- package/package.json +3 -2
- package/types/canvas.d.ts +3 -3
- package/types/index.d.ts +2 -2
- package/types/types.d.ts +0 -4
package/dist/canvas.js
CHANGED
|
@@ -1,79 +1,117 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Canvas = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* Represents a canvas for drawing and animating
|
|
6
3
|
*/
|
|
7
|
-
|
|
4
|
+
export class Canvas {
|
|
5
|
+
/**
|
|
6
|
+
* Default canvas options
|
|
7
|
+
*/
|
|
8
|
+
static defaults = {
|
|
9
|
+
debug: false,
|
|
10
|
+
parent: document.body,
|
|
11
|
+
width: 600,
|
|
12
|
+
height: 400,
|
|
13
|
+
scale: 1,
|
|
14
|
+
background: 'white',
|
|
15
|
+
border: 'transparent',
|
|
16
|
+
borderBlur: 'transparent',
|
|
17
|
+
showMouse: true,
|
|
18
|
+
keepFocused: false,
|
|
19
|
+
numLayers: 1,
|
|
20
|
+
numTracks: 1,
|
|
21
|
+
keydown() { return; },
|
|
22
|
+
keyup() { return; },
|
|
23
|
+
mousemove() { return; },
|
|
24
|
+
mousedown() { return; },
|
|
25
|
+
mouseup() { return; },
|
|
26
|
+
loop() { return; },
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Configuration options for this canvas
|
|
30
|
+
*/
|
|
31
|
+
config;
|
|
32
|
+
/**
|
|
33
|
+
* Used to render 2D objects onto the main canvas
|
|
34
|
+
*/
|
|
35
|
+
graphic;
|
|
36
|
+
/**
|
|
37
|
+
* Can be used to render 2D graphics onto layers of the canvas
|
|
38
|
+
*/
|
|
39
|
+
layers = [];
|
|
40
|
+
/**
|
|
41
|
+
* Contains a list of current keys pressed
|
|
42
|
+
*/
|
|
43
|
+
keys = [];
|
|
44
|
+
/**
|
|
45
|
+
* Contains a list of current mouse buttons pressed
|
|
46
|
+
*/
|
|
47
|
+
mouseButtons = [];
|
|
48
|
+
/**
|
|
49
|
+
* The width of the canvas, in pixels
|
|
50
|
+
*/
|
|
51
|
+
width;
|
|
52
|
+
/**
|
|
53
|
+
* The height of the canvas, in pixels
|
|
54
|
+
*/
|
|
55
|
+
height;
|
|
56
|
+
/**
|
|
57
|
+
* Contains the mouse X-coordinate, in pixels
|
|
58
|
+
*/
|
|
59
|
+
mouseX = 0;
|
|
60
|
+
/**
|
|
61
|
+
* Contains the mouse Y-coordinate, in pixels
|
|
62
|
+
*/
|
|
63
|
+
mouseY = 0;
|
|
64
|
+
/**
|
|
65
|
+
* Represents the animation ID handle to cancel the animation
|
|
66
|
+
*/
|
|
67
|
+
animation = 0;
|
|
68
|
+
/**
|
|
69
|
+
* The last frame's high resolution timestamp
|
|
70
|
+
*/
|
|
71
|
+
lastFrame = 0;
|
|
72
|
+
/**
|
|
73
|
+
* Determine whether the client is focused or not
|
|
74
|
+
*/
|
|
75
|
+
focused = false;
|
|
76
|
+
/**
|
|
77
|
+
* Determine whether the user has interacted with this canvas
|
|
78
|
+
*/
|
|
79
|
+
interacted = false;
|
|
80
|
+
/**
|
|
81
|
+
* The media recording object for screen captures
|
|
82
|
+
*/
|
|
83
|
+
recorder;
|
|
84
|
+
/**
|
|
85
|
+
* Determine whether audio is allowed or muted
|
|
86
|
+
*/
|
|
87
|
+
muted = false;
|
|
88
|
+
/**
|
|
89
|
+
* Contains the list of all active audio elements
|
|
90
|
+
*/
|
|
91
|
+
audios = [];
|
|
8
92
|
/**
|
|
9
93
|
* Create a new canvas with the provided options
|
|
10
94
|
* @param options Configuration options
|
|
11
95
|
*/
|
|
12
|
-
|
|
13
|
-
if (options === void 0) { options = {}; }
|
|
14
|
-
var _this = this;
|
|
15
|
-
/**
|
|
16
|
-
* Can be used to render 2D graphics onto layers of the canvas
|
|
17
|
-
*/
|
|
18
|
-
this.layers = [];
|
|
19
|
-
/**
|
|
20
|
-
* Contains a list of current keys pressed
|
|
21
|
-
*/
|
|
22
|
-
this.keys = [];
|
|
23
|
-
/**
|
|
24
|
-
* Contains a list of current mouse buttons pressed
|
|
25
|
-
*/
|
|
26
|
-
this.mouseButtons = [];
|
|
27
|
-
/**
|
|
28
|
-
* Contains the mouse X-coordinate, in pixels
|
|
29
|
-
*/
|
|
30
|
-
this.mouseX = 0;
|
|
31
|
-
/**
|
|
32
|
-
* Contains the mouse Y-coordinate, in pixels
|
|
33
|
-
*/
|
|
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;
|
|
47
|
-
/**
|
|
48
|
-
* Determine whether the user has interacted with this canvas
|
|
49
|
-
*/
|
|
50
|
-
this.interacted = false;
|
|
51
|
-
/**
|
|
52
|
-
* Determine whether audio is allowed or muted
|
|
53
|
-
*/
|
|
54
|
-
this.muted = false;
|
|
55
|
-
/**
|
|
56
|
-
* Contains the list of all active audio elements
|
|
57
|
-
*/
|
|
58
|
-
this.audios = [];
|
|
96
|
+
constructor(options = {}) {
|
|
59
97
|
this.config = Canvas.setDefaults(options, Canvas.defaults);
|
|
60
98
|
this.width = this.config.width;
|
|
61
99
|
this.height = this.config.height;
|
|
62
100
|
// Create the main <canvas> element and set properties
|
|
63
|
-
|
|
101
|
+
const main = document.createElement('canvas');
|
|
64
102
|
main.tabIndex = 1; // For element focus
|
|
65
103
|
main.style.outline = 'none';
|
|
66
104
|
main.style.imageRendering = 'pixelated';
|
|
67
105
|
main.width = this.config.width;
|
|
68
106
|
main.height = this.config.height;
|
|
69
|
-
main.style.width =
|
|
70
|
-
main.style.height =
|
|
71
|
-
main.style.border =
|
|
107
|
+
main.style.width = `${this.config.width * this.config.scale}px`;
|
|
108
|
+
main.style.height = `${this.config.height * this.config.scale}px`;
|
|
109
|
+
main.style.border = `${this.config.scale}px solid ${this.config.border}`;
|
|
72
110
|
main.style.cursor = this.config.showMouse ? 'default' : 'none';
|
|
73
111
|
main.style.boxSizing = 'content-box';
|
|
74
112
|
this.config.parent.appendChild(main);
|
|
75
113
|
// Create main canvas graphics object
|
|
76
|
-
|
|
114
|
+
const graphics = main.getContext('2d');
|
|
77
115
|
if (graphics) {
|
|
78
116
|
this.graphic = graphics;
|
|
79
117
|
}
|
|
@@ -82,9 +120,9 @@ var Canvas = /** @class */ (function () {
|
|
|
82
120
|
}
|
|
83
121
|
graphics.imageSmoothingEnabled = false;
|
|
84
122
|
// Create canvas layers
|
|
85
|
-
for (
|
|
86
|
-
|
|
87
|
-
|
|
123
|
+
for (let i = 0; i < this.config.numLayers; i++) {
|
|
124
|
+
const canvas = document.createElement('canvas');
|
|
125
|
+
const layer = canvas.getContext('2d');
|
|
88
126
|
if (layer) {
|
|
89
127
|
this.layers.push(layer);
|
|
90
128
|
}
|
|
@@ -98,124 +136,123 @@ var Canvas = /** @class */ (function () {
|
|
|
98
136
|
layer.imageSmoothingEnabled = false;
|
|
99
137
|
}
|
|
100
138
|
// Event listeners
|
|
101
|
-
main.addEventListener('mousemove',
|
|
102
|
-
if (!
|
|
139
|
+
main.addEventListener('mousemove', e => {
|
|
140
|
+
if (!this.focused) {
|
|
103
141
|
return;
|
|
104
142
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
143
|
+
this.mouseX = (e.offsetX / this.config.scale) | 0;
|
|
144
|
+
this.mouseY = (e.offsetY / this.config.scale) | 0;
|
|
145
|
+
this.log(e.type, this.mouseX, this.mouseY);
|
|
146
|
+
this.config.mousemove(this.mouseX, this.mouseY);
|
|
109
147
|
});
|
|
110
|
-
main.addEventListener('keydown',
|
|
148
|
+
main.addEventListener('keydown', e => {
|
|
111
149
|
audioContext.resume();
|
|
112
|
-
|
|
113
|
-
if (!
|
|
150
|
+
this.interacted = true;
|
|
151
|
+
if (!this.focused) {
|
|
114
152
|
return;
|
|
115
153
|
}
|
|
116
154
|
e.preventDefault();
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (!
|
|
120
|
-
|
|
121
|
-
|
|
155
|
+
const key = e.key.toLowerCase();
|
|
156
|
+
this.log(e.type, this.keys);
|
|
157
|
+
if (!this.keys.includes(key)) {
|
|
158
|
+
this.keys.push(key);
|
|
159
|
+
this.config.keydown(key);
|
|
122
160
|
}
|
|
123
161
|
});
|
|
124
|
-
main.addEventListener('keyup',
|
|
125
|
-
if (!
|
|
162
|
+
main.addEventListener('keyup', e => {
|
|
163
|
+
if (!this.focused) {
|
|
126
164
|
return;
|
|
127
165
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
166
|
+
const key = e.key.toLowerCase();
|
|
167
|
+
const index = this.keys.indexOf(key);
|
|
168
|
+
this.log(e.type, this.keys);
|
|
131
169
|
if (index >= 0) {
|
|
132
|
-
|
|
133
|
-
|
|
170
|
+
this.keys.splice(index, 1);
|
|
171
|
+
this.config.keyup(key);
|
|
134
172
|
}
|
|
135
173
|
});
|
|
136
|
-
main.addEventListener('mousedown',
|
|
174
|
+
main.addEventListener('mousedown', e => {
|
|
137
175
|
audioContext.resume();
|
|
138
|
-
|
|
139
|
-
if (!
|
|
176
|
+
this.interacted = true;
|
|
177
|
+
if (!this.focused) {
|
|
140
178
|
return;
|
|
141
179
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (!
|
|
145
|
-
|
|
146
|
-
|
|
180
|
+
const button = e.button;
|
|
181
|
+
this.log(e.type, this.mouseButtons);
|
|
182
|
+
if (!this.mouseButtons.includes(button)) {
|
|
183
|
+
this.mouseButtons.push(button);
|
|
184
|
+
this.config.mousedown(button);
|
|
147
185
|
}
|
|
148
186
|
});
|
|
149
|
-
main.addEventListener('mouseup',
|
|
150
|
-
if (!
|
|
187
|
+
main.addEventListener('mouseup', e => {
|
|
188
|
+
if (!this.focused) {
|
|
151
189
|
return;
|
|
152
190
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
191
|
+
const button = e.button;
|
|
192
|
+
const index = this.mouseButtons.indexOf(button);
|
|
193
|
+
this.log(e.type, this.mouseButtons);
|
|
156
194
|
if (index >= 0) {
|
|
157
|
-
|
|
158
|
-
|
|
195
|
+
this.mouseButtons.splice(index, 1);
|
|
196
|
+
this.config.mouseup(button);
|
|
159
197
|
}
|
|
160
198
|
});
|
|
161
|
-
main.addEventListener('focusin',
|
|
162
|
-
|
|
163
|
-
main.style.borderColor =
|
|
164
|
-
|
|
165
|
-
|
|
199
|
+
main.addEventListener('focusin', e => {
|
|
200
|
+
this.focused = true;
|
|
201
|
+
main.style.borderColor = this.config.border;
|
|
202
|
+
this.log(e.type, this.focused);
|
|
203
|
+
this.animation = requestAnimationFrame(time => this.startAnimate(time));
|
|
166
204
|
});
|
|
167
|
-
main.addEventListener('focusout',
|
|
168
|
-
if (
|
|
205
|
+
main.addEventListener('focusout', e => {
|
|
206
|
+
if (this.config.keepFocused) {
|
|
169
207
|
main.focus();
|
|
170
208
|
}
|
|
171
209
|
else {
|
|
172
|
-
|
|
173
|
-
main.style.borderColor =
|
|
174
|
-
|
|
175
|
-
cancelAnimationFrame(
|
|
210
|
+
this.focused = false;
|
|
211
|
+
main.style.borderColor = this.config.borderBlur;
|
|
212
|
+
this.log(e.type, this.focused);
|
|
213
|
+
cancelAnimationFrame(this.animation);
|
|
176
214
|
}
|
|
177
215
|
});
|
|
178
|
-
window.addEventListener('blur',
|
|
179
|
-
|
|
180
|
-
cancelAnimationFrame(
|
|
216
|
+
window.addEventListener('blur', e => {
|
|
217
|
+
this.log(e.type);
|
|
218
|
+
cancelAnimationFrame(this.animation);
|
|
181
219
|
});
|
|
182
|
-
main.addEventListener('contextmenu',
|
|
220
|
+
main.addEventListener('contextmenu', e => e.preventDefault());
|
|
183
221
|
// Initialize audio tracks for recording
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
for (
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
222
|
+
const stream = new MediaStream(main.captureStream()); // video stream
|
|
223
|
+
const audioContext = new window.AudioContext();
|
|
224
|
+
for (let track = 0; track < this.config.numTracks; track++) {
|
|
225
|
+
const audio = new Audio();
|
|
226
|
+
const source = audioContext.createMediaElementSource(audio);
|
|
227
|
+
const destination = audioContext.createMediaStreamDestination();
|
|
190
228
|
source.connect(destination); // contains the audio stream for recording
|
|
191
229
|
source.connect(audioContext.destination); // so the user can hear
|
|
192
230
|
this.audios.push(audio);
|
|
193
|
-
for (
|
|
194
|
-
|
|
195
|
-
this.log("Adding ".concat(audioTrack.id, " to track ").concat(track, "."));
|
|
231
|
+
for (const audioTrack of destination.stream.getAudioTracks()) {
|
|
232
|
+
this.log(`Adding ${audioTrack.id} to track ${track}.`);
|
|
196
233
|
stream.addTrack(audioTrack); // add audio track
|
|
197
234
|
}
|
|
198
235
|
}
|
|
199
236
|
// Initialize the media recorder
|
|
200
|
-
|
|
237
|
+
const recordPart = [];
|
|
201
238
|
this.recorder = new MediaRecorder(stream);
|
|
202
|
-
this.recorder.addEventListener('dataavailable',
|
|
203
|
-
|
|
239
|
+
this.recorder.addEventListener('dataavailable', e => {
|
|
240
|
+
this.log(e.type);
|
|
204
241
|
recordPart.push(e.data);
|
|
205
242
|
});
|
|
206
|
-
this.recorder.addEventListener('stop',
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
243
|
+
this.recorder.addEventListener('stop', e => {
|
|
244
|
+
this.log(e.type);
|
|
245
|
+
const recording = new Blob(recordPart, { type: 'video/webm;codecs=h264' });
|
|
246
|
+
const url = URL.createObjectURL(recording);
|
|
247
|
+
const downloadLink = document.createElement('a');
|
|
211
248
|
downloadLink.setAttribute('href', url);
|
|
212
|
-
downloadLink.setAttribute('download',
|
|
249
|
+
downloadLink.setAttribute('download', `recording-${Date.now()}`);
|
|
213
250
|
downloadLink.click();
|
|
214
251
|
// Clear out the existing blob parts for recording a new capture
|
|
215
252
|
recordPart.splice(0);
|
|
216
253
|
// Remove all keys and mouse buttons down because we lose focus
|
|
217
|
-
|
|
218
|
-
|
|
254
|
+
this.keys.splice(0);
|
|
255
|
+
this.mouseButtons.splice(0);
|
|
219
256
|
});
|
|
220
257
|
// Focus on the canvas
|
|
221
258
|
main.focus();
|
|
@@ -223,91 +260,87 @@ var Canvas = /** @class */ (function () {
|
|
|
223
260
|
/**
|
|
224
261
|
* Start the animation.
|
|
225
262
|
*/
|
|
226
|
-
|
|
227
|
-
var _this = this;
|
|
263
|
+
startAnimate(time) {
|
|
228
264
|
this.log('startAnimate', time);
|
|
229
265
|
this.lastFrame = time;
|
|
230
|
-
this.animation = requestAnimationFrame(
|
|
231
|
-
}
|
|
266
|
+
this.animation = requestAnimationFrame(time => this.animate(time));
|
|
267
|
+
}
|
|
232
268
|
/**
|
|
233
269
|
* Run the main animation loop.
|
|
234
270
|
*/
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
var dt = currentFrame - this.lastFrame;
|
|
271
|
+
animate(time) {
|
|
272
|
+
const currentFrame = time;
|
|
273
|
+
const dt = currentFrame - this.lastFrame;
|
|
239
274
|
this.lastFrame = currentFrame;
|
|
240
275
|
this.config.loop(dt);
|
|
241
276
|
// Draw all the layers onto the main canvas
|
|
242
277
|
this.graphic.fillStyle = this.config.background;
|
|
243
278
|
this.graphic.fillRect(0, 0, this.width, this.height);
|
|
244
|
-
for (
|
|
245
|
-
var layer = _a[_i];
|
|
279
|
+
for (const layer of this.layers) {
|
|
246
280
|
this.graphic.drawImage(layer.canvas, 0, 0);
|
|
247
281
|
}
|
|
248
|
-
this.animation = requestAnimationFrame(
|
|
249
|
-
}
|
|
282
|
+
this.animation = requestAnimationFrame(time => this.animate(time));
|
|
283
|
+
}
|
|
250
284
|
/**
|
|
251
285
|
* Determine whether a key is currently pressed.
|
|
252
286
|
* @param key The key to check
|
|
253
287
|
* @returns True if `key` is down
|
|
254
288
|
*/
|
|
255
|
-
|
|
289
|
+
isKeyDown(key) {
|
|
256
290
|
return this.keys.includes(key.toLowerCase());
|
|
257
|
-
}
|
|
291
|
+
}
|
|
258
292
|
/**
|
|
259
293
|
* Determine whether a mouse button is currently pressed.
|
|
260
294
|
* @param button The button ID
|
|
261
295
|
* @returns True if `button` is down
|
|
262
296
|
*/
|
|
263
|
-
|
|
297
|
+
isMouseButtonDown(button) {
|
|
264
298
|
return this.mouseButtons.includes(button);
|
|
265
|
-
}
|
|
299
|
+
}
|
|
266
300
|
/**
|
|
267
301
|
* Get the current cursor position.
|
|
268
302
|
* @returns Cursor position as `[x, y]`
|
|
269
303
|
*/
|
|
270
|
-
|
|
304
|
+
getMousePosition() {
|
|
271
305
|
return [this.mouseX, this.mouseY];
|
|
272
|
-
}
|
|
306
|
+
}
|
|
273
307
|
/**
|
|
274
308
|
* Take a screenshot of the canvas contents and save to a .png file.
|
|
275
309
|
* @param name The file name of the screenshot
|
|
276
310
|
*/
|
|
277
|
-
|
|
278
|
-
if (name === void 0) { name = 'screenshot'; }
|
|
311
|
+
screenshot(name = 'screenshot') {
|
|
279
312
|
// Generate a data URL and set it as the download parameter for <a>
|
|
280
|
-
|
|
281
|
-
|
|
313
|
+
const dataURL = this.graphic.canvas.toDataURL();
|
|
314
|
+
const downloadLink = document.createElement('a');
|
|
282
315
|
downloadLink.setAttribute('href', dataURL);
|
|
283
316
|
downloadLink.setAttribute('download', name);
|
|
284
317
|
downloadLink.click();
|
|
285
318
|
// Remove all keys and mouse buttons down because we lose focus
|
|
286
319
|
this.keys.splice(0);
|
|
287
320
|
this.mouseButtons.splice(0);
|
|
288
|
-
}
|
|
321
|
+
}
|
|
289
322
|
/**
|
|
290
323
|
* Start recording all layers on the canvas
|
|
291
324
|
*/
|
|
292
|
-
|
|
325
|
+
startRecording() {
|
|
293
326
|
if (!this.interacted) {
|
|
294
327
|
throw new Error('The user has not yet interacted with this canvas.');
|
|
295
328
|
}
|
|
296
329
|
this.recorder.start();
|
|
297
|
-
}
|
|
330
|
+
}
|
|
298
331
|
/**
|
|
299
332
|
* Stop recording and download screen capture
|
|
300
333
|
*/
|
|
301
|
-
|
|
334
|
+
stopRecording() {
|
|
302
335
|
this.recorder.stop();
|
|
303
|
-
}
|
|
336
|
+
}
|
|
304
337
|
/**
|
|
305
338
|
* Determines whether the media recorder is active
|
|
306
339
|
* @returns True if currently recording
|
|
307
340
|
*/
|
|
308
|
-
|
|
341
|
+
isRecording() {
|
|
309
342
|
return this.recorder.state === 'recording';
|
|
310
|
-
}
|
|
343
|
+
}
|
|
311
344
|
/**
|
|
312
345
|
* Play an audio file
|
|
313
346
|
* @param src The path of the audio file
|
|
@@ -315,221 +348,161 @@ var Canvas = /** @class */ (function () {
|
|
|
315
348
|
* @param volume The normalized [0-1] volume
|
|
316
349
|
* @param track The track number to play on
|
|
317
350
|
*/
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
if (volume === void 0) { volume = 1; }
|
|
321
|
-
if (track === void 0) { track = 0; }
|
|
322
|
-
this.log("Playing audio on track ".concat(track, "."));
|
|
351
|
+
playAudio(src, loop = false, volume = 1, track = 0) {
|
|
352
|
+
this.log(`Playing audio on track ${track}.`);
|
|
323
353
|
if (!this.interacted) {
|
|
324
354
|
throw new Error('The user has not yet interacted with this canvas.');
|
|
325
355
|
}
|
|
326
356
|
if (track < 0 || track >= this.config.numTracks) {
|
|
327
|
-
throw new Error(
|
|
357
|
+
throw new Error(`Track ${track} is out of range. [0,${this.config.numTracks})`);
|
|
328
358
|
}
|
|
329
|
-
|
|
359
|
+
const audio = this.audios[track];
|
|
330
360
|
audio.src = src;
|
|
331
361
|
audio.loop = loop;
|
|
332
362
|
audio.volume = volume;
|
|
333
363
|
audio.muted = this.muted;
|
|
334
364
|
audio.play();
|
|
335
|
-
}
|
|
365
|
+
}
|
|
336
366
|
/**
|
|
337
367
|
* Stop all audio tracks from playing
|
|
338
368
|
*/
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
this.log("Stopping audio on track ".concat(track, "."));
|
|
369
|
+
stopAudio(track = -1) {
|
|
370
|
+
this.log(`Stopping audio on track ${track}.`);
|
|
342
371
|
if (track >= this.config.numTracks) {
|
|
343
|
-
throw new Error(
|
|
372
|
+
throw new Error(`Track ${track} is out of range. [0,${this.config.numTracks})`);
|
|
344
373
|
}
|
|
345
374
|
if (track < 0) {
|
|
346
|
-
for (
|
|
347
|
-
var audio = _a[_i];
|
|
375
|
+
for (const audio of this.audios) {
|
|
348
376
|
audio.pause();
|
|
349
377
|
}
|
|
350
378
|
}
|
|
351
379
|
else {
|
|
352
380
|
this.audios[track].pause();
|
|
353
381
|
}
|
|
354
|
-
}
|
|
382
|
+
}
|
|
355
383
|
/**
|
|
356
384
|
* Mute all audio
|
|
357
385
|
*/
|
|
358
|
-
|
|
386
|
+
mute() {
|
|
359
387
|
this.muted = true;
|
|
360
|
-
for (
|
|
361
|
-
var audio = _a[_i];
|
|
388
|
+
for (const audio of this.audios) {
|
|
362
389
|
audio.muted = this.muted;
|
|
363
390
|
}
|
|
364
|
-
}
|
|
391
|
+
}
|
|
365
392
|
/**
|
|
366
393
|
* Unmute all audio
|
|
367
394
|
*/
|
|
368
|
-
|
|
395
|
+
unmute() {
|
|
369
396
|
this.muted = false;
|
|
370
|
-
for (
|
|
371
|
-
var audio = _a[_i];
|
|
397
|
+
for (const audio of this.audios) {
|
|
372
398
|
audio.muted = this.muted;
|
|
373
399
|
}
|
|
374
|
-
}
|
|
400
|
+
}
|
|
375
401
|
/**
|
|
376
402
|
* Determines whether audio is muted
|
|
377
403
|
* @returns True if currently muted
|
|
378
404
|
*/
|
|
379
|
-
|
|
405
|
+
isMuted() {
|
|
380
406
|
return this.muted;
|
|
381
|
-
}
|
|
407
|
+
}
|
|
382
408
|
/**
|
|
383
409
|
* Draw an object onto the canvas.
|
|
384
410
|
* @param drawable Any drawable object
|
|
385
411
|
* @param layer The zero-indexed layer to draw to
|
|
386
412
|
*/
|
|
387
|
-
|
|
388
|
-
if (layer === void 0) { layer = 0; }
|
|
413
|
+
draw(drawable, layer = 0) {
|
|
389
414
|
if (layer < 0 || layer >= this.config.numLayers) {
|
|
390
|
-
throw new Error(
|
|
415
|
+
throw new Error(`Layer ${layer} is out of range. [0,${this.config.numLayers})`);
|
|
391
416
|
}
|
|
392
417
|
drawable.draw(this.layers[layer]);
|
|
393
|
-
}
|
|
418
|
+
}
|
|
394
419
|
/**
|
|
395
420
|
* Completely clears the canvas.
|
|
396
421
|
* @param layer The zero-indexed layer to clear, if unset, will clear all layers
|
|
397
422
|
*/
|
|
398
|
-
|
|
399
|
-
if (layer === void 0) { layer = -1; }
|
|
423
|
+
clear(layer = -1) {
|
|
400
424
|
if (layer >= this.config.numLayers) {
|
|
401
|
-
throw new Error(
|
|
425
|
+
throw new Error(`Layer ${layer} is out of range. [0,${this.config.numLayers})`);
|
|
402
426
|
}
|
|
403
427
|
if (layer < 0) {
|
|
404
|
-
for (
|
|
405
|
-
|
|
406
|
-
layer_1.clearRect(0, 0, this.config.width, this.config.height);
|
|
428
|
+
for (const layer of this.layers) {
|
|
429
|
+
layer.clearRect(0, 0, this.config.width, this.config.height);
|
|
407
430
|
}
|
|
408
431
|
}
|
|
409
432
|
else {
|
|
410
433
|
this.layers[layer].clearRect(0, 0, this.config.width, this.config.height);
|
|
411
434
|
}
|
|
412
|
-
}
|
|
435
|
+
}
|
|
413
436
|
/**
|
|
414
437
|
* Save arbitrary data to the browser storage.
|
|
415
438
|
* **Warning:** This will overwrite any existing data under these object keys.
|
|
416
439
|
* @param data The arbirary data object to save
|
|
417
440
|
* @param name An optional "namespace" to store the data, which can be assigned when a user has multiple profiles, for example
|
|
418
441
|
*/
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
localStorage.setItem(key, value);
|
|
426
|
-
this.log("Saved \"".concat(name, ".").concat(rawkey, "\" = \"").concat(rawval, ".").concat(typeof rawval, "\" as \"").concat(key, "\" = \"").concat(value, "\"."));
|
|
427
|
-
}
|
|
428
|
-
};
|
|
442
|
+
saveData(data, name = 'data') {
|
|
443
|
+
const key = this.encode(name);
|
|
444
|
+
const value = this.encode(data);
|
|
445
|
+
localStorage.setItem(key, value);
|
|
446
|
+
this.log(`Saved "${data}" in ${name}.`);
|
|
447
|
+
}
|
|
429
448
|
/**
|
|
430
449
|
* Load arbitrary data from the browser storage.
|
|
431
450
|
* **Warning:** The loaded data object is not strictly type-checked or sanitized, so it may have missing fields or contain extra fields. It's good practice to manually validate the data once it's loaded from storage.
|
|
432
451
|
* @param name An optional "namespace" to load data from, assigned when saving data
|
|
433
452
|
* @returns The data object loaded from browser storage
|
|
434
453
|
*/
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
for (var _i = 0, _b = Object.keys(localStorage); _i < _b.length; _i++) {
|
|
440
|
-
var key = _b[_i];
|
|
454
|
+
loadData(name = 'data') {
|
|
455
|
+
const key = this.encode(name);
|
|
456
|
+
const value = localStorage.getItem(key);
|
|
457
|
+
if (value) {
|
|
441
458
|
try {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
loaded[decodedKey[1]] = decodedVal[0];
|
|
446
|
-
this.log("Loaded \"".concat(decodedKey.join('.'), "\" = \"").concat(decodedVal.join('.'), "\" from \"").concat(key, "\""));
|
|
447
|
-
}
|
|
459
|
+
const decoded = this.decode(value);
|
|
460
|
+
this.log(`Loaded "${decoded}" from ${name}.`);
|
|
461
|
+
return decoded;
|
|
448
462
|
}
|
|
449
|
-
catch
|
|
450
|
-
this.log(
|
|
463
|
+
catch {
|
|
464
|
+
this.log(`Failed to load ${name}.`);
|
|
451
465
|
}
|
|
452
466
|
}
|
|
453
|
-
return
|
|
454
|
-
}
|
|
467
|
+
return undefined;
|
|
468
|
+
}
|
|
455
469
|
/**
|
|
456
470
|
* Clear **all** saved data. This action is permanent!
|
|
457
471
|
* @param name An optional "namespace" to clear data from.
|
|
458
472
|
*/
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
if (this.decode(key)[0] === name) {
|
|
465
|
-
localStorage.removeItem(key);
|
|
466
|
-
this.log("Removed ".concat(key, " from storage."));
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
catch (_b) {
|
|
470
|
-
this.log("Skipping \"".concat(key, "\"..."));
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
};
|
|
473
|
+
clearData(name = 'data') {
|
|
474
|
+
const key = this.encode(name);
|
|
475
|
+
localStorage.removeItem(key);
|
|
476
|
+
this.log(`Removed ${name} from storage.`);
|
|
477
|
+
}
|
|
474
478
|
/**
|
|
475
479
|
* Encode data for local browser storage.
|
|
476
480
|
*/
|
|
477
|
-
|
|
478
|
-
return
|
|
479
|
-
}
|
|
481
|
+
encode(raw) {
|
|
482
|
+
return btoa(encodeURIComponent(JSON.stringify(raw)));
|
|
483
|
+
}
|
|
480
484
|
/**
|
|
481
485
|
* Decode data from local browser storage.
|
|
482
486
|
*/
|
|
483
|
-
|
|
484
|
-
return
|
|
485
|
-
}
|
|
487
|
+
decode(code) {
|
|
488
|
+
return JSON.parse(decodeURIComponent(atob(code)));
|
|
489
|
+
}
|
|
486
490
|
/**
|
|
487
491
|
* Log a message to the debug console.
|
|
488
492
|
*/
|
|
489
|
-
|
|
490
|
-
var x = [];
|
|
491
|
-
for (var _i = 0; _i < arguments.length; _i++) {
|
|
492
|
-
x[_i] = arguments[_i];
|
|
493
|
-
}
|
|
493
|
+
log(...x) {
|
|
494
494
|
if (this.config.debug) {
|
|
495
|
-
console.log
|
|
495
|
+
console.log(...x);
|
|
496
496
|
}
|
|
497
|
-
}
|
|
497
|
+
}
|
|
498
498
|
/**
|
|
499
499
|
* Set defaults for all undefined options.
|
|
500
500
|
*/
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
var key = _b[_i];
|
|
506
|
-
copy[key] = (_a = options[key]) !== null && _a !== void 0 ? _a : defaults[key];
|
|
501
|
+
static setDefaults(options, defaults) {
|
|
502
|
+
const copy = {};
|
|
503
|
+
for (const key of Object.keys(defaults)) {
|
|
504
|
+
copy[key] = options[key] ?? defaults[key];
|
|
507
505
|
}
|
|
508
506
|
return copy;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
* Default canvas options
|
|
512
|
-
*/
|
|
513
|
-
Canvas.defaults = {
|
|
514
|
-
debug: false,
|
|
515
|
-
parent: document.body,
|
|
516
|
-
width: 600,
|
|
517
|
-
height: 400,
|
|
518
|
-
scale: 1,
|
|
519
|
-
background: 'white',
|
|
520
|
-
border: 'transparent',
|
|
521
|
-
borderBlur: 'transparent',
|
|
522
|
-
showMouse: true,
|
|
523
|
-
keepFocused: false,
|
|
524
|
-
numLayers: 1,
|
|
525
|
-
numTracks: 1,
|
|
526
|
-
keydown: function () { return; },
|
|
527
|
-
keyup: function () { return; },
|
|
528
|
-
mousemove: function () { return; },
|
|
529
|
-
mousedown: function () { return; },
|
|
530
|
-
mouseup: function () { return; },
|
|
531
|
-
loop: function () { return; },
|
|
532
|
-
};
|
|
533
|
-
return Canvas;
|
|
534
|
-
}());
|
|
535
|
-
exports.Canvas = Canvas;
|
|
507
|
+
}
|
|
508
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
1
|
/**
|
|
18
2
|
* @packageDocumentation
|
|
19
3
|
* Canvas 2D rendering toolkit for games and visual projects
|
|
@@ -21,5 +5,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
21
5
|
* 
|
|
22
6
|
* 
|
|
23
7
|
*/
|
|
24
|
-
|
|
25
|
-
|
|
8
|
+
export * from './canvas.js';
|
|
9
|
+
export * from './types.js';
|
package/dist/types.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphico",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Canvas 2D rendering toolkit for games and visual projects",
|
|
5
5
|
"homepage": "https://npm.nicfv.com/",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"bin": "",
|
|
7
8
|
"main": "dist/index.js",
|
|
8
9
|
"types": "types/index.d.ts",
|
|
@@ -42,6 +43,6 @@
|
|
|
42
43
|
"repository": "github:nicfv/npm",
|
|
43
44
|
"license": "MIT",
|
|
44
45
|
"devDependencies": {
|
|
45
|
-
"t6": "1.
|
|
46
|
+
"t6": "1.3.0"
|
|
46
47
|
}
|
|
47
48
|
}
|
package/types/canvas.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Drawable, Options
|
|
1
|
+
import { Drawable, Options } from './index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Represents a canvas for drawing and animating
|
|
4
4
|
*/
|
|
@@ -161,14 +161,14 @@ export declare class Canvas {
|
|
|
161
161
|
* @param data The arbirary data object to save
|
|
162
162
|
* @param name An optional "namespace" to store the data, which can be assigned when a user has multiple profiles, for example
|
|
163
163
|
*/
|
|
164
|
-
saveData<T
|
|
164
|
+
saveData<T>(data: T, name?: string): void;
|
|
165
165
|
/**
|
|
166
166
|
* Load arbitrary data from the browser storage.
|
|
167
167
|
* **Warning:** The loaded data object is not strictly type-checked or sanitized, so it may have missing fields or contain extra fields. It's good practice to manually validate the data once it's loaded from storage.
|
|
168
168
|
* @param name An optional "namespace" to load data from, assigned when saving data
|
|
169
169
|
* @returns The data object loaded from browser storage
|
|
170
170
|
*/
|
|
171
|
-
loadData<T
|
|
171
|
+
loadData<T>(name?: string): Partial<T> | undefined;
|
|
172
172
|
/**
|
|
173
173
|
* Clear **all** saved data. This action is permanent!
|
|
174
174
|
* @param name An optional "namespace" to clear data from.
|
package/types/index.d.ts
CHANGED