litecanvas 0.98.0 → 0.98.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/README.md CHANGED
@@ -28,74 +28,205 @@ Litecanvas is a lightweight HTML5 canvas 2D engine suitable for small web games,
28
28
  - **Extensible**: Use or create [plugins](https://www.npmjs.com/search?q=keywords:litecanvas) to add functionalities or change the engine.
29
29
  - **Playground**: Access or install the [playground](https://litecanvas.js.org/) webapp to code and share games (even offline).
30
30
 
31
- [Learn more in the cheatsheet...](https://litecanvas.js.org/about.html)
32
-
33
31
  ## Getting Started
34
32
 
35
- You can try our [online playground](https://litecanvas.github.io) or install the NPM package:
33
+ ### Installation
34
+
35
+ You can get started using our [online playground](https://litecanvas.github.io).
36
+
37
+ Or installing our package via NPM:
36
38
 
37
39
  ```sh
38
40
  npm i litecanvas
39
41
  ```
40
42
 
41
- or just use add a `<script>` tag with our CDN link:
43
+ Or just create a HTML file and add a `<script>` tag with our CDN link:
42
44
 
43
45
  ```html
44
46
  <script src="https://unpkg.com/litecanvas"></script>
45
47
  ```
46
48
 
47
- ### Show me the code!
49
+ ### Basic game structure
48
50
 
49
51
  ```js
50
- // import the package if you installed via NPM
51
- import litecanvas from 'litecanvas'
52
+ litecanvas()
53
+
54
+ function init() {
55
+ // this functions is called one time only
56
+ // before the game starts
57
+ }
58
+
59
+ function update(dt) {
60
+ // this functions is called 60 times per second
61
+ // your game logic goes here
62
+ }
52
63
 
53
- // Start and setup the engine
54
- // learn more: https://litecanvas.js.org/about.html#settings
64
+ function draw() {
65
+ // this functions is called 60 times per second
66
+ // your game rendering goes here
67
+ }
68
+ ```
69
+
70
+ > **Note**: if you installed via NPM you need to import the package first: <br/>
71
+ > `import litecanvas from "litecanvas"`
72
+
73
+ ### Set the width and height of the game screen
74
+
75
+ ```js
76
+ // example: a game screen size equal to 480x360
55
77
  litecanvas({
56
- loop: { init, update, draw, tapped },
78
+ width: 480,
79
+ height: 360,
57
80
  })
81
+ ```
58
82
 
59
- // this function runs once at the beginning
60
- function init() {
61
- bg = 0 // the color #0 (black)
62
- color = 3 // the color #3 (white)
63
- radius = W / 10 // the canvas Width/10
64
- posx = W / 2 // center X (or canvas Width/2)
65
- posy = H / 2 // center Y (or canvas Height/2)
83
+ ### Colors
84
+
85
+ Litecanvas has a default palette with 12 colors:
86
+
87
+ | # | Color | # | Color |
88
+ | --- | ---------- | --- | ----------- |
89
+ | 0 | Black | 6 | Dark blue |
90
+ | 1 | Dark grey | 7 | Light blue |
91
+ | 2 | Light grey | 8 | Dark green |
92
+ | 3 | White | 9 | Light green |
93
+ | 4 | Red | 10 | Brown |
94
+ | 5 | Yellow | 11 | Beige |
95
+
96
+ ![The litecanvas color palette](.github/_assets/palette.png)
97
+
98
+ Each time a Litecanvas' function ask for a color, you should use an of theses colors by its index.
99
+
100
+ ```js
101
+ // example: draw a white rectangle
102
+ color = 3
103
+ rectfill(0, 0, 32, 32, color)
104
+ ```
105
+
106
+ ### Printing messages
107
+
108
+ ```js
109
+ litecanvas()
110
+
111
+ function draw() {
112
+ // clear and fill the game screen with color #0 (black)
113
+ cls(0)
114
+
115
+ // print a red "Hello" text at x=0, y=0
116
+ text(0, 0, 'Hello', 4)
117
+ }
118
+ ```
119
+
120
+ ### Drawing shapes
121
+
122
+ You can use the following functions to draw shapes:
123
+
124
+ - `rect(x, y, width, height, color)` draw a rectangle outline
125
+ - `rectfill(x, y, width, height, color)` draw a color-filled rectangle
126
+ - `circ(x, y, radius, color)` draw a circle outline
127
+ - `circfill(x, y, radius, color)` draw a color-filled circle
128
+ - `oval(x, y, rx, ry, color)` draw a ellipse outline
129
+ - `ovalfill(x, y, rx, ry, color)` draw a color-filled ellipse
130
+
131
+ ```js
132
+ litecanvas()
133
+
134
+ function draw() {
135
+ cls(0)
136
+
137
+ // draw a color filled rectangle at x=0 and y=0
138
+ // with width=32 and height=32
139
+ // and color=3 (white)
140
+ rectfill(0, 0, 32, 32, 3)
141
+
142
+ // draw a circle outline at x=64 and y=32
143
+ // with radius=40
144
+ // and color=5 (yellow)
145
+ circ(64, 32, 40, 5)
66
146
  }
147
+ ```
148
+
149
+ ### Keyboard
150
+
151
+ ```js
152
+ litecanvas()
153
+
154
+ function update() {
155
+ if (iskeydown('space')) {
156
+ // checks if the spacebar key is down
157
+ }
158
+
159
+ if (iskeypressed('a')) {
160
+ // checks if the "a" key was pressed
161
+ }
67
162
 
68
- // this function detect clicks/touches
69
- function tapped(x, y) {
70
- // changes the circle position
71
- // based on the position of the tap
72
- posx = x
73
- posy = y
163
+ // Returns the last key pressed in your keyboard.
164
+ const key = lastkey()
165
+
166
+ console.log(key)
74
167
  }
168
+ ```
75
169
 
76
- // put the game logic in this function
77
- function update(dt) {
78
- // make the circle falls 200 pixels per second
79
- posy += 200 * dt
170
+ > Note: you can call `iskeydown()` or `iskeypressed()` (without arguments) to check for any key.
171
+
172
+ ### Clicks and Touches
173
+
174
+ ```js
175
+ litecanvas()
176
+
177
+ let x, y
178
+
179
+ function tapped(tapX, tapY) {
180
+ // this function is called when a click or a touch happens
181
+ // tapX and tapY is where the tap happened
182
+ x = tapX
183
+ y = tapY
80
184
  }
81
185
 
82
- // put the game rendering in this function
83
186
  function draw() {
84
- cls(bg) // clear the screen
85
- circfill(posx, posy, radius, color) // draw a circle
86
- text(10, 10, 'Tap anywhere') // draw a text
187
+ cls(0)
188
+
189
+ if (x != null) {
190
+ // Draw a red circle wherever you tap
191
+ circfill(x, y, 32, 4)
192
+ }
193
+ }
194
+ ```
195
+
196
+ ### Mouse cursor
197
+
198
+ Use `MX` and `MY` variables (automatically declared by Litecanvas) to track the position of the mouse cursor.
199
+
200
+ ```js
201
+ litecanvas()
202
+
203
+ function draw() {
204
+ cls(0)
205
+
206
+ // draw a red circle in the mouse cursor's position
207
+ circfill(MX, MY, 32, 4)
87
208
  }
88
209
  ```
89
210
 
90
- [Play with this code in the playground](https://litecanvas.js.org?c=eJx9U8tu2zAQvOsrBsghcitYtnMz4HvvLdD2SJFriTVNEuSqthH430tSduQEQQUI0GNmdnZ22bb4ziIwhFWIxKMHDwSyvbZUtS0MiWBxdIG2GJh93Lat0UxS2L8iLv%2FEpQt9Kzo38nLgo3lKIqxtH6sZVb9WSJdxzm%2FxCm01Nxi9EkwNVBCnBiy8J4VrU10XVa7Lg47Yj1aydhZhtBHOSoLgYrCjZNCmOtUbJsvWC0y1uh47rFCECNIZF%2FC0Qt0ZIQ%2BLApk%2B7vDyAfWC%2BjQk7xMqCKXHmGA%2F0WI9K5bGcNKKh3a9KlDv4vkG3GScJMsU8At1kn1H2CzuhEsifPtA%2BP0p4fpJLIpSxAxptDzElt0oB4pzIlOo9bnB5Z5LrjII21Oc2tBBGspGdGbcIZ2IaRhZYZh%2Fwu3Le1J9bPf82MrlZtOP05h6caQ0917LNJ735meb0ybUih9MHsWBHh3uhTEx5b%2BC12dKjz4FFUk6q%2Bb6X3fYJMQXKP7MRyCrKKSd%2BY%2BXvI1vSyRNrLt%2BUULL56BIRRmIpqSyt702ps5RNMVDc1uYZtqmws2aELdOCpHpzPV61SDfzz%2BET8fvchoo0PMjI8Oq6z8yIyTy)
211
+ ### Litecanvas' variables
212
+
213
+ Like `MX` and `MY`, Litecanvas also declares these other variables:
214
+
215
+ - `W`: the width of the game canvas
216
+ - `H`: the height of the game canvas
217
+ - `T`: the amount of seconds since the game started
218
+ - `PI`: approximately 3.14 radians (or 180 degrees)
219
+ - `TWO_PI`: approximately 6.28 radians (or 360 degrees)
220
+ - `HALF_PI`: approximately 1.57 radians (or 90 degrees)
91
221
 
92
- https://github.com/user-attachments/assets/854ac6bd-724f-4da8-bb3c-bc04dba5d8c8
222
+ ### And much more!
223
+
224
+ You can find a complete list of everything litecanvas has to offer on our [cheatsheet](https://litecanvas.js.org/about.html).
93
225
 
94
226
  ## Demos
95
227
 
96
228
  Try some demos in the playground:
97
229
 
98
- - [Pong](https://litecanvas.js.org?c=eJy1VV1y00gQftcpmidJWJYl2U5IIKFMyiEPgKk4YMLW1paQxvYUiqSSxiT8hCtwgn3bQ3AeLsAV6J7RzyjEKV42rozdPT39%2B803CROQh%2FECDsAPPMcA%2FEP5hORxI74h0Wu3z1EeDSs5ZqUgA7JTmndhkmg%2Fz9ufc%2F6Jaa5JNUlXCelqb7yQ0RrpvJXKnLEYxaBOpYyyQjub8CUrURwqUbArQQErWxEWQh5fhknJDCPhgkVh%2BiEsrc%2FS4pLHYr0Pw7oPa8ZXa7EPoweouLYNYzCAo00psgt4%2B%2Bn4DZTZJo1LWGYFiDUvYRVeMLLJCyYEZ0Wfr1LMz4iytBTwZPbqxdEUw%2F%2FlgAM%2BG9KX6w1pCVDhQEAaB%2FZGXrVRf1zccXeU%2Fc7fD7fHmJ9NTs8ohOeOHewKjLFT8tgIl6EeZbzrVZHlUgXyRne5fzJ59uyf2fHx%2FOh0On1BcXx36MtyxhjAcz2KSnFw2ZXRpNTfc%2Fdw3aGVPp7r%2B%2FWvQJ6T60iuPlVoLDdpJHiWAk%2B5sGz43AIL4y4GQYsulE8GAfThBAYw6kyeTFHpB8a15lKEec5i68qBj7VjvgTrXoWQWtcFjSg2rFUvryzZa7tRYcM2RSrF6861%2BPrVusLk5CUbQGB3UtnkcSiYFYvbE1FOAQciuMBbUkYFY%2BnjxrIC%2FAHeAN2YgAjZB1Y8NqQpqi5QRJQyyiNOmH6xE1bklry8Kmc1SLs5GmepKdBKaOdRDCuHKqcmJen0EeVTuffkFsMr1%2B73VDsOYdGYLaoeGY0nNdxeyxuHcKLPpgaDv%2BN1dASI3VZVk4L67oPfmWIX0nY1v7p0NR7pFfKs5DQzDYi9A0VX9ytmug%2Bx0HCpts%2B724qosJnvkD0inY%2BqmEqvYiJCbrS3Cqw3ZQFfvlQJPWr0eqNUjgfQb0tvgze4vm5npNLf6kwy8p2ummKiNYveq1qiLEl4SaC%2F5GKt45BC4m7BIqHqc6pno0lA%2F6WQSo%2BQXBdIM%2FYtCWrFqleiR0%2FYH2QtG6B2bQURSdzdexsX4WXDSlFSWp59J48QIYUJkqllRiwVrDAdMC84tcC0O1YlFmnV%2FNXdspD2HOI6PHs2eQlnM0X46Gto35yhgvshXcQ2DWrxkieJ9XsT6dFv%2FMiqeBFJ220jqSliS%2Ba31S47Q6Wvw3TF09WN2i0f3yT6N3%2F8%2B9%2FP79%2FAdAuWs1CoamwHRrf7ZWl8h1eiltqzZZp4eyQibBdrnlNGFr6tnn2zi%2F%2FX9DCbAN9J8%2Bnk%2BRRmr6enZqfvv7lB3vDcB%2BNt3nqqY%2FOj2el0n4r7oxIRzb8ATsCfrQ%3D%3D)
99
230
  - [Bouncing Ball](https://litecanvas.js.org?c=eJxtkkFugzAQRfecYpaGOMFJW6lVQhddcQPWlm0iSy4gY0hRkrt3ADc4SRdI9nzm%2FzcMRjlo6lY7XVeQQa8E2TJG8YlpBHg3tdBuGM9Hy3t%2FtFzqrsX3X9%2BjyGinBK963pI4isquEpOZrrQjMZwDFx9QJDuap7t4v5h6hdEcq9fApWskd4pINzv9oW5%2BYJXdfPGWgHShPtzpw6wHKLOBT1%2F6g45AXtrTFL5qZFNQlyUoeVQtVnUJZCFb%2Ba%2FzCQVcLgHy2gsHYPM0dzhJBuvt%2Fp8q23y8TeVg%2BAyE4d9NkEr9UigUPifGpusjHc4FN778CWOYMJ4TxwXl3neyDXckLT%2F5TQvTEjYtVmgrSm1MiLjYUQ%2F78rDu8SfAUYbZzSrX2QrOYwUjfwF7%2FdPj)
100
231
  - [Scroller](https://litecanvas.js.org?c=eJxVUM1SgzAQvvMU68FpAhFDLVpH%2BxbOcOj0ECGUzATSIYsyOn13NwXRHpJNvv1%2BkrUGdam6D%2BUZj6J66Eo0rgPTGQTG4TsCaLX36qhhB6tC29K1GtCBXYQ3KyKN1C6onpSl034jcvEonsRWPItMiiw7ROd%2F9sOpUqiBVThFePMV%2FIv7OSu1ujtiQ52G4FxeAu528CAlxFDhHNiqkY0C1pJfuVe9%2Bvx9fGk9ozZA7XpgViMYEsoXKq9wnUZYkkwqgMBEYr4lTKZZbPiC0kLdh%2FRZ7Yd3jz0zArI%2FVumsCySax762zvUM4%2FyWbnMYP1yoqEcMn2dh4wvExsTEAYpluhXQJGuZeNORx4bHjZjfIKaUIDvTAH4AEIGEgw%3D%3D)
101
232
  - [3D projection](https://litecanvas.js.org?c=eJyNVcuS2jAQvPsrJofUykE8wykJySlVyRckuy4fZGwWgZEpWQSWFP%2BeGUlgizVLqgyyprul1mgkl9IUc6H%2BiJrFUbTYqbmRlQKppGEx%2FI0AanksYAa%2F4Cv8gG%2F4G8IUPmEfW4S3uloVTjSDBAMAyZjDCJ%2BUuy6%2Bj8MuRWw3pREqqUzdqPs0I4d2k3IYDiE5cHjhcEwdr4vWRjqA%2Fj2kRQgHuwm8jvc7gbTJVZHTYikg1HNJyR1Fp1byd9tcmILlxm2AI%2FWQNRiNIwwsKg2sLAxIkn7G5otP4qAs1LNZYqjXc2KAeaVqgzhl2LImG2G0PLDBYOBUiUxjS6UxdWWEc4i0za5kNoC2fjNrJOY0luPf5D5euJ5yh%2F%2FUzb%2FkCx02oqbiAvopyGGuxd4X8Lys2SimxG139ZIRt5Sq2MvcLNmEukYLVZeUc6rqCbdFbpGuVE%2Bv06vQDZMcmIQejGN4D1PeeI9DGlIQZS1u7EK3BBxkB%2BFkD84WV9Ne9VkkOGTc14QzSitmfrtFmozoOTMoMA4C2TUjuzA%2BhjP6PaEJfbZtuYl5VVLBCdT4omwwXe0tdg1kXpR1iDIvys4AInIBzE30bjZzjBjMElvA7YXvWleaPfzEy62UObiqL%2BoHWwpuVF3U56PYtdPWabDbKHC1mLhL6KJaOdUKVXYZ%2BNqo3Mmqdxt71F3kolw75Zrmc8p1WwlW16NUyjRZp%2FAB87NOk1XqCSffOmsYx%2FFQEp2xEy1XF2anFXGC3WvOtha5xEPg5vXsm7f5vKovAns1qla3TQyRUDby12KXn8e3%2FFwNE85y45tz7TE084aTp%2F93cisP95Pw6qPYdhLc2f4LGJpJDngukxf6O6Yk%2FgdWEko3)
@@ -103,6 +234,19 @@ Try some demos in the playground:
103
234
 
104
235
  > _See other demos in [samples](/samples) folder_
105
236
 
237
+ ## Contributing
238
+
239
+ 1. Fork this repository and clone it.
240
+ 1. Install the dependencies: `npm i`
241
+ 1. Create a new branch and make your changes.
242
+ 1. Format the code: `npm run format`
243
+ 1. Create new tests in `tests` directory, if necessary.
244
+ 1. Test with `npm run test`
245
+ 1. Create your pull request.
246
+ 1. Done!
247
+
248
+ > Note: You'll need Node.JS installed in your machine.
249
+
106
250
  ## Inspirations
107
251
 
108
252
  - [floppy](https://github.com/lpagg/floppy): a micro game engine for beginners.
package/dist/dist.dev.js CHANGED
@@ -32,7 +32,7 @@
32
32
  };
33
33
 
34
34
  // src/version.js
35
- var version = "0.98.0";
35
+ var version = "0.98.2";
36
36
 
37
37
  // src/index.js
38
38
  function litecanvas(settings = {}) {
@@ -50,7 +50,7 @@
50
50
  keyboardEvents: true
51
51
  };
52
52
  settings = Object.assign(defaults, settings);
53
- let _initialized = false, _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _fpsInterval = 1e3 / 60, _accumulated, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _currentPalette, _colors, _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1], _coreEvents = "init,update,draw,tap,untap,tapping,tapped,resized", _mathFunctions = "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp", _eventListeners = {};
53
+ let _initialized = false, _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _fpsInterval = 1e3 / 60, _accumulated, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colorPalette = defaultPalette, _colorPaletteState = [], _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1], _coreEvents = "init,update,draw,tap,untap,tapping,tapped,resized", _mathFunctions = "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp", _eventListeners = {};
54
54
  const instance = {
55
55
  /** @type {number} */
56
56
  W: 0,
@@ -565,7 +565,7 @@
565
565
  "[litecanvas] text() 5th param must be a string"
566
566
  );
567
567
  _ctx.font = `${fontStyle} ${_fontSize}px ${_fontFamily}`;
568
- _ctx.fillStyle = _colors[~~color % _colors.length];
568
+ _ctx.fillStyle = getColor(color);
569
569
  _ctx.fillText(message, ~~x, ~~y);
570
570
  },
571
571
  /**
@@ -772,7 +772,7 @@
772
772
  null == color || isNumber(color) && color >= 0,
773
773
  "[litecanvas] fill() 1st param must be a positive number or zero"
774
774
  );
775
- _ctx.fillStyle = _colors[~~color % _colors.length];
775
+ _ctx.fillStyle = getColor(color);
776
776
  _ctx.fill();
777
777
  },
778
778
  /**
@@ -785,7 +785,7 @@
785
785
  null == color || isNumber(color) && color >= 0,
786
786
  "[litecanvas] stroke() 1st param must be a positive number or zero"
787
787
  );
788
- _ctx.strokeStyle = _colors[~~color % _colors.length];
788
+ _ctx.strokeStyle = getColor(color);
789
789
  _ctx.stroke();
790
790
  },
791
791
  /**
@@ -913,7 +913,7 @@
913
913
  }
914
914
  },
915
915
  /**
916
- * Set or reset the color palette.
916
+ * Set new palette colors or restore the default palette.
917
917
  *
918
918
  * @param {string[]} [colors]
919
919
  */
@@ -922,14 +922,16 @@
922
922
  Array.isArray(colors) && colors.length > 0,
923
923
  "[litecanvas] pal() 1st param must be a array of strings"
924
924
  );
925
- _colors = colors;
926
- _currentPalette = [...colors];
925
+ _colorPalette = colors;
926
+ _colorPaletteState = [];
927
927
  },
928
928
  /**
929
- * Swap two colors of the current palette.
929
+ * Replace the color "a" with color "b".
930
930
  *
931
931
  * If called without arguments, reset the current palette.
932
932
  *
933
+ * Note: `palc()` don't affect drawings made with `image()`.
934
+ *
933
935
  * @param {number?} a
934
936
  * @param {number?} b
935
937
  */
@@ -943,10 +945,9 @@
943
945
  "[litecanvas] palc() 2nd param must be a positive number"
944
946
  );
945
947
  if (a == null) {
946
- _colors = [..._currentPalette];
948
+ _colorPaletteState = [];
947
949
  } else {
948
- ;
949
- [_colors[a], _colors[b]] = [_colors[b], _colors[a]];
950
+ _colorPaletteState[a] = b;
950
951
  }
951
952
  },
952
953
  /**
@@ -1016,7 +1017,7 @@
1016
1017
  // 4
1017
1018
  _eventListeners,
1018
1019
  // 5
1019
- _colors,
1020
+ _colorPalette,
1020
1021
  // 6
1021
1022
  _defaultSound,
1022
1023
  // 7
@@ -1410,6 +1411,10 @@
1410
1411
  instance.def(key, pluginData[key]);
1411
1412
  }
1412
1413
  }
1414
+ function getColor(index) {
1415
+ const i = _colorPaletteState[index] ?? index;
1416
+ return _colorPalette[~~i % _colorPalette.length];
1417
+ }
1413
1418
  if (settings.global) {
1414
1419
  if (root.ENGINE) {
1415
1420
  throw new Error("only one global litecanvas is allowed");
@@ -1420,7 +1425,6 @@
1420
1425
  DEV: console.info(`[litecanvas] version ${version} started`);
1421
1426
  DEV: console.debug(`[litecanvas] litecanvas() options =`, settings);
1422
1427
  setupCanvas();
1423
- instance.pal();
1424
1428
  if ("loading" === document.readyState) {
1425
1429
  on(root, "DOMContentLoaded", () => raf(init));
1426
1430
  } else {
package/dist/dist.js CHANGED
@@ -42,7 +42,7 @@
42
42
  keyboardEvents: true
43
43
  };
44
44
  settings = Object.assign(defaults, settings);
45
- let _initialized = false, _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _fpsInterval = 1e3 / 60, _accumulated, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _currentPalette, _colors, _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1], _coreEvents = "init,update,draw,tap,untap,tapping,tapped,resized", _mathFunctions = "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp", _eventListeners = {};
45
+ let _initialized = false, _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _fpsInterval = 1e3 / 60, _accumulated, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colorPalette = defaultPalette, _colorPaletteState = [], _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1], _coreEvents = "init,update,draw,tap,untap,tapping,tapped,resized", _mathFunctions = "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp", _eventListeners = {};
46
46
  const instance = {
47
47
  /** @type {number} */
48
48
  W: 0,
@@ -373,7 +373,7 @@
373
373
  */
374
374
  text(x, y, message, color = 3, fontStyle = "normal") {
375
375
  _ctx.font = `${fontStyle} ${_fontSize}px ${_fontFamily}`;
376
- _ctx.fillStyle = _colors[~~color % _colors.length];
376
+ _ctx.fillStyle = getColor(color);
377
377
  _ctx.fillText(message, ~~x, ~~y);
378
378
  },
379
379
  /**
@@ -528,7 +528,7 @@
528
528
  * @param {number} [color=0]
529
529
  */
530
530
  fill(color) {
531
- _ctx.fillStyle = _colors[~~color % _colors.length];
531
+ _ctx.fillStyle = getColor(color);
532
532
  _ctx.fill();
533
533
  },
534
534
  /**
@@ -537,7 +537,7 @@
537
537
  * @param {number} [color=0]
538
538
  */
539
539
  stroke(color) {
540
- _ctx.strokeStyle = _colors[~~color % _colors.length];
540
+ _ctx.strokeStyle = getColor(color);
541
541
  _ctx.stroke();
542
542
  },
543
543
  /**
@@ -631,28 +631,29 @@
631
631
  }
632
632
  },
633
633
  /**
634
- * Set or reset the color palette.
634
+ * Set new palette colors or restore the default palette.
635
635
  *
636
636
  * @param {string[]} [colors]
637
637
  */
638
638
  pal(colors = defaultPalette) {
639
- _colors = colors;
640
- _currentPalette = [...colors];
639
+ _colorPalette = colors;
640
+ _colorPaletteState = [];
641
641
  },
642
642
  /**
643
- * Swap two colors of the current palette.
643
+ * Replace the color "a" with color "b".
644
644
  *
645
645
  * If called without arguments, reset the current palette.
646
646
  *
647
+ * Note: `palc()` don't affect drawings made with `image()`.
648
+ *
647
649
  * @param {number?} a
648
650
  * @param {number?} b
649
651
  */
650
652
  palc(a, b) {
651
653
  if (a == null) {
652
- _colors = [..._currentPalette];
654
+ _colorPaletteState = [];
653
655
  } else {
654
- ;
655
- [_colors[a], _colors[b]] = [_colors[b], _colors[a]];
656
+ _colorPaletteState[a] = b;
656
657
  }
657
658
  },
658
659
  /**
@@ -704,7 +705,7 @@
704
705
  // 4
705
706
  _eventListeners,
706
707
  // 5
707
- _colors,
708
+ _colorPalette,
708
709
  // 6
709
710
  _defaultSound,
710
711
  // 7
@@ -1060,6 +1061,10 @@
1060
1061
  instance.def(key, pluginData[key]);
1061
1062
  }
1062
1063
  }
1064
+ function getColor(index) {
1065
+ const i = _colorPaletteState[index] ?? index;
1066
+ return _colorPalette[~~i % _colorPalette.length];
1067
+ }
1063
1068
  if (settings.global) {
1064
1069
  if (root.ENGINE) {
1065
1070
  throw new Error("only one global litecanvas is allowed");
@@ -1068,7 +1073,6 @@
1068
1073
  root.ENGINE = instance;
1069
1074
  }
1070
1075
  setupCanvas();
1071
- instance.pal();
1072
1076
  if ("loading" === document.readyState) {
1073
1077
  on(root, "DOMContentLoaded", () => raf(init));
1074
1078
  } else {
package/dist/dist.min.js CHANGED
@@ -1 +1 @@
1
- (()=>{var e=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(t={}){let a=window,l=Math,n=2*l.PI,i=requestAnimationFrame,o=[],r=(e,t,a)=>{e.addEventListener(t,a,!1),o.push(()=>e.removeEventListener(t,a,!1))},s=e=>e.toLowerCase(),c=e=>e.preventDefault(),f=e=>e.beginPath(),d=(e=>{let t=new AudioContext;return e.zzfxV=1,(a=1,l=.05,n=220,i=0,o=0,r=.1,s=0,c=1,f=0,d=0,u=0,p=0,h=0,m=0,g=0,v=0,w=0,x=1,y=0,b=0,k=0)=>{let E=Math,z=2*E.PI,T=f*=500*z/44100/44100,I=n*=(1-l+2*l*E.random(l=[]))*z/44100,D=0,S=0,A=0,M=1,C=0,L=0,N=0,P=k<0?-1:1,F=z*P*k*2/44100,q=E.cos(F),B=E.sin,H=B(F)/4,O=1+H,V=-2*q/O,W=(1-H)/O,R=(1+P*q)/2/O,G=-(P+q)/O,X=0,Y=0,$=0,j=0;for(i=44100*i+9,y*=44100,o*=44100,r*=44100,w*=44100,d*=500*z/85766121e6,g*=z/44100,u*=z/44100,p*=44100,h=44100*h|0,a*=.3*e.zzfxV,P=i+y+o+r+w|0;A<P;l[A++]=N*a)++L%(100*v|0)||(N=s?1<s?2<s?3<s?B(D*D):E.max(E.min(E.tan(D),1),-1):1-(2*D/z%2+2)%2:1-4*E.abs(E.round(D/z)-D/z):B(D),N=(h?1-b+b*B(z*A/h):1)*(N<0?-1:1)*E.abs(N)**c*(A<i?A/i:A<i+y?1-(A-i)/y*(1-x):A<i+y+o?x:A<P-w?(P-A-w)/r*x:0),N=w?N/2+(w>A?0:(A<P-w?1:(P-A)/w)*l[A-w|0]/2/a):N,k&&(N=j=R*X+G*(X=Y)+R*(Y=N)-W*$-V*($=j))),D+=(F=(n+=f+=d)*E.cos(g*S++))+F*m*B(A**5),M&&++M>p&&(n+=u,I+=u,M=0),!h||++C%h||(n=I,f=T,M=M||1);(a=t.createBuffer(1,P,44100)).getChannelData(0).set(l),(n=t.createBufferSource()).buffer=a,n.connect(t.destination),n.start()}})(a);t=Object.assign({width:null,height:null,autoscale:!0,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0},t);let u=!1,p,h=1,m,g=.5,v=1,w,x=1e3/60,y,b,k="sans-serif",E=20,z=Date.now(),T,I,D=[.5,0,1750,,,.3,1,,,,600,.1],S={},A={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:n,HALF_PI:n/4,lerp:(e,t,a)=>a*(t-e)+e,deg2rad:e=>l.PI/180*e,rad2deg:e=>180/l.PI*e,round:(e,t=0)=>{if(!t)return l.round(e);let a=10**t;return l.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,wrap:(e,t,a)=>e-(a-t)*l.floor((e-t)/(a-t)),map(e,t,a,l,n,i){let o=(e-t)/(a-t)*(n-l)+l;return i?A.clamp(o,l,n):o},norm:(e,t,a)=>A.map(e,t,a,0,1),wave:(e,t,a,l=Math.sin)=>e+(l(a)+1)/2*(t-e),rand:(e=0,t=1)=>(z=(1664525*z+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>l.floor(A.rand(e,t+1)),rseed(e){z=~~e},cls(e){null==e?m.clearRect(0,0,m.canvas.width,m.canvas.height):A.rectfill(0,0,m.canvas.width,m.canvas.height,e)},rect(e,t,a,l,n,i){f(m),m[i?"roundRect":"rect"](~~e-g,~~t-g,~~a+2*g,~~l+2*g,i),A.stroke(n)},rectfill(e,t,a,l,n,i){f(m),m[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),A.fill(n)},circ(e,t,a,l){f(m),m.arc(~~e,~~t,~~a,0,n),A.stroke(l)},circfill(e,t,a,l){f(m),m.arc(~~e,~~t,~~a,0,n),A.fill(l)},oval(e,t,a,l,i){f(m),m.ellipse(~~e,~~t,~~a,~~l,0,0,n),A.stroke(i)},ovalfill(e,t,a,l,i){f(m),m.ellipse(~~e,~~t,~~a,~~l,0,0,n),A.fill(i)},line(e,t,a,l,n){f(m);let i=.5*(0!==g&&~~e==~~a),o=.5*(0!==g&&~~t==~~l);m.moveTo(~~e+i,~~t+o),m.lineTo(~~a+i,~~l+o),A.stroke(n)},linewidth(e){m.lineWidth=~~e,g=.5*(0!=~~e%2)},linedash(e,t=0){m.setLineDash(e),m.lineDashOffset=t},text(e,t,a,l=3,n="normal"){m.font=`${n} ${E}px ${k}`,m.fillStyle=I[~~l%I.length],m.fillText(a,~~e,~~t)},textfont(e){k=e},textsize(e){E=e},textalign(e,t){e&&(m.textAlign=e),t&&(m.textBaseline=t)},image(e,t,a){m.drawImage(a,~~e,~~t)},spr(e,t,a,l,n){let i=n.replace(/\s/g,"");for(let n=0;n<a;n++)for(let a=0;a<l;a++){let o=i[l*a+n]||".";"."!==o&&A.rectfill(e+n,t+a,1,1,parseInt(o,16)||0)}},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,o=m;return n.width=e*i,n.height=t*i,(m=n.getContext("2d")).scale(i,i),a(m),m=o,n.transferToImageBitmap()},ctx:e=>(e&&(m=e),m),push(){m.save()},pop(){m.restore()},translate(e,t){m.translate(~~e,~~t)},scale(e,t){m.scale(e,t||e)},rotate(e){m.rotate(e)},alpha(e){m.globalAlpha=A.clamp(e,0,1)},fill(e){m.fillStyle=I[~~e%I.length],m.fill()},stroke(e){m.strokeStyle=I[~~e%I.length],m.stroke()},clip(e){f(m),e(m),m.clip()},sfx:(e,t=0,l=1)=>!!a.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||D,(0!==t||1!==l)&&((e=e.slice())[0]=l*(e[0]||1),e[10]=~~e[10]+t),d.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>p,use(e,t={}){var a=e,l=t;let n=a(A,l);for(let e in n)A.def(e,n[e])},listen:(e,t)=>(S[e=s(e)]=S[e]||new Set,S[e].add(t),()=>S&&S[e].delete(t)),emit(e,t,a,l,n){u&&(N("before:"+(e=s(e)),t,a,l,n),N(e,t,a,l,n),N("after:"+e,t,a,l,n))},pal(t=e){I=t,T=[...t]},palc(e,t){null==e?I=[...T]:[I[e],I[t]]=[I[t],I[e]]},def(e,l){A[e]=l,t.global&&(a[e]=l)},timescale(e){v=e},framerate(e){x=1e3/~~e},stat(e){let l={index:e,value:[t,u,x/1e3,h,S,I,D,v,a.zzfxV,z,E,k][e]};return A.emit("stat",l),l.value},quit(){for(let e of(A.pause(),A.emit("quit"),S={},o))e();if(t.global){for(let e in A)delete a[e];delete a.ENGINE}u=!1},pause(){cancelAnimationFrame(b),b=0},resume(){u&&!b&&(y=x,w=Date.now(),b=i(C))},paused:()=>!b};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))A[e]=l[e];function M(){let e=t.loop?t.loop:a;for(let t of"init,update,draw,tap,untap,tapping,tapped,resized".split(","))e[t]&&A.listen(t,e[t]);if(t.autoscale&&r(a,"resize",L),t.tapEvents){let e=e=>[(e.pageX-p.offsetLeft)/h,(e.pageY-p.offsetTop)/h],t=new Map,l=(e,a,l)=>{let n={x:a,y:l,xi:a,yi:l,t:Date.now()};return t.set(e,n),n},n=(e,a,n)=>{let i=t.get(e)||l(e);i.x=a,i.y=n},i=e=>e&&Date.now()-e.t<=300,o=!1;r(p,"mousedown",t=>{if(0===t.button){c(t);let[a,n]=e(t);A.emit("tap",a,n,0),l(0,a,n),o=!0}}),r(p,"mouseup",a=>{if(0===a.button){c(a);let l=t.get(0),[n,r]=e(a);i(l)&&A.emit("tapped",l.xi,l.yi,0),A.emit("untap",n,r,0),t.delete(0),o=!1}}),r(a,"mousemove",t=>{c(t);let[a,l]=e(t);A.def("MX",a),A.def("MY",l),o&&(A.emit("tapping",a,l,0),n(0,a,l))}),r(p,"touchstart",t=>{for(let a of(c(t),t.changedTouches)){let[t,n]=e(a);A.emit("tap",t,n,a.identifier+1),l(a.identifier+1,t,n)}}),r(p,"touchmove",t=>{for(let a of(c(t),t.changedTouches)){let[t,l]=e(a);A.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let s=e=>{c(e);let a=[];if(e.targetTouches.length>0)for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,l]of t)a.includes(e)||(i(l)&&A.emit("tapped",l.xi,l.yi,e),A.emit("untap",l.x,l.y,e),t.delete(e))};r(p,"touchend",s),r(p,"touchcancel",s),r(a,"blur",()=>{for(let[e,a]of(o=!1,t))A.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,l=(e,t="")=>(t=s(t))?e.has("space"===t?" ":t):e.size>0,n="";r(a,"keydown",a=>{let l=s(a.key);e.has(l)||(e.add(l),t.add(l),n=" "===l?"space":l)}),r(a,"keyup",t=>{e.delete(s(t.key))}),r(a,"blur",()=>e.clear()),A.listen("after:update",()=>t.clear()),A.def("iskeydown",t=>l(e,t)),A.def("iskeypressed",e=>l(t,e)),A.def("lastkey",()=>n)}u=!0,A.emit("init",A),A.resume()}function C(){b=i(C);let e=Date.now(),t=0,a=e-w;for(w=e,y+=a<100?a:x;y>=x;){t++,y-=x;let e=x/1e3*v;A.emit("update",e,t),A.def("T",A.T+e)}t&&(A.emit("draw",m),t>1&&(y=0))}function L(){let e=t.width>0?t.width:innerWidth,a=t.width>0?t.height||t.width:innerHeight;if(A.def("W",e),A.def("H",a),p.width=e,p.height=a,t.autoscale){let n=+t.autoscale;p.style.display||(p.style.display="block",p.style.margin="auto"),h=l.min(innerWidth/e,innerHeight/a),h=n>1&&h>n?n:h,p.style.width=e*h+"px",p.style.height=a*h+"px"}m.imageSmoothingEnabled=!1,A.textalign("start","top"),A.emit("resized",h)}function N(e,t,a,l,n){if(S[e])for(let i of S[e])i(t,a,l,n)}if(t.global){if(a.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(a,A),a.ENGINE=A}return m=(p=(p="string"==typeof t.canvas?document.querySelector(t.canvas):t.canvas)||document.createElement("canvas")).getContext("2d"),r(p,"click",()=>focus()),L(),p.parentNode||document.body.appendChild(p),p.style.imageRendering="pixelated",p.oncontextmenu=()=>!1,A.pal(),"loading"===document.readyState?r(a,"DOMContentLoaded",()=>i(M)):i(M),A}})();
1
+ (()=>{var e=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(t={}){let a=window,l=Math,n=2*l.PI,i=requestAnimationFrame,o=[],r=(e,t,a)=>{e.addEventListener(t,a,!1),o.push(()=>e.removeEventListener(t,a,!1))},s=e=>e.toLowerCase(),c=e=>e.preventDefault(),f=e=>e.beginPath(),d=(e=>{let t=new AudioContext;return e.zzfxV=1,(a=1,l=.05,n=220,i=0,o=0,r=.1,s=0,c=1,f=0,d=0,u=0,p=0,h=0,m=0,g=0,v=0,w=0,x=1,y=0,b=0,k=0)=>{let E=Math,z=2*E.PI,T=f*=500*z/44100/44100,I=n*=(1-l+2*l*E.random(l=[]))*z/44100,D=0,S=0,A=0,M=1,C=0,L=0,N=0,P=k<0?-1:1,F=z*P*k*2/44100,q=E.cos(F),B=E.sin,H=B(F)/4,O=1+H,V=-2*q/O,W=(1-H)/O,R=(1+P*q)/2/O,G=-(P+q)/O,X=0,Y=0,$=0,j=0;for(i=44100*i+9,y*=44100,o*=44100,r*=44100,w*=44100,d*=500*z/85766121e6,g*=z/44100,u*=z/44100,p*=44100,h=44100*h|0,a*=.3*e.zzfxV,P=i+y+o+r+w|0;A<P;l[A++]=N*a)++L%(100*v|0)||(N=s?1<s?2<s?3<s?B(D*D):E.max(E.min(E.tan(D),1),-1):1-(2*D/z%2+2)%2:1-4*E.abs(E.round(D/z)-D/z):B(D),N=(h?1-b+b*B(z*A/h):1)*(N<0?-1:1)*E.abs(N)**c*(A<i?A/i:A<i+y?1-(A-i)/y*(1-x):A<i+y+o?x:A<P-w?(P-A-w)/r*x:0),N=w?N/2+(w>A?0:(A<P-w?1:(P-A)/w)*l[A-w|0]/2/a):N,k&&(N=j=R*X+G*(X=Y)+R*(Y=N)-W*$-V*($=j))),D+=(F=(n+=f+=d)*E.cos(g*S++))+F*m*B(A**5),M&&++M>p&&(n+=u,I+=u,M=0),!h||++C%h||(n=I,f=T,M=M||1);(a=t.createBuffer(1,P,44100)).getChannelData(0).set(l),(n=t.createBufferSource()).buffer=a,n.connect(t.destination),n.start()}})(a);t=Object.assign({width:null,height:null,autoscale:!0,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0},t);let u=!1,p,h=1,m,g=.5,v=1,w,x=1e3/60,y,b,k="sans-serif",E=20,z=Date.now(),T=e,I=[],D=[.5,0,1750,,,.3,1,,,,600,.1],S={},A={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:n,HALF_PI:n/4,lerp:(e,t,a)=>a*(t-e)+e,deg2rad:e=>l.PI/180*e,rad2deg:e=>180/l.PI*e,round:(e,t=0)=>{if(!t)return l.round(e);let a=10**t;return l.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,wrap:(e,t,a)=>e-(a-t)*l.floor((e-t)/(a-t)),map(e,t,a,l,n,i){let o=(e-t)/(a-t)*(n-l)+l;return i?A.clamp(o,l,n):o},norm:(e,t,a)=>A.map(e,t,a,0,1),wave:(e,t,a,l=Math.sin)=>e+(l(a)+1)/2*(t-e),rand:(e=0,t=1)=>(z=(1664525*z+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>l.floor(A.rand(e,t+1)),rseed(e){z=~~e},cls(e){null==e?m.clearRect(0,0,m.canvas.width,m.canvas.height):A.rectfill(0,0,m.canvas.width,m.canvas.height,e)},rect(e,t,a,l,n,i){f(m),m[i?"roundRect":"rect"](~~e-g,~~t-g,~~a+2*g,~~l+2*g,i),A.stroke(n)},rectfill(e,t,a,l,n,i){f(m),m[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),A.fill(n)},circ(e,t,a,l){f(m),m.arc(~~e,~~t,~~a,0,n),A.stroke(l)},circfill(e,t,a,l){f(m),m.arc(~~e,~~t,~~a,0,n),A.fill(l)},oval(e,t,a,l,i){f(m),m.ellipse(~~e,~~t,~~a,~~l,0,0,n),A.stroke(i)},ovalfill(e,t,a,l,i){f(m),m.ellipse(~~e,~~t,~~a,~~l,0,0,n),A.fill(i)},line(e,t,a,l,n){f(m);let i=.5*(0!==g&&~~e==~~a),o=.5*(0!==g&&~~t==~~l);m.moveTo(~~e+i,~~t+o),m.lineTo(~~a+i,~~l+o),A.stroke(n)},linewidth(e){m.lineWidth=~~e,g=.5*(0!=~~e%2)},linedash(e,t=0){m.setLineDash(e),m.lineDashOffset=t},text(e,t,a,l=3,n="normal"){m.font=`${n} ${E}px ${k}`,m.fillStyle=P(l),m.fillText(a,~~e,~~t)},textfont(e){k=e},textsize(e){E=e},textalign(e,t){e&&(m.textAlign=e),t&&(m.textBaseline=t)},image(e,t,a){m.drawImage(a,~~e,~~t)},spr(e,t,a,l,n){let i=n.replace(/\s/g,"");for(let n=0;n<a;n++)for(let a=0;a<l;a++){let o=i[l*a+n]||".";"."!==o&&A.rectfill(e+n,t+a,1,1,parseInt(o,16)||0)}},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,o=m;return n.width=e*i,n.height=t*i,(m=n.getContext("2d")).scale(i,i),a(m),m=o,n.transferToImageBitmap()},ctx:e=>(e&&(m=e),m),push(){m.save()},pop(){m.restore()},translate(e,t){m.translate(~~e,~~t)},scale(e,t){m.scale(e,t||e)},rotate(e){m.rotate(e)},alpha(e){m.globalAlpha=A.clamp(e,0,1)},fill(e){m.fillStyle=P(e),m.fill()},stroke(e){m.strokeStyle=P(e),m.stroke()},clip(e){f(m),e(m),m.clip()},sfx:(e,t=0,l=1)=>!!a.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||D,(0!==t||1!==l)&&((e=e.slice())[0]=l*(e[0]||1),e[10]=~~e[10]+t),d.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>p,use(e,t={}){var a=e,l=t;let n=a(A,l);for(let e in n)A.def(e,n[e])},listen:(e,t)=>(S[e=s(e)]=S[e]||new Set,S[e].add(t),()=>S&&S[e].delete(t)),emit(e,t,a,l,n){u&&(N("before:"+(e=s(e)),t,a,l,n),N(e,t,a,l,n),N("after:"+e,t,a,l,n))},pal(t=e){T=t,I=[]},palc(e,t){null==e?I=[]:I[e]=t},def(e,l){A[e]=l,t.global&&(a[e]=l)},timescale(e){v=e},framerate(e){x=1e3/~~e},stat(e){let l={index:e,value:[t,u,x/1e3,h,S,T,D,v,a.zzfxV,z,E,k][e]};return A.emit("stat",l),l.value},quit(){for(let e of(A.pause(),A.emit("quit"),S={},o))e();if(t.global){for(let e in A)delete a[e];delete a.ENGINE}u=!1},pause(){cancelAnimationFrame(b),b=0},resume(){u&&!b&&(y=x,w=Date.now(),b=i(C))},paused:()=>!b};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))A[e]=l[e];function M(){let e=t.loop?t.loop:a;for(let t of"init,update,draw,tap,untap,tapping,tapped,resized".split(","))e[t]&&A.listen(t,e[t]);if(t.autoscale&&r(a,"resize",L),t.tapEvents){let e=e=>[(e.pageX-p.offsetLeft)/h,(e.pageY-p.offsetTop)/h],t=new Map,l=(e,a,l)=>{let n={x:a,y:l,xi:a,yi:l,t:Date.now()};return t.set(e,n),n},n=(e,a,n)=>{let i=t.get(e)||l(e);i.x=a,i.y=n},i=e=>e&&Date.now()-e.t<=300,o=!1;r(p,"mousedown",t=>{if(0===t.button){c(t);let[a,n]=e(t);A.emit("tap",a,n,0),l(0,a,n),o=!0}}),r(p,"mouseup",a=>{if(0===a.button){c(a);let l=t.get(0),[n,r]=e(a);i(l)&&A.emit("tapped",l.xi,l.yi,0),A.emit("untap",n,r,0),t.delete(0),o=!1}}),r(a,"mousemove",t=>{c(t);let[a,l]=e(t);A.def("MX",a),A.def("MY",l),o&&(A.emit("tapping",a,l,0),n(0,a,l))}),r(p,"touchstart",t=>{for(let a of(c(t),t.changedTouches)){let[t,n]=e(a);A.emit("tap",t,n,a.identifier+1),l(a.identifier+1,t,n)}}),r(p,"touchmove",t=>{for(let a of(c(t),t.changedTouches)){let[t,l]=e(a);A.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let s=e=>{c(e);let a=[];if(e.targetTouches.length>0)for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,l]of t)a.includes(e)||(i(l)&&A.emit("tapped",l.xi,l.yi,e),A.emit("untap",l.x,l.y,e),t.delete(e))};r(p,"touchend",s),r(p,"touchcancel",s),r(a,"blur",()=>{for(let[e,a]of(o=!1,t))A.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,l=(e,t="")=>(t=s(t))?e.has("space"===t?" ":t):e.size>0,n="";r(a,"keydown",a=>{let l=s(a.key);e.has(l)||(e.add(l),t.add(l),n=" "===l?"space":l)}),r(a,"keyup",t=>{e.delete(s(t.key))}),r(a,"blur",()=>e.clear()),A.listen("after:update",()=>t.clear()),A.def("iskeydown",t=>l(e,t)),A.def("iskeypressed",e=>l(t,e)),A.def("lastkey",()=>n)}u=!0,A.emit("init",A),A.resume()}function C(){b=i(C);let e=Date.now(),t=0,a=e-w;for(w=e,y+=a<100?a:x;y>=x;){t++,y-=x;let e=x/1e3*v;A.emit("update",e,t),A.def("T",A.T+e)}t&&(A.emit("draw",m),t>1&&(y=0))}function L(){let e=t.width>0?t.width:innerWidth,a=t.width>0?t.height||t.width:innerHeight;if(A.def("W",e),A.def("H",a),p.width=e,p.height=a,t.autoscale){let n=+t.autoscale;p.style.display||(p.style.display="block",p.style.margin="auto"),h=l.min(innerWidth/e,innerHeight/a),h=n>1&&h>n?n:h,p.style.width=e*h+"px",p.style.height=a*h+"px"}m.imageSmoothingEnabled=!1,A.textalign("start","top"),A.emit("resized",h)}function N(e,t,a,l,n){if(S[e])for(let i of S[e])i(t,a,l,n)}function P(e){return T[~~(I[e]??e)%T.length]}if(t.global){if(a.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(a,A),a.ENGINE=A}return m=(p=(p="string"==typeof t.canvas?document.querySelector(t.canvas):t.canvas)||document.createElement("canvas")).getContext("2d"),r(p,"click",()=>focus()),L(),p.parentNode||document.body.appendChild(p),p.style.imageRendering="pixelated",p.oncontextmenu=()=>!1,"loading"===document.readyState?r(a,"DOMContentLoaded",()=>i(M)):i(M),A}})();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.98.0",
4
- "description": "Lightweight HTML5 canvas 2D game engine suitable for small projects and creative coding. Inspired by PICO-8 and P5/Processing.",
3
+ "version": "0.98.2",
4
+ "description": "Lightweight HTML5 canvas 2D game engine suitable for small projects and creative coding. Inspired by PICO-8 and p5.js/Processing.",
5
5
  "license": "MIT",
6
6
  "author": "Luiz Bills <luizbills@pm.me>",
7
7
  "contributors": [],
package/src/index.js CHANGED
@@ -73,9 +73,9 @@ export default function litecanvas(settings = {}) {
73
73
  /** @type {number} */
74
74
  _rngSeed = Date.now(),
75
75
  /** @type {string[]} */
76
- _currentPalette,
77
- /** @type {string[]} */
78
- _colors,
76
+ _colorPalette = defaultPalette,
77
+ /** @type {number[]} */
78
+ _colorPaletteState = [],
79
79
  /** @type {number[]} */
80
80
  _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1],
81
81
  /** @type {string} */
@@ -659,7 +659,7 @@ export default function litecanvas(settings = {}) {
659
659
  )
660
660
 
661
661
  _ctx.font = `${fontStyle} ${_fontSize}px ${_fontFamily}`
662
- _ctx.fillStyle = _colors[~~color % _colors.length]
662
+ _ctx.fillStyle = getColor(color)
663
663
  _ctx.fillText(message, ~~x, ~~y)
664
664
  },
665
665
 
@@ -899,7 +899,7 @@ export default function litecanvas(settings = {}) {
899
899
  '[litecanvas] fill() 1st param must be a positive number or zero'
900
900
  )
901
901
 
902
- _ctx.fillStyle = _colors[~~color % _colors.length]
902
+ _ctx.fillStyle = getColor(color)
903
903
  _ctx.fill()
904
904
  },
905
905
 
@@ -914,7 +914,7 @@ export default function litecanvas(settings = {}) {
914
914
  '[litecanvas] stroke() 1st param must be a positive number or zero'
915
915
  )
916
916
 
917
- _ctx.strokeStyle = _colors[~~color % _colors.length]
917
+ _ctx.strokeStyle = getColor(color)
918
918
  _ctx.stroke()
919
919
  },
920
920
 
@@ -1067,7 +1067,7 @@ export default function litecanvas(settings = {}) {
1067
1067
  },
1068
1068
 
1069
1069
  /**
1070
- * Set or reset the color palette.
1070
+ * Set new palette colors or restore the default palette.
1071
1071
  *
1072
1072
  * @param {string[]} [colors]
1073
1073
  */
@@ -1076,15 +1076,17 @@ export default function litecanvas(settings = {}) {
1076
1076
  Array.isArray(colors) && colors.length > 0,
1077
1077
  '[litecanvas] pal() 1st param must be a array of strings'
1078
1078
  )
1079
- _colors = colors
1080
- _currentPalette = [...colors]
1079
+ _colorPalette = colors
1080
+ _colorPaletteState = []
1081
1081
  },
1082
1082
 
1083
1083
  /**
1084
- * Swap two colors of the current palette.
1084
+ * Replace the color "a" with color "b".
1085
1085
  *
1086
1086
  * If called without arguments, reset the current palette.
1087
1087
  *
1088
+ * Note: `palc()` don't affect drawings made with `image()`.
1089
+ *
1088
1090
  * @param {number?} a
1089
1091
  * @param {number?} b
1090
1092
  */
@@ -1098,9 +1100,9 @@ export default function litecanvas(settings = {}) {
1098
1100
  '[litecanvas] palc() 2nd param must be a positive number'
1099
1101
  )
1100
1102
  if (a == null) {
1101
- _colors = [..._currentPalette]
1103
+ _colorPaletteState = []
1102
1104
  } else {
1103
- ;[_colors[a], _colors[b]] = [_colors[b], _colors[a]]
1105
+ _colorPaletteState[a] = b
1104
1106
  }
1105
1107
  },
1106
1108
 
@@ -1178,7 +1180,7 @@ export default function litecanvas(settings = {}) {
1178
1180
  // 4
1179
1181
  _eventListeners,
1180
1182
  // 5
1181
- _colors,
1183
+ _colorPalette,
1182
1184
  // 6
1183
1185
  _defaultSound,
1184
1186
  // 7
@@ -1687,6 +1689,15 @@ export default function litecanvas(settings = {}) {
1687
1689
  }
1688
1690
  }
1689
1691
 
1692
+ /**
1693
+ * @param {number} index
1694
+ * @returns {string}
1695
+ */
1696
+ function getColor(index) {
1697
+ const i = _colorPaletteState[index] ?? index
1698
+ return _colorPalette[~~i % _colorPalette.length]
1699
+ }
1700
+
1690
1701
  if (settings.global) {
1691
1702
  if (root.ENGINE) {
1692
1703
  throw new Error('only one global litecanvas is allowed')
@@ -1700,9 +1711,6 @@ export default function litecanvas(settings = {}) {
1700
1711
 
1701
1712
  setupCanvas()
1702
1713
 
1703
- // init the color palette
1704
- instance.pal()
1705
-
1706
1714
  if ('loading' === document.readyState) {
1707
1715
  on(root, 'DOMContentLoaded', () => raf(init))
1708
1716
  } else {
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.98.0'
2
+ export const version = '0.98.2'
package/types/global.d.ts CHANGED
@@ -545,13 +545,13 @@ declare global {
545
545
  */
546
546
  function emit(event: string, arg1?: any, arg2?: any, arg3?: any, arg4?: any): void
547
547
  /**
548
- * Set or reset the color palette.
548
+ * Set new palette colors or restore the default palette.
549
549
  *
550
550
  * @param [colors]
551
551
  */
552
552
  function pal(colors?: string[]): void
553
553
  /**
554
- * Swap two colors of the current palette.
554
+ * Replace the color "a" with color "b".
555
555
  *
556
556
  * If called without arguments, reset the current palette.
557
557
  *
package/types/types.d.ts CHANGED
@@ -542,13 +542,13 @@ type LitecanvasInstance = {
542
542
  */
543
543
  def(key: string, value: any): void
544
544
  /**
545
- * Set or reset the color palette.
545
+ * Set new palette colors or restore the default palette.
546
546
  *
547
547
  * @param [colors]
548
548
  */
549
549
  pal(colors?: string[]): void
550
550
  /**
551
- * Swap two colors of the current palette.
551
+ * Replace the color "a" with color "b".
552
552
  *
553
553
  * If called without arguments, reset the current palette.
554
554
  *