graphico 1.0.0 → 1.1.1

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/README.md CHANGED
@@ -4,11 +4,20 @@
4
4
 
5
5
  ## Features
6
6
 
7
- - Simple, object-oriented canvas API for organized graphics programming
8
- - Easy debugging and extensive configuration
9
- - Supports multiple canvas layers with transparency
10
- - Hardware-accelerated graphics and animation
11
- - User interaction via keyboard or mouse inputs
12
- - Built-in logic for screenshots and screen recordings
13
-
14
- See the examples to learn how to use `graphico`!
7
+ `graphico` boasts several features that make it an extremely versatile package for several project types.
8
+
9
+ - Simple, **object-oriented canvas API** for organized graphics programming
10
+ - Easy debugging and extensive configuration for **complete customizability**
11
+ - Supports multiple independent **canvas layers** with transparency
12
+ - **Hardware-accelerated graphics** for fast and smooth animation
13
+ - **User interaction** captured via keyboard inputs or mouse movements or clicks
14
+ - Multiple audio tracks supported by system **audio player** with volume, mute, and loop toggle
15
+ - Built-in logic for **screenshots** and **screen recordings** that capture both video and audio
16
+
17
+ ## Caveats
18
+
19
+ By default, many browsers prevent autoplay, which arguably is a good thing. (Don't you hate it when you have several tabs open and one of them starts making noise?) This security feature blocks API calls to `Canvas.playAudio()` from being called automatically on page load. It needs to be triggered manually (initially) by some form of user interaction; either a key press or a mouse click. After this initial interaction step, calls to `playAudio()` can be performed automatically. If you wanted to have background music looping for a game, it would need to be triggered after pressing the "start" button, for example.
20
+
21
+ ## Learn More
22
+
23
+ The best way to learn and experience its features is to try it out yourself. See the examples to learn how to use `graphico`!
package/dist/canvas.js CHANGED
@@ -44,6 +44,18 @@ var Canvas = /** @class */ (function () {
44
44
  * Determine whether the client is focused or not
45
45
  */
46
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 = [];
47
59
  this.config = Canvas.setDefaults(options, Canvas.defaults);
48
60
  this.width = this.config.width;
49
61
  this.height = this.config.height;
@@ -95,6 +107,8 @@ var Canvas = /** @class */ (function () {
95
107
  _this.config.mousemove(_this.mouseX, _this.mouseY);
96
108
  });
97
109
  main.addEventListener('keydown', function (e) {
110
+ audioContext.resume();
111
+ _this.interacted = true;
98
112
  if (!_this.focused) {
99
113
  return;
100
114
  }
@@ -119,6 +133,8 @@ var Canvas = /** @class */ (function () {
119
133
  }
120
134
  });
121
135
  main.addEventListener('mousedown', function (e) {
136
+ audioContext.resume();
137
+ _this.interacted = true;
122
138
  if (!_this.focused) {
123
139
  return;
124
140
  }
@@ -148,21 +164,40 @@ var Canvas = /** @class */ (function () {
148
164
  _this.animation = requestAnimationFrame(function (time) { return _this.startAnimate(time); });
149
165
  });
150
166
  main.addEventListener('focusout', function (e) {
151
- _this.focused = false;
152
- main.style.borderColor = _this.config.borderBlur;
153
- _this.log(e.type, _this.focused);
154
- cancelAnimationFrame(_this.animation);
167
+ if (_this.config.keepFocused) {
168
+ main.focus();
169
+ }
170
+ else {
171
+ _this.focused = false;
172
+ main.style.borderColor = _this.config.borderBlur;
173
+ _this.log(e.type, _this.focused);
174
+ cancelAnimationFrame(_this.animation);
175
+ }
155
176
  });
156
177
  window.addEventListener('blur', function (e) {
157
178
  _this.log(e.type);
158
179
  cancelAnimationFrame(_this.animation);
159
180
  });
160
181
  main.addEventListener('contextmenu', function (e) { return e.preventDefault(); });
161
- // Focus on the canvas
162
- main.focus();
182
+ // Initialize audio tracks for recording
183
+ var stream = new MediaStream(main.captureStream()); // video stream
184
+ var audioContext = new window.AudioContext();
185
+ for (var track = 0; track < this.config.numTracks; track++) {
186
+ var audio = new Audio();
187
+ var source = audioContext.createMediaElementSource(audio);
188
+ var destination = audioContext.createMediaStreamDestination();
189
+ source.connect(destination); // contains the audio stream for recording
190
+ source.connect(audioContext.destination); // so the user can hear
191
+ this.audios.push(audio);
192
+ for (var _i = 0, _a = destination.stream.getAudioTracks(); _i < _a.length; _i++) {
193
+ var audioTrack = _a[_i];
194
+ this.log("Adding ".concat(audioTrack.id, " to track ").concat(track, "."));
195
+ stream.addTrack(audioTrack); // add audio track
196
+ }
197
+ }
163
198
  // Initialize the media recorder
164
199
  var recordPart = [];
165
- this.recorder = new MediaRecorder(main.captureStream());
200
+ this.recorder = new MediaRecorder(stream);
166
201
  this.recorder.addEventListener('dataavailable', function (e) {
167
202
  _this.log(e.type);
168
203
  recordPart.push(e.data);
@@ -181,6 +216,8 @@ var Canvas = /** @class */ (function () {
181
216
  _this.keys.splice(0);
182
217
  _this.mouseButtons.splice(0);
183
218
  });
219
+ // Focus on the canvas
220
+ main.focus();
184
221
  }
185
222
  /**
186
223
  * Start the animation.
@@ -252,6 +289,9 @@ var Canvas = /** @class */ (function () {
252
289
  * Start recording all layers on the canvas
253
290
  */
254
291
  Canvas.prototype.startRecording = function () {
292
+ if (!this.interacted) {
293
+ throw new Error('The user has not yet interacted with this canvas.');
294
+ }
255
295
  this.recorder.start();
256
296
  };
257
297
  /**
@@ -262,11 +302,82 @@ var Canvas = /** @class */ (function () {
262
302
  };
263
303
  /**
264
304
  * Determines whether the media recorder is active
265
- * @returns `true` if currently recording
305
+ * @returns True if currently recording
266
306
  */
267
307
  Canvas.prototype.isRecording = function () {
268
308
  return this.recorder.state === 'recording';
269
309
  };
310
+ /**
311
+ * Play an audio file
312
+ * @param src The path of the audio file
313
+ * @param loop Whether to play the audio on loop
314
+ * @param volume The normalized [0-1] volume
315
+ * @param track The track number to play on
316
+ */
317
+ Canvas.prototype.playAudio = function (src, loop, volume, track) {
318
+ if (loop === void 0) { loop = false; }
319
+ if (volume === void 0) { volume = 1; }
320
+ if (track === void 0) { track = 0; }
321
+ this.log("Playing audio on track ".concat(track, "."));
322
+ if (!this.interacted) {
323
+ throw new Error('The user has not yet interacted with this canvas.');
324
+ }
325
+ if (track < 0 || track >= this.config.numTracks) {
326
+ throw new Error("Track ".concat(track, " is out of range. [0,").concat(this.config.numTracks, ")"));
327
+ }
328
+ var audio = this.audios[track];
329
+ audio.src = src;
330
+ audio.loop = loop;
331
+ audio.volume = volume;
332
+ audio.muted = this.muted;
333
+ audio.play();
334
+ };
335
+ /**
336
+ * Stop all audio tracks from playing
337
+ */
338
+ Canvas.prototype.stopAudio = function (track) {
339
+ if (track === void 0) { track = -1; }
340
+ this.log("Stopping audio on track ".concat(track, "."));
341
+ if (track >= this.config.numTracks) {
342
+ throw new Error("Track ".concat(track, " is out of range. [0,").concat(this.config.numTracks, ")"));
343
+ }
344
+ if (track < 0) {
345
+ for (var _i = 0, _a = this.audios; _i < _a.length; _i++) {
346
+ var audio = _a[_i];
347
+ audio.pause();
348
+ }
349
+ }
350
+ else {
351
+ this.audios[track].pause();
352
+ }
353
+ };
354
+ /**
355
+ * Mute all audio
356
+ */
357
+ Canvas.prototype.mute = function () {
358
+ this.muted = true;
359
+ for (var _i = 0, _a = this.audios; _i < _a.length; _i++) {
360
+ var audio = _a[_i];
361
+ audio.muted = this.muted;
362
+ }
363
+ };
364
+ /**
365
+ * Unmute all audio
366
+ */
367
+ Canvas.prototype.unmute = function () {
368
+ this.muted = false;
369
+ for (var _i = 0, _a = this.audios; _i < _a.length; _i++) {
370
+ var audio = _a[_i];
371
+ audio.muted = this.muted;
372
+ }
373
+ };
374
+ /**
375
+ * Determines whether audio is muted
376
+ * @returns True if currently muted
377
+ */
378
+ Canvas.prototype.isMuted = function () {
379
+ return this.muted;
380
+ };
270
381
  /**
271
382
  * Draw an object onto the canvas.
272
383
  * @param drawable Any drawable object
@@ -274,6 +385,9 @@ var Canvas = /** @class */ (function () {
274
385
  */
275
386
  Canvas.prototype.draw = function (drawable, layer) {
276
387
  if (layer === void 0) { layer = 0; }
388
+ if (layer < 0 || layer >= this.config.numLayers) {
389
+ throw new Error("Layer ".concat(layer, " is out of range. [0,").concat(this.config.numLayers, ")"));
390
+ }
277
391
  drawable.draw(this.layers[layer]);
278
392
  };
279
393
  /**
@@ -282,6 +396,9 @@ var Canvas = /** @class */ (function () {
282
396
  */
283
397
  Canvas.prototype.clear = function (layer) {
284
398
  if (layer === void 0) { layer = -1; }
399
+ if (layer >= this.config.numLayers) {
400
+ throw new Error("Layer ".concat(layer, " is out of range. [0,").concat(this.config.numLayers, ")"));
401
+ }
285
402
  if (layer < 0) {
286
403
  for (var _i = 0, _a = this.layers; _i < _a.length; _i++) {
287
404
  var layer_1 = _a[_i];
@@ -329,7 +446,9 @@ var Canvas = /** @class */ (function () {
329
446
  border: 'transparent',
330
447
  borderBlur: 'transparent',
331
448
  showMouse: true,
449
+ keepFocused: false,
332
450
  numLayers: 1,
451
+ numTracks: 1,
333
452
  keydown: function () { return; },
334
453
  keyup: function () { return; },
335
454
  mousemove: function () { return; },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphico",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Canvas 2D rendering toolkit for games and visual projects",
5
5
  "homepage": "https://npm.nicfv.com/",
6
6
  "bin": "",
@@ -23,7 +23,12 @@
23
23
  "drawing",
24
24
  "art",
25
25
  "render",
26
- "2D"
26
+ "2D",
27
+ "layer",
28
+ "audio",
29
+ "track",
30
+ "animate",
31
+ "visual"
27
32
  ],
28
33
  "author": {
29
34
  "name": "Nicolas Ventura",
package/types/canvas.d.ts CHANGED
@@ -55,10 +55,22 @@ export declare class Canvas {
55
55
  * Determine whether the client is focused or not
56
56
  */
57
57
  private focused;
58
+ /**
59
+ * Determine whether the user has interacted with this canvas
60
+ */
61
+ private interacted;
58
62
  /**
59
63
  * The media recording object for screen captures
60
64
  */
61
65
  private readonly recorder;
66
+ /**
67
+ * Determine whether audio is allowed or muted
68
+ */
69
+ private muted;
70
+ /**
71
+ * Contains the list of all active audio elements
72
+ */
73
+ private readonly audios;
62
74
  /**
63
75
  * Create a new canvas with the provided options
64
76
  * @param options Configuration options
@@ -104,9 +116,34 @@ export declare class Canvas {
104
116
  stopRecording(): void;
105
117
  /**
106
118
  * Determines whether the media recorder is active
107
- * @returns `true` if currently recording
119
+ * @returns True if currently recording
108
120
  */
109
121
  isRecording(): boolean;
122
+ /**
123
+ * Play an audio file
124
+ * @param src The path of the audio file
125
+ * @param loop Whether to play the audio on loop
126
+ * @param volume The normalized [0-1] volume
127
+ * @param track The track number to play on
128
+ */
129
+ playAudio(src: string, loop?: boolean, volume?: number, track?: number): void;
130
+ /**
131
+ * Stop all audio tracks from playing
132
+ */
133
+ stopAudio(track?: number): void;
134
+ /**
135
+ * Mute all audio
136
+ */
137
+ mute(): void;
138
+ /**
139
+ * Unmute all audio
140
+ */
141
+ unmute(): void;
142
+ /**
143
+ * Determines whether audio is muted
144
+ * @returns True if currently muted
145
+ */
146
+ isMuted(): boolean;
110
147
  /**
111
148
  * Draw an object onto the canvas.
112
149
  * @param drawable Any drawable object
package/types/types.d.ts CHANGED
@@ -38,10 +38,18 @@ export interface Options {
38
38
  * Optionally show or hide the mouse when hovering over the canvas
39
39
  */
40
40
  readonly showMouse: boolean;
41
+ /**
42
+ * Refocus the canvas when it loses focus
43
+ */
44
+ readonly keepFocused: boolean;
41
45
  /**
42
46
  * The number of layers in this canvas
43
47
  */
44
48
  readonly numLayers: number;
49
+ /**
50
+ * The number of audio tracks
51
+ */
52
+ readonly numTracks: number;
45
53
  /**
46
54
  * Event listener for when a key is pressed
47
55
  * @param key The key that was pressed