littlejsengine 1.14.5 → 1.14.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.
Files changed (45) hide show
  1. package/dist/littlejs.d.ts +53 -19
  2. package/dist/littlejs.esm.js +183 -106
  3. package/dist/littlejs.esm.min.js +1 -1
  4. package/dist/littlejs.js +179 -104
  5. package/dist/littlejs.min.js +1 -1
  6. package/dist/littlejs.release.js +178 -103
  7. package/examples/box2d/game.js +1 -3
  8. package/examples/box2d/gameObjects.js +2 -2
  9. package/examples/htmlMenu/game.js +1 -1
  10. package/examples/index.html +92 -51
  11. package/examples/puzzle/game.js +1 -2
  12. package/examples/shorts/animation.js +2 -2
  13. package/examples/shorts/box2d.js +1 -0
  14. package/examples/shorts/box2dCar.js +1 -0
  15. package/examples/shorts/flappyGame.js +2 -1
  16. package/examples/shorts/helloWorld.js +2 -2
  17. package/examples/shorts/hillGlideGame.js +6 -3
  18. package/examples/shorts/maze.js +1 -0
  19. package/examples/shorts/medals.js +3 -0
  20. package/examples/shorts/music.js +3 -5
  21. package/examples/shorts/piano.js +24 -25
  22. package/examples/shorts/platformer.js +1 -0
  23. package/examples/shorts/shapes.js +6 -6
  24. package/examples/shorts/sound.js +21 -32
  25. package/examples/shorts/spriteAtlas.js +1 -2
  26. package/examples/shorts/tileLayer.js +1 -0
  27. package/examples/shorts/tiltedView.js +1 -1
  28. package/examples/shorts/timers.js +34 -31
  29. package/examples/shorts/topDown.js +2 -1
  30. package/examples/shorts/uiSystem.js +15 -10
  31. package/examples/stress/index.html +1 -1
  32. package/examples/uiSystem/game.js +1 -1
  33. package/package.json +1 -1
  34. package/plugins/uiSystem.js +83 -58
  35. package/reference.md +1 -0
  36. package/src/engine.js +23 -6
  37. package/src/engineDebug.js +1 -1
  38. package/src/engineDraw.js +19 -12
  39. package/src/engineExport.js +4 -2
  40. package/src/engineInput.js +7 -0
  41. package/src/engineObject.js +4 -4
  42. package/src/engineSettings.js +11 -1
  43. package/src/engineTileLayer.js +2 -2
  44. package/src/engineUtilities.js +9 -2
  45. package/src/engineWebGL.js +20 -18
@@ -1,40 +1,43 @@
1
- class TimerButton extends EngineObject
2
- {
3
- constructor(pos, time)
4
- {
5
- super(pos, vec2(5.5));
6
- this.time = time;
7
- this.timer = new Timer;
8
- }
1
+ let timerButton, timerSlider;
9
2
 
10
- update()
11
- {
12
- // start or stop the timer when clicked
13
- if (mouseWasPressed(0) && isOverlapping(this.pos, this.size, mousePos))
14
- this.timer.isSet() ? this.timer.unset() : this.timer.set(this.time);
15
- }
3
+ function gameInit()
4
+ {
5
+ // setup ui system plugin
6
+ new UISystemPlugin();
7
+ uiSystem.defaultCornerRadius = 10;
8
+ canvasClearColor = hsl(.3,.3,.2);
16
9
 
17
- render()
10
+ // create timer button
11
+ const pos = mainCanvasSize.scale(.5).add(vec2(0, -40));
12
+ timerButton = new UIButton(pos, vec2(200, 90), 'Start');
13
+ timerButton.timer = new Timer;
14
+ timerButton.onClick = ()=>
18
15
  {
19
- // get color based on timer state
20
- this.color = this.timer.isSet() ? this.timer.active() ? BLUE : RED : GRAY;
21
-
22
- // draw the button and timer text
23
- drawRect(this.pos, this.size, this.color);
24
- if (this.timer.isSet())
16
+ if (timerButton.timer.isSet())
25
17
  {
26
- drawTextOverlay(this.timer.get().toFixed(1), this.pos.add(vec2(0,1)), 2);
27
- const percent = this.timer.getPercent()*100|0;
28
- drawTextOverlay(percent+'%', this.pos.add(vec2(0,-1)), 2);
18
+ timerButton.timer.unset();
19
+ timerButton.text = 'Start';
29
20
  }
30
21
  else
31
- drawTextOverlay('Click\nto set', this.pos, 2);
22
+ {
23
+ timerButton.timer.set(3);
24
+ timerButton.text = 'Stop';
25
+ }
32
26
  }
33
- }
34
27
 
35
- function gameInit()
36
- {
37
- // create some timer buttons
38
- new TimerButton(vec2(-5, 0), 3);
39
- new TimerButton(vec2( 5, 0), 0);
28
+ // create non-interactive slider to display timer
29
+ timerSlider = new UIScrollbar(vec2(0, 100), vec2(400, 50));
30
+ timerSlider.interactive = false;
31
+ timerSlider.update = ()=>
32
+ {
33
+ // update the timer display
34
+ const t = timerButton.timer.get();
35
+ const timeText = t.toFixed(2) + 's';
36
+ const isSet = timerButton.timer.isSet();
37
+ const setTime = timerButton.timer.getSetTime();
38
+ timerSlider.text = timeText;
39
+ timerSlider.value = setTime ? 1+t/setTime : 1;
40
+ timerSlider.color = isSet ? t < 0 ? CYAN : RED : GRAY;
41
+ }
42
+ timerButton.addChild(timerSlider);
40
43
  }
@@ -23,13 +23,14 @@ class Player extends EngineObject
23
23
  function gameInit()
24
24
  {
25
25
  // setup level
26
+ canvasClearColor = hsl(.3,.2,.6);
26
27
  objectDefaultDamping = .7;
27
28
  new Player;
28
29
 
29
30
  // create collision objects
30
31
  for (let i=300; i--;)
31
32
  {
32
- const o = new EngineObject(randInCircle(15+i,7), vec2(rand(4,9),rand(4,9)), 0, 0, hsl(0,0,rand(.8,1)));
33
+ const o = new EngineObject(randInCircle(15+i,7), vec2(rand(4,9),rand(4,9)), 0, 0, hsl(0,0,rand(.1,.3)));
33
34
  o.setCollision(); // make object collide
34
35
  o.mass = 0; // make object have static physics
35
36
  }
@@ -4,30 +4,35 @@ let uiMenu;
4
4
 
5
5
  function gameInit()
6
6
  {
7
- // load ui system plugin
7
+ // setup ui system plugin
8
8
  new UISystemPlugin;
9
9
  uiSystem.defaultSoundPress = new Sound([1,0,220]);
10
10
  uiSystem.defaultSoundClick = new Sound([1,0,440]);
11
11
  uiSystem.defaultCornerRadius = 8;
12
12
 
13
+ // example button that returns to menu
14
+ const buttonBack = new UIButton(mainCanvasSize.scale(.5), vec2(200), 'Back\nto\nMenu');
15
+ buttonBack.textHeight = 60;
16
+ buttonBack.onClick = ()=> uiMenu.visible = true;
17
+
13
18
  // setup example menu
14
- uiBackground = new UIObject(mainCanvasSize.scale(.5), vec2(1e5));
15
- uiBackground.color = hsl(0,0,.8);
16
- const uiMenu = new UIObject(vec2(), vec2(450));
17
- uiBackground.addChild(uiMenu);
19
+ const uiMenu = new UIObject(mainCanvasSize.scale(.5), vec2(600,450));
20
+ canvasClearColor = hsl(0,0,.8);
18
21
 
19
22
  // example text
20
- const textTitle = new UIText(vec2(0,-120), vec2(400, 60), 'LittleJS UI\nSystem Demo');
21
- uiMenu.addChild(textTitle);
23
+ uiMenu.addChild(new UIText(vec2(-80,-120), vec2(500, 80), 'LittleJS UI\nSystem Demo'));
24
+
25
+ // example image
26
+ uiMenu.addChild(new UITile(vec2(200,-140), vec2(128), tile(3, 128)));
22
27
 
23
28
  // example button
24
- const button1 = new UIButton(vec2(70,0), vec2(140, 80), 'Test');
29
+ const button1 = new UIButton(vec2(80,0), vec2(140, 80), 'Test');
25
30
  button1.textHeight = 60;
26
31
  uiMenu.addChild(button1);
27
- button1.onClick = ()=> uiBackground.color = randColor();
32
+ button1.onClick = ()=> canvasClearColor = randColor();
28
33
 
29
34
  // example checkbox
30
- const checkbox = new UICheckbox(vec2(-70,0), vec2(50));
35
+ const checkbox = new UICheckbox(vec2(-80,0), vec2(50));
31
36
  checkbox.onChange = ()=> button1.disabled = checkbox.checked;
32
37
  uiMenu.addChild(checkbox);
33
38
 
@@ -47,7 +47,7 @@ function gameInit()
47
47
  LJS.setCanvasFixedSize(vec2(1920, 1080)); // 1080p
48
48
  LJS.setCameraScale(20);
49
49
  LJS.setCameraPos(tileLayer.size.scale(.5));
50
- LJS.mainCanvas.style.background = '#555';
50
+ LJS.setCanvasClearColor(rgb(.4,.4,.4));
51
51
  LJS.setGravity(vec2(0,-.005));
52
52
  sprites = [];
53
53
 
@@ -85,6 +85,7 @@ function gameInit()
85
85
  {
86
86
  new LJS.UISystemPlugin;
87
87
  createUI();
88
+ LJS.setCanvasClearColor(hsl(0,0,.2));
88
89
  }
89
90
 
90
91
  ///////////////////////////////////////////////////////////////////////////////
@@ -110,7 +111,6 @@ function gameUpdatePost()
110
111
  function gameRender()
111
112
  {
112
113
  // test game rendering
113
- LJS.drawRect(vec2(), vec2(1e3), hsl(0,0,.2));
114
114
  for (let i=0; i<1e3; ++i)
115
115
  {
116
116
  const pos = vec2(30*Math.sin(i+LJS.time/9),20*Math.sin(i*i+LJS.time/9));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "littlejsengine",
3
- "version": "1.14.5",
3
+ "version": "1.14.8",
4
4
  "description": "LittleJS - Tiny and Fast HTML5 Game Engine",
5
5
  "main": "dist/littlejs.esm.js",
6
6
  "types": "dist/littlejs.d.ts",
@@ -49,6 +49,8 @@ class UISystemPlugin
49
49
  this.defaultLineWidth = 4;
50
50
  /** @property {number} - Default rounded rect corner radius for UI elements */
51
51
  this.defaultCornerRadius = 0;
52
+ /** @property {number} - Default scale to use for fitting text to object */
53
+ this.defaultTextScale = .8;
52
54
  /** @property {string} - Default font for UI elements */
53
55
  this.defaultFont = 'arial';
54
56
  /** @property {Sound} - Default sound when interactive UI element is pressed */
@@ -61,10 +63,15 @@ class UISystemPlugin
61
63
  this.uiObjects = [];
62
64
  /** @property {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} - Context to render UI elements to */
63
65
  this.uiContext = context;
66
+ /** @property {UIObject} - Top most object user is over */
67
+ this.hoverObject = undefined;
68
+ /** @property {UIObject} - Object user is currently interacting with */
69
+ this.activeObject = undefined;
64
70
 
65
71
  engineAddPlugin(uiUpdate, uiRender);
66
72
 
67
73
  // setup recursive update and render
74
+ // update in reverse order to detect mouse enter/leave
68
75
  function uiUpdate()
69
76
  {
70
77
  function updateInvisibleObject(o)
@@ -89,7 +96,13 @@ class UISystemPlugin
89
96
  else
90
97
  updateInvisibleObject(o);
91
98
  }
92
- uiSystem.uiObjects.forEach(o=> o.parent || updateObject(o));
99
+ // reset hover object at start of update
100
+ uiSystem.hoverObject = undefined;
101
+ for (let i = uiSystem.uiObjects.length; i--;)
102
+ {
103
+ const o = uiSystem.uiObjects[i];
104
+ o.parent || updateObject(o)
105
+ }
93
106
  }
94
107
  function uiRender()
95
108
  {
@@ -168,10 +181,11 @@ class UISystemPlugin
168
181
  * @param {number} [lineWidth=uiSystem.defaultLineWidth]
169
182
  * @param {Color} [lineColor=uiSystem.defaultLineColor]
170
183
  * @param {string} [align]
171
- * @param {string} [font=uiSystem.defaultFont] */
172
- drawText(text, pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, align='center', font=uiSystem.defaultFont)
184
+ * @param {string} [font=uiSystem.defaultFont]
185
+ * @param {boolean} [applyMaxWidth=true] */
186
+ drawText(text, pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, align='center', font=uiSystem.defaultFont, applyMaxWidth=true)
173
187
  {
174
- drawTextScreen(text, pos, size.y, color, lineWidth, lineColor, align, font, size.x, uiSystem.uiContext);
188
+ drawTextScreen(text, pos, size.y, color, lineWidth, lineColor, align, font, applyMaxWidth ? size.x : undefined, uiSystem.uiContext);
175
189
  }
176
190
  }
177
191
 
@@ -195,6 +209,8 @@ class UIObject
195
209
  this.size = size.copy();
196
210
  /** @property {Color} - Color of the object */
197
211
  this.color = uiSystem.defaultColor;
212
+ /** @property {Color} - Color of the object when active, uses color if undefined */
213
+ this.activeColor = undefined;
198
214
  /** @property {string} - Text for this ui object */
199
215
  this.text = undefined;
200
216
  /** @property {Color} - Color when disabled */
@@ -213,15 +229,19 @@ class UIObject
213
229
  this.cornerRadius = uiSystem.defaultCornerRadius;
214
230
  /** @property {string} - Font for this objecct */
215
231
  this.font = uiSystem.defaultFont;
232
+ /** @property {number} - Override for text width */
233
+ this.textWidth = undefined;
216
234
  /** @property {number} - Override for text height */
217
235
  this.textHeight = undefined;
236
+ /** @property {number} - Scale text to fit in the object */
237
+ this.textScale = uiSystem.defaultTextScale;
218
238
  /** @property {boolean} - Should this object be drawn */
219
239
  this.visible = true;
220
240
  /** @property {Array<UIObject>} - A list of this object's children */
221
241
  this.children = [];
222
242
  /** @property {UIObject} - This object's parent, position is in parent space */
223
243
  this.parent = undefined;
224
- /** @property {number} - Extra size added to make small buttons easier to touch on mobile devices */
244
+ /** @property {number} - Added size to make small buttons easier to touch on mobile devices */
225
245
  this.extraTouchSize = 0;
226
246
  /** @property {Sound} - Sound when interactive element is pressed */
227
247
  this.soundPress = uiSystem.defaultSoundPress;
@@ -229,12 +249,10 @@ class UIObject
229
249
  this.soundRelease = uiSystem.defaultSoundRelease;
230
250
  /** @property {Sound} - Sound when interactive element is clicked */
231
251
  this.soundClick = uiSystem.defaultSoundClick;
232
- /** @property {boolean} - Is the mouse over this element */
233
- this.mouseIsOver = false;
234
252
  /** @property {boolean} - Is this element interactive */
235
253
  this.interactive = false;
236
- /** @property {boolean} - Is the mouse held on this element (was pressed while over and hasn't been released) */
237
- this.mouseIsHeld = false;
254
+ /** @property {boolean} - Activate when dragged over with mouse held down */
255
+ this.dragActivate = false;
238
256
  uiSystem.uiObjects.push(this);
239
257
  }
240
258
 
@@ -261,15 +279,18 @@ class UIObject
261
279
  /** Update the object, called automatically by plugin once each frame */
262
280
  update()
263
281
  {
264
- const mouseWasOver = this.mouseIsOver;
265
- const mousePress = mouseWasPressed(0);
282
+ const wasHover = this.isHoverObject();
283
+ const isActive = this.isActiveObject();
266
284
  const mouseDown = mouseIsDown(0);
267
- if (mousePress || !mouseDown || this.mouseIsHeld)
285
+ const mousePress = this.dragActivate ? mouseDown : mouseWasPressed(0);
286
+ if (!uiSystem.hoverObject)
287
+ if (mousePress || !mouseDown || isActive)
268
288
  {
269
289
  const size = this.size.add(vec2(isTouchDevice && this.extraTouchSize || 0));
270
- this.mouseIsOver = isOverlapping(this.pos, size, mousePosScreen);
290
+ if (isOverlapping(this.pos, size, mousePosScreen))
291
+ uiSystem.hoverObject = this;
271
292
  }
272
- if (this.mouseIsOver)
293
+ if (this.isHoverObject())
273
294
  {
274
295
  if (mousePress)
275
296
  inputClearKey(0,0,0,1,0); // clear mouse was pressed state
@@ -282,10 +303,12 @@ class UIObject
282
303
  this.onPress();
283
304
  if (this.soundPress)
284
305
  this.soundPress.play();
306
+ if (uiSystem.activeObject && !isActive)
307
+ uiSystem.activeObject.onRelease();
308
+ uiSystem.activeObject = this;
285
309
  }
286
- this.mouseIsHeld = true;
287
310
  }
288
- if (!mouseDown && this.mouseIsHeld && this.interactive)
311
+ if (!mouseDown && uiSystem.activeObject === this && this.interactive)
289
312
  {
290
313
  this.onClick();
291
314
  if (this.soundClick)
@@ -293,37 +316,52 @@ class UIObject
293
316
  }
294
317
  }
295
318
  }
296
- if (!mouseDown)
319
+ if (isActive && (!mouseDown || !this.isHoverObject()))
297
320
  {
298
- if (this.mouseIsHeld && this.interactive)
299
- {
300
- this.onRelease();
301
- if (this.soundRelease)
302
- this.soundRelease.play();
303
- }
304
- if (isTouchDevice)
305
- this.mouseIsOver = false;
306
- this.mouseIsHeld = false;
321
+ this.onRelease();
322
+ if (this.soundRelease)
323
+ this.soundRelease.play();
324
+ uiSystem.activeObject = undefined;
307
325
  }
308
326
 
309
- if (this.mouseIsOver !== mouseWasOver)
310
- this.mouseIsOver ? this.onEnter() : this.onLeave();
327
+ if (this.isHoverObject() !== wasHover)
328
+ this.isHoverObject() ? this.onEnter() : this.onLeave();
311
329
  }
312
330
 
313
331
  /** Render the object, called automatically by plugin once each frame */
314
332
  render()
315
333
  {
316
- if (this.size.x && this.size.y)
317
- uiSystem.drawRect(this.pos, this.size, this.color, this.lineWidth, this.lineColor, this.cornerRadius);
334
+ if (!this.size.x || !this.size.y) return;
335
+
336
+ const lineColor = this.interactive && this.isActiveObject() && !this.disabled ? this.color : this.lineColor;
337
+ const color = this.interactive ? this.disabled ? this.disabledColor : this.isActiveObject() ? this.activeColor || this.color : this.isHoverObject() ? this.hoverColor : this.color : this.color;
338
+ uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
318
339
  }
319
340
 
320
341
  /** Special update when object is not visible */
321
342
  updateInvisible()
322
343
  {
323
344
  // reset input state when not visible
324
- this.mouseIsOver = this.mouseIsHeld = false;
345
+ if (this.isActiveObject())
346
+ uiSystem.activeObject = undefined;
347
+ }
348
+
349
+ /** Get the size for text with overrides and scale
350
+ * @return {Vector2}
351
+ */
352
+ getTextSize()
353
+ {
354
+ return vec2(
355
+ this.textWidth || this.textScale * this.size.x,
356
+ this.textHeight || this.textScale * this.size.y);
325
357
  }
326
358
 
359
+ /** @return {boolean} - Is the mouse hovering over this element */
360
+ isHoverObject() { return uiSystem.hoverObject === this; }
361
+
362
+ /** @return {boolean} - Is the mouse held onto this element */
363
+ isActiveObject() { return uiSystem.activeObject === this; }
364
+
327
365
  /** Called when the mouse enters the object */
328
366
  onEnter() {}
329
367
 
@@ -371,7 +409,7 @@ class UIText extends UIObject
371
409
  }
372
410
  render()
373
411
  {
374
- const textSize = vec2(this.size.x, this.textHeight || this.size.y);
412
+ const textSize = this.getTextSize();
375
413
  uiSystem.drawText(this.text, this.pos, textSize, this.textColor, this.lineWidth, this.lineColor, this.align, this.font);
376
414
  }
377
415
  }
@@ -434,14 +472,10 @@ class UIButton extends UIObject
434
472
  }
435
473
  render()
436
474
  {
437
- // draw the button
438
- const lineColor = this.mouseIsHeld && !this.disabled ? this.color : this.lineColor;
439
- const color = this.disabled ? this.disabledColor : this.mouseIsOver ? this.hoverColor : this.color;
440
- uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
475
+ super.render();
441
476
 
442
- // draw the text
443
- const textScale = .8; // scale text to fit
444
- const textSize = vec2(this.size.x, this.textHeight || this.size.y*textScale);
477
+ // draw the text scaled to fit
478
+ const textSize = this.getTextSize();
445
479
  uiSystem.drawText(this.text, this.pos, textSize,
446
480
  this.textColor, 0, undefined, this.align, this.font);
447
481
  }
@@ -479,8 +513,7 @@ class UICheckbox extends UIObject
479
513
  }
480
514
  render()
481
515
  {
482
- const color = this.disabled ? this.disabledColor : this.mouseIsOver ? this.hoverColor : this.color;
483
- uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, this.lineColor, this.cornerRadius);
516
+ super.render();
484
517
  if (this.checked)
485
518
  {
486
519
  const p = this.cornerRadius / min(this.size.x, this.size.y) * 2;
@@ -490,13 +523,11 @@ class UICheckbox extends UIObject
490
523
  uiSystem.drawLine(this.pos.add(s.multiply(vec2(-1,1))), this.pos.add(s.multiply(vec2(1,-1))), this.lineWidth, this.lineColor);
491
524
  }
492
525
 
493
- // draw the text to the right side of the checkbox
494
- const textScale = .8; // scale text to fit
495
- const gapScale = .55;
496
- const textSize = vec2(this.size.x, this.textHeight || this.size.y*textScale);
497
- const pos = this.pos.add(vec2(this.size.x*gapScale,0));
526
+ // draw the text next to the checkbox
527
+ const textSize = this.getTextSize();
528
+ const pos = this.pos.add(vec2(this.size.x,0));
498
529
  uiSystem.drawText(this.text, pos, textSize,
499
- this.textColor, 0, undefined, 'left', this.font);
530
+ this.textColor, 0, undefined, 'left', this.font, false);
500
531
  }
501
532
  }
502
533
 
@@ -532,7 +563,7 @@ class UIScrollbar extends UIObject
532
563
  update()
533
564
  {
534
565
  super.update();
535
- if (this.mouseIsHeld && this.interactive)
566
+ if (this.isActiveObject() && this.interactive)
536
567
  {
537
568
  // check if value changed
538
569
  const handleSize = vec2(this.size.y);
@@ -546,12 +577,7 @@ class UIScrollbar extends UIObject
546
577
  }
547
578
  render()
548
579
  {
549
- // draw the scrollbar background
550
- const lineColor = this.interactive && this.mouseIsHeld && !this.disabled ?
551
- this.color : this.lineColor;
552
- const color = this.disabled ? this.disabledColor :
553
- this.interactive && this.mouseIsHeld ? this.hoverColor : this.color;
554
- uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
580
+ super.render();
555
581
 
556
582
  // draw the scrollbar handle
557
583
  const handleSize = vec2(this.size.y);
@@ -560,12 +586,11 @@ class UIScrollbar extends UIObject
560
586
  const p2 = this.pos.x + handleWidth/2;
561
587
  const handlePos = vec2(lerp(p1, p2, this.value), this.pos.y);
562
588
  const handleColor = this.disabled ? this.disabledColor :
563
- this.interactive && this.mouseIsHeld ? this.color : this.handleColor;
589
+ this.interactive && this.isActiveObject() ? this.color : this.handleColor;
564
590
  uiSystem.drawRect(handlePos, handleSize, handleColor, this.lineWidth, this.lineColor, this.cornerRadius);
565
591
 
566
- // draw the text on the scrollbar
567
- const textScale = .8; // scale text to fit in scrollbar
568
- const textSize = vec2(this.size.x, this.textHeight || this.size.y*textScale);
592
+ // draw the text scaled to fit on the scrollbar
593
+ const textSize = this.getTextSize();
569
594
  uiSystem.drawText(this.text, this.pos, textSize,
570
595
  this.textColor, 0, undefined, this.align, this.font);
571
596
  }
package/reference.md CHANGED
@@ -186,6 +186,7 @@ getCameraSize() // Get the camera's visible area in world space
186
186
  // Display settings
187
187
  canvasMaxSize = (1920, 1200) // The max size of the canvas
188
188
  canvasFixedSize = (0, 0) // Fixed size of the canvas
189
+ canvasClearColor = BLACK // Color used to clear the canvas at start of frame
189
190
  fontDefault = 'arial' // Default font used for text rendering
190
191
  canvasPixelated = true // Use nearest neighbor canvas scaling for more pixelated look
191
192
  tilesPixelated = true // Disable filtering for crisper pixel art
package/src/engine.js CHANGED
@@ -30,7 +30,7 @@ const engineName = 'LittleJS';
30
30
  * @type {string}
31
31
  * @default
32
32
  * @memberof Engine */
33
- const engineVersion = '1.14.5';
33
+ const engineVersion = '1.14.8';
34
34
 
35
35
  /** Frames per second to update
36
36
  * @type {number}
@@ -169,15 +169,15 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
169
169
  frameTimeBufferMS += paused ? 0 : frameTimeDeltaMS;
170
170
  if (!debugSpeedUp)
171
171
  frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp min framerate
172
- if (debugVideoCaptureIsActive())
173
- frameTimeBufferMS = 0; // no time smoothing when capturing video
174
- updateCanvas();
175
172
 
176
173
  if (paused)
177
174
  {
175
+ updateCanvas();
176
+
178
177
  // update object transforms even when paused
179
178
  for (const o of engineObjects)
180
179
  o.parent || o.updateTransforms();
180
+
181
181
  inputUpdate();
182
182
  pluginUpdateList.forEach(f=>f());
183
183
  debugUpdate();
@@ -202,6 +202,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
202
202
  time = frame++ / frameRate;
203
203
 
204
204
  // update game and objects
205
+ updateCanvas();
205
206
  inputUpdate();
206
207
  gameUpdate();
207
208
  pluginUpdateList.forEach(f=>f());
@@ -211,14 +212,23 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
211
212
  debugUpdate();
212
213
  gameUpdatePost();
213
214
  inputUpdatePost();
215
+
216
+ if (debugVideoCaptureIsActive())
217
+ renderFrame();
214
218
  }
215
219
 
216
220
  // add the time smoothing back in
217
221
  frameTimeBufferMS += deltaSmooth;
218
222
  }
219
223
 
220
- if (!headlessMode)
224
+ if (!debugVideoCaptureIsActive())
225
+ renderFrame();
226
+ requestAnimationFrame(engineUpdate);
227
+
228
+ function renderFrame()
221
229
  {
230
+ if (headlessMode) return;
231
+
222
232
  // render sort then render while removing destroyed objects
223
233
  enginePreRender();
224
234
  gameRender();
@@ -248,7 +258,6 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
248
258
  }
249
259
  drawCount = 0;
250
260
  }
251
- requestAnimationFrame(engineUpdate);
252
261
  }
253
262
 
254
263
  function updateCanvas()
@@ -274,6 +283,14 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
274
283
  mainCanvas.height = min(innerHeight, canvasMaxSize.y);
275
284
  }
276
285
 
286
+ // apply the clear color to main canvas
287
+ if (canvasClearColor.a > 0)
288
+ {
289
+ mainContext.fillStyle = canvasClearColor.toString();
290
+ mainContext.fillRect(0, 0, mainCanvasSize.x, mainCanvasSize.y);
291
+ mainContext.fillStyle = BLACK.toString();
292
+ }
293
+
277
294
  // clear overlay canvas and set size
278
295
  overlayCanvas.width = mainCanvas.width;
279
296
  overlayCanvas.height = mainCanvas.height;
@@ -524,7 +524,7 @@ function debugVideoCaptureStart()
524
524
  const chunks = [];
525
525
  debugVideoCaptureTrack = stream.getVideoTracks()[0];
526
526
  if (debugVideoCaptureTrack.applyConstraints)
527
- debugVideoCaptureTrack.applyConstraints({frameRate:frameRate}); // force 60 fps
527
+ debugVideoCaptureTrack.applyConstraints({frameRate:60}); // force 60 fps
528
528
  debugVideoCapture = new MediaRecorder(stream, {mimeType:'video/webm;codecs=vp8'});
529
529
  debugVideoCapture.ondataavailable = (e)=> chunks.push(e.data);
530
530
  debugVideoCapture.onstop = ()=>