litecanvas 0.83.4 → 0.84.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/README.md +8 -7
- package/dist/dist.dev.js +40 -35
- package/dist/dist.js +27 -22
- package/dist/dist.min.js +1 -1
- package/package.json +6 -4
- package/src/index.js +12 -12
- package/src/zzfx.js +11 -3
- package/types/index.d.ts +4 -6
- package/types/types.d.ts +4 -6
package/README.md
CHANGED
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
# Litecanvas
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/litecanvas/)
|
|
5
|
+
[](https://www.npmjs.com/package/litecanvas/)
|
|
6
|
+
[](LICENSE)
|
|
6
7
|
|
|
7
8
|
Litecanvas is a lightweight HTML5 canvas 2D engine suitable for small web games, prototypes, game jams, animations, creative coding, learning game programming and game design, etc.
|
|
8
9
|
|
|
9
|
-
|
|
10
10
|
[](https://bills.itch.io/litecanvas)
|
|
11
11
|
[](https://discord.com/invite/r2c3rGsvH3)
|
|
12
12
|
[](https://litecanvas.js.org/)
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
<!-- prettier-ignore -->
|
|
15
|
+
> [!WARNING]
|
|
15
16
|
> **This project is still in the "alpha" stage. Break changes may occur frequently. All feedback is welcome and appreciated.**
|
|
16
17
|
|
|
17
18
|
### Features
|
|
@@ -60,9 +61,9 @@ litecanvas({
|
|
|
60
61
|
function init() {
|
|
61
62
|
bg = 0 // the color #0 (black)
|
|
62
63
|
color = 3 // the color #3 (white)
|
|
63
|
-
radius = W / 10 // the canvas
|
|
64
|
-
posx =
|
|
65
|
-
posy =
|
|
64
|
+
radius = W / 10 // the canvas Width/10
|
|
65
|
+
posx = W / 2 // center X (or canvas Width/2)
|
|
66
|
+
posy = H / 2 // center Y (or canvas Height/2)
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
// this function detect clicks/touches
|
|
@@ -75,7 +76,7 @@ function tapped(x, y) {
|
|
|
75
76
|
|
|
76
77
|
// put the game logic in this function
|
|
77
78
|
function update(dt) {
|
|
78
|
-
// make the circle falls
|
|
79
|
+
// make the circle falls 200 pixels per second
|
|
79
80
|
posy += 200 * dt
|
|
80
81
|
}
|
|
81
82
|
|
package/dist/dist.dev.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
// src/zzfx.js
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
var setupZzFX = (global) => {
|
|
4
|
+
const zzfxX = new AudioContext();
|
|
5
|
+
global.zzfxV = 1;
|
|
6
|
+
return (i = 1, d = 0.05, z = 220, e = 0, P = 0, S = 0.1, I = 0, c = 1, T = 0, H = 0, V = 0, J = 0, h = 0, j = 0, K = 0, E = 0, r = 0, B = 1, X = 0, L = 0, D = 0) => {
|
|
7
|
+
let n = Math, t = 2 * n.PI, a = 44100, F = T *= 500 * t / a / a, O = z *= (1 - d + 2 * d * n.random(d = [])) * t / a, x = 0, _ = 0, f = 0, g = 1, $ = 0, l = 0, o = 0, s = D < 0 ? -1 : 1, u = t * s * D * 2 / a, G = n.cos(u), C = n.sin, Q = C(u) / 4, M = 1 + Q, m = -2 * G / M, y = (1 - Q) / M, R = (1 + s * G) / 2 / M, A = -(s + G) / M, v = R, U = 0, W = 0, Y = 0, Z = 0;
|
|
8
|
+
for (e = a * e + 9, X *= a, P *= a, S *= a, r *= a, H *= 500 * t / a ** 3, K *= t / a, V *= t / a, J *= a, h = a * h | 0, i *= 0.3 * global.zzfxV, s = e + X + P + S + r | 0; f < s; d[f++] = o * i) ++l % (100 * E | 0) || (o = I ? 1 < I ? 2 < I ? 3 < I ? C(x * x) : n.max(n.min(n.tan(x), 1), -1) : 1 - (2 * x / t % 2 + 2) % 2 : 1 - 4 * n.abs(n.round(x / t) - x / t) : C(x), o = (h ? 1 - L + L * C(t * f / h) : 1) * (o < 0 ? -1 : 1) * n.abs(o) ** c * (f < e ? f / e : f < e + X ? 1 - (f - e) / X * (1 - B) : f < e + X + P ? B : f < s - r ? (s - f - r) / S * B : 0), o = r ? o / 2 + (r > f ? 0 : (f < s - r ? 1 : (s - f) / r) * d[f - r | 0] / 2 / i) : o, D && (o = Z = v * U + A * (U = W) + R * (W = o) - y * Y - m * (Y = Z))), u = (z += T += H) * n.cos(K * _++), x += u + u * j * C(f ** 5), g && ++g > J && (z += V, O += V, g = 0), !h || ++$ % h || (z = O, T = F, g = g || 1);
|
|
9
|
+
i = zzfxX.createBuffer(1, s, a), i.getChannelData(0).set(d), z = zzfxX.createBufferSource(), z.buffer = i, z.connect(zzfxX.destination), z.start();
|
|
10
|
+
};
|
|
8
11
|
};
|
|
9
12
|
|
|
10
13
|
// src/palette.js
|
|
@@ -33,7 +36,7 @@
|
|
|
33
36
|
const root = globalThis, math = Math, TWO_PI = math.PI * 2, raf = requestAnimationFrame, _browserEventListeners = [], on = (elem, evt, callback) => {
|
|
34
37
|
elem.addEventListener(evt, callback, false);
|
|
35
38
|
_browserEventListeners.push(() => elem.removeEventListener(evt, callback, false));
|
|
36
|
-
}, isNumber = Number.isFinite, defaults = {
|
|
39
|
+
}, zzfx = setupZzFX(root), isNumber = Number.isFinite, defaults = {
|
|
37
40
|
width: null,
|
|
38
41
|
height: null,
|
|
39
42
|
autoscale: true,
|
|
@@ -58,8 +61,6 @@
|
|
|
58
61
|
tapped: null
|
|
59
62
|
};
|
|
60
63
|
const instance = {
|
|
61
|
-
/** @type {HTMLCanvasElement} */
|
|
62
|
-
CANVAS: null,
|
|
63
64
|
/** @type {number} */
|
|
64
65
|
W: 0,
|
|
65
66
|
/** @type {number} */
|
|
@@ -67,10 +68,6 @@
|
|
|
67
68
|
/** @type {number} */
|
|
68
69
|
T: 0,
|
|
69
70
|
/** @type {number} */
|
|
70
|
-
CX: 0,
|
|
71
|
-
/** @type {number} */
|
|
72
|
-
CY: 0,
|
|
73
|
-
/** @type {number} */
|
|
74
71
|
MX: -1,
|
|
75
72
|
/** @type {number} */
|
|
76
73
|
MY: -1,
|
|
@@ -156,13 +153,13 @@
|
|
|
156
153
|
* @param {number} max
|
|
157
154
|
* @returns {number}
|
|
158
155
|
*/
|
|
159
|
-
clamp: (value,
|
|
156
|
+
clamp: (value, min, max) => {
|
|
160
157
|
DEV: assert(isNumber(value), "clamp: 1st param must be a number");
|
|
161
|
-
DEV: assert(isNumber(
|
|
162
|
-
DEV: assert(isNumber(
|
|
163
|
-
DEV: assert(
|
|
164
|
-
if (value <
|
|
165
|
-
if (value >
|
|
158
|
+
DEV: assert(isNumber(min), "clamp: 2nd param must be a number");
|
|
159
|
+
DEV: assert(isNumber(max), "clamp: 3rd param must be a number");
|
|
160
|
+
DEV: assert(max > min, "clamp: the 2nd param must be less than the 3rd param");
|
|
161
|
+
if (value < min) return min;
|
|
162
|
+
if (value > max) return max;
|
|
166
163
|
return value;
|
|
167
164
|
},
|
|
168
165
|
/**
|
|
@@ -173,12 +170,12 @@
|
|
|
173
170
|
* @param {number} max
|
|
174
171
|
* @returns {number}
|
|
175
172
|
*/
|
|
176
|
-
wrap: (value,
|
|
173
|
+
wrap: (value, min, max) => {
|
|
177
174
|
DEV: assert(isNumber(value), "wrap: 1st param must be a number");
|
|
178
|
-
DEV: assert(isNumber(
|
|
179
|
-
DEV: assert(isNumber(
|
|
180
|
-
DEV: assert(
|
|
181
|
-
return value - (
|
|
175
|
+
DEV: assert(isNumber(min), "wrap: 2nd param must be a number");
|
|
176
|
+
DEV: assert(isNumber(max), "wrap: 3rd param must be a number");
|
|
177
|
+
DEV: assert(max > min, "wrap: the 2nd param must be less than the 3rd param");
|
|
178
|
+
return value - (max - min) * math.floor((value - min) / (max - min));
|
|
182
179
|
},
|
|
183
180
|
/**
|
|
184
181
|
* Re-maps a number from one range to another.
|
|
@@ -197,7 +194,7 @@
|
|
|
197
194
|
DEV: assert(isNumber(stop1), "map: 3rd param must be a number");
|
|
198
195
|
DEV: assert(isNumber(start2), "map: 4th param must be a number");
|
|
199
196
|
DEV: assert(isNumber(stop2), "map: 5th param must be a number");
|
|
200
|
-
DEV: assert(
|
|
197
|
+
DEV: assert(stop1 !== start1, "map: the 2nd param must be different than the 3rd param");
|
|
201
198
|
const result = (value - start1) / (stop1 - start1) * (stop2 - start2) + start2;
|
|
202
199
|
return withinBounds ? instance.clamp(result, start2, stop2) : result;
|
|
203
200
|
},
|
|
@@ -244,15 +241,15 @@
|
|
|
244
241
|
* @param {number} [max=1.0]
|
|
245
242
|
* @returns {number} the random number
|
|
246
243
|
*/
|
|
247
|
-
rand: (
|
|
248
|
-
DEV: assert(isNumber(
|
|
249
|
-
DEV: assert(isNumber(
|
|
250
|
-
DEV: assert(
|
|
244
|
+
rand: (min = 0, max = 1) => {
|
|
245
|
+
DEV: assert(isNumber(min), "rand: 1st param must be a number");
|
|
246
|
+
DEV: assert(isNumber(max), "rand: 2nd param must be a number");
|
|
247
|
+
DEV: assert(max > min, "rand: the 1st param must be less than the 2nd param");
|
|
251
248
|
const a = 1664525;
|
|
252
249
|
const c = 1013904223;
|
|
253
250
|
const m = 4294967296;
|
|
254
251
|
_rngSeed = (a * _rngSeed + c) % m;
|
|
255
|
-
return _rngSeed / m * (
|
|
252
|
+
return _rngSeed / m * (max - min) + min;
|
|
256
253
|
},
|
|
257
254
|
/**
|
|
258
255
|
* Generates a pseudorandom integer between min (inclusive) and max (inclusive)
|
|
@@ -261,11 +258,11 @@
|
|
|
261
258
|
* @param {number} [max=1]
|
|
262
259
|
* @returns {number} the random number
|
|
263
260
|
*/
|
|
264
|
-
randi: (
|
|
265
|
-
DEV: assert(isNumber(
|
|
266
|
-
DEV: assert(isNumber(
|
|
267
|
-
DEV: assert(
|
|
268
|
-
return math.floor(instance.rand(
|
|
261
|
+
randi: (min = 0, max = 1) => {
|
|
262
|
+
DEV: assert(isNumber(min), "randi: 1st param must be a number");
|
|
263
|
+
DEV: assert(isNumber(max), "randi: 2nd param must be a number");
|
|
264
|
+
DEV: assert(max > min, "randi: the 1st param must be less than the 2nd param");
|
|
265
|
+
return math.floor(instance.rand(min, max + 1));
|
|
269
266
|
},
|
|
270
267
|
/**
|
|
271
268
|
* Initializes the random number generator with an explicit seed value.
|
|
@@ -774,6 +771,14 @@
|
|
|
774
771
|
root.zzfxV = value;
|
|
775
772
|
},
|
|
776
773
|
/** PLUGINS API */
|
|
774
|
+
/**
|
|
775
|
+
* Returns the canvas
|
|
776
|
+
*
|
|
777
|
+
* @returns {HTMLCanvasElement}
|
|
778
|
+
*/
|
|
779
|
+
canvas() {
|
|
780
|
+
return _canvas;
|
|
781
|
+
},
|
|
777
782
|
/**
|
|
778
783
|
* Prepares a plugin to be loaded
|
|
779
784
|
*
|
|
@@ -1165,7 +1170,7 @@
|
|
|
1165
1170
|
function drawFrame(now) {
|
|
1166
1171
|
let updated = 0;
|
|
1167
1172
|
if (settings.animate) {
|
|
1168
|
-
_accumulated += math.min(0.
|
|
1173
|
+
_accumulated += math.min(0.1, (now - _lastFrameTime) / 1e3);
|
|
1169
1174
|
_lastFrameTime = now;
|
|
1170
1175
|
while (_accumulated >= _deltaTime) {
|
|
1171
1176
|
updated++;
|
package/dist/dist.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
// src/zzfx.js
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
var setupZzFX = (global) => {
|
|
4
|
+
const zzfxX = new AudioContext();
|
|
5
|
+
global.zzfxV = 1;
|
|
6
|
+
return (i = 1, d = 0.05, z = 220, e = 0, P = 0, S = 0.1, I = 0, c = 1, T = 0, H = 0, V = 0, J = 0, h = 0, j = 0, K = 0, E = 0, r = 0, B = 1, X = 0, L = 0, D = 0) => {
|
|
7
|
+
let n = Math, t = 2 * n.PI, a = 44100, F = T *= 500 * t / a / a, O = z *= (1 - d + 2 * d * n.random(d = [])) * t / a, x = 0, _ = 0, f = 0, g = 1, $ = 0, l = 0, o = 0, s = D < 0 ? -1 : 1, u = t * s * D * 2 / a, G = n.cos(u), C = n.sin, Q = C(u) / 4, M = 1 + Q, m = -2 * G / M, y = (1 - Q) / M, R = (1 + s * G) / 2 / M, A = -(s + G) / M, v = R, U = 0, W = 0, Y = 0, Z = 0;
|
|
8
|
+
for (e = a * e + 9, X *= a, P *= a, S *= a, r *= a, H *= 500 * t / a ** 3, K *= t / a, V *= t / a, J *= a, h = a * h | 0, i *= 0.3 * global.zzfxV, s = e + X + P + S + r | 0; f < s; d[f++] = o * i) ++l % (100 * E | 0) || (o = I ? 1 < I ? 2 < I ? 3 < I ? C(x * x) : n.max(n.min(n.tan(x), 1), -1) : 1 - (2 * x / t % 2 + 2) % 2 : 1 - 4 * n.abs(n.round(x / t) - x / t) : C(x), o = (h ? 1 - L + L * C(t * f / h) : 1) * (o < 0 ? -1 : 1) * n.abs(o) ** c * (f < e ? f / e : f < e + X ? 1 - (f - e) / X * (1 - B) : f < e + X + P ? B : f < s - r ? (s - f - r) / S * B : 0), o = r ? o / 2 + (r > f ? 0 : (f < s - r ? 1 : (s - f) / r) * d[f - r | 0] / 2 / i) : o, D && (o = Z = v * U + A * (U = W) + R * (W = o) - y * Y - m * (Y = Z))), u = (z += T += H) * n.cos(K * _++), x += u + u * j * C(f ** 5), g && ++g > J && (z += V, O += V, g = 0), !h || ++$ % h || (z = O, T = F, g = g || 1);
|
|
9
|
+
i = zzfxX.createBuffer(1, s, a), i.getChannelData(0).set(d), z = zzfxX.createBufferSource(), z.buffer = i, z.connect(zzfxX.destination), z.start();
|
|
10
|
+
};
|
|
8
11
|
};
|
|
9
12
|
|
|
10
13
|
// src/palette.js
|
|
@@ -28,7 +31,7 @@
|
|
|
28
31
|
const root = globalThis, math = Math, TWO_PI = math.PI * 2, raf = requestAnimationFrame, _browserEventListeners = [], on = (elem, evt, callback) => {
|
|
29
32
|
elem.addEventListener(evt, callback, false);
|
|
30
33
|
_browserEventListeners.push(() => elem.removeEventListener(evt, callback, false));
|
|
31
|
-
}, isNumber = Number.isFinite, defaults = {
|
|
34
|
+
}, zzfx = setupZzFX(root), isNumber = Number.isFinite, defaults = {
|
|
32
35
|
width: null,
|
|
33
36
|
height: null,
|
|
34
37
|
autoscale: true,
|
|
@@ -53,8 +56,6 @@
|
|
|
53
56
|
tapped: null
|
|
54
57
|
};
|
|
55
58
|
const instance = {
|
|
56
|
-
/** @type {HTMLCanvasElement} */
|
|
57
|
-
CANVAS: null,
|
|
58
59
|
/** @type {number} */
|
|
59
60
|
W: 0,
|
|
60
61
|
/** @type {number} */
|
|
@@ -62,10 +63,6 @@
|
|
|
62
63
|
/** @type {number} */
|
|
63
64
|
T: 0,
|
|
64
65
|
/** @type {number} */
|
|
65
|
-
CX: 0,
|
|
66
|
-
/** @type {number} */
|
|
67
|
-
CY: 0,
|
|
68
|
-
/** @type {number} */
|
|
69
66
|
MX: -1,
|
|
70
67
|
/** @type {number} */
|
|
71
68
|
MY: -1,
|
|
@@ -141,9 +138,9 @@
|
|
|
141
138
|
* @param {number} max
|
|
142
139
|
* @returns {number}
|
|
143
140
|
*/
|
|
144
|
-
clamp: (value,
|
|
145
|
-
if (value <
|
|
146
|
-
if (value >
|
|
141
|
+
clamp: (value, min, max) => {
|
|
142
|
+
if (value < min) return min;
|
|
143
|
+
if (value > max) return max;
|
|
147
144
|
return value;
|
|
148
145
|
},
|
|
149
146
|
/**
|
|
@@ -154,8 +151,8 @@
|
|
|
154
151
|
* @param {number} max
|
|
155
152
|
* @returns {number}
|
|
156
153
|
*/
|
|
157
|
-
wrap: (value,
|
|
158
|
-
return value - (
|
|
154
|
+
wrap: (value, min, max) => {
|
|
155
|
+
return value - (max - min) * math.floor((value - min) / (max - min));
|
|
159
156
|
},
|
|
160
157
|
/**
|
|
161
158
|
* Re-maps a number from one range to another.
|
|
@@ -205,12 +202,12 @@
|
|
|
205
202
|
* @param {number} [max=1.0]
|
|
206
203
|
* @returns {number} the random number
|
|
207
204
|
*/
|
|
208
|
-
rand: (
|
|
205
|
+
rand: (min = 0, max = 1) => {
|
|
209
206
|
const a = 1664525;
|
|
210
207
|
const c = 1013904223;
|
|
211
208
|
const m = 4294967296;
|
|
212
209
|
_rngSeed = (a * _rngSeed + c) % m;
|
|
213
|
-
return _rngSeed / m * (
|
|
210
|
+
return _rngSeed / m * (max - min) + min;
|
|
214
211
|
},
|
|
215
212
|
/**
|
|
216
213
|
* Generates a pseudorandom integer between min (inclusive) and max (inclusive)
|
|
@@ -219,8 +216,8 @@
|
|
|
219
216
|
* @param {number} [max=1]
|
|
220
217
|
* @returns {number} the random number
|
|
221
218
|
*/
|
|
222
|
-
randi: (
|
|
223
|
-
return math.floor(instance.rand(
|
|
219
|
+
randi: (min = 0, max = 1) => {
|
|
220
|
+
return math.floor(instance.rand(min, max + 1));
|
|
224
221
|
},
|
|
225
222
|
/**
|
|
226
223
|
* Initializes the random number generator with an explicit seed value.
|
|
@@ -586,6 +583,14 @@
|
|
|
586
583
|
root.zzfxV = value;
|
|
587
584
|
},
|
|
588
585
|
/** PLUGINS API */
|
|
586
|
+
/**
|
|
587
|
+
* Returns the canvas
|
|
588
|
+
*
|
|
589
|
+
* @returns {HTMLCanvasElement}
|
|
590
|
+
*/
|
|
591
|
+
canvas() {
|
|
592
|
+
return _canvas;
|
|
593
|
+
},
|
|
589
594
|
/**
|
|
590
595
|
* Prepares a plugin to be loaded
|
|
591
596
|
*
|
|
@@ -947,7 +952,7 @@
|
|
|
947
952
|
function drawFrame(now) {
|
|
948
953
|
let updated = 0;
|
|
949
954
|
if (settings.animate) {
|
|
950
|
-
_accumulated += math.min(0.
|
|
955
|
+
_accumulated += math.min(0.1, (now - _lastFrameTime) / 1e3);
|
|
951
956
|
_lastFrameTime = now;
|
|
952
957
|
while (_accumulated >= _deltaTime) {
|
|
953
958
|
updated++;
|
package/dist/dist.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{var e=
|
|
1
|
+
(()=>{var e=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(t={}){let a=globalThis,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=>{let t=new AudioContext;return e.zzfxV=1,(a=1,l=.05,n=220,i=0,o=0,r=.1,s=0,f=1,c=0,d=0,p=0,u=0,h=0,g=0,m=0,w=0,v=0,x=1,b=0,y=0,k=0)=>{let E=Math,z=2*E.PI,C=c*=500*z/44100/44100,T=n*=(1-l+2*l*E.random(l=[]))*z/44100,I=0,P=0,S=0,A=1,L=0,X=0,Y=0,M=k<0?-1:1,H=z*M*k*2/44100,N=E.cos(H),W=E.sin,D=W(H)/4,F=1+D,V=-2*N/F,q=(1-D)/F,B=(1+M*N)/2/F,O=-(M+N)/F,R=0,G=0,$=0,j=0;for(i=44100*i+9,b*=44100,o*=44100,r*=44100,v*=44100,d*=500*z/85766121e6,m*=z/44100,p*=z/44100,u*=44100,h=44100*h|0,a*=.3*e.zzfxV,M=i+b+o+r+v|0;S<M;l[S++]=Y*a)++X%(100*w|0)||(Y=s?1<s?2<s?3<s?W(I*I):E.max(E.min(E.tan(I),1),-1):1-(2*I/z%2+2)%2:1-4*E.abs(E.round(I/z)-I/z):W(I),Y=(h?1-y+y*W(z*S/h):1)*(Y<0?-1:1)*E.abs(Y)**f*(S<i?S/i:S<i+b?1-(S-i)/b*(1-x):S<i+b+o?x:S<M-v?(M-S-v)/r*x:0),Y=v?Y/2+(v>S?0:(S<M-v?1:(M-S)/v)*l[S-v|0]/2/a):Y,k&&(Y=j=B*R+O*(R=G)+B*(G=Y)-q*$-V*($=j))),I+=(H=(n+=c+=d)*E.cos(m*P++))+H*g*W(S**5),A&&++A>u&&(n+=p,T+=p,A=0),!h||++L%h||(n=T,c=C,A=A||1);(a=t.createBuffer(1,M,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,pixelart:!1,antialias:!1,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0,animate:!0},t);let f=!1,c=[],d,p=1,u,h=.5,g=1,m,w=1/60,v=0,x,b="sans-serif",y=20,k=Date.now(),E=e,z=[.5,0,1750,,,.3,1,,,,600,.1],C={init:null,update:null,draw:null,resized:null,tap:null,untap:null,tapping:null,tapped:null},T={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?T.clamp(o,l,n):o},norm:(e,t,a)=>T.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)=>(k=(1664525*k+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>l.floor(T.rand(e,t+1)),rseed(e){k=~~e},cls(e){null==e?u.clearRect(0,0,u.canvas.width,u.canvas.height):T.rectfill(0,0,u.canvas.width,u.canvas.height,e)},rect(e,t,a,l,n,i){u.beginPath(),u[i?"roundRect":"rect"](~~e-h,~~t-h,~~a+2*h,~~l+2*h,i),T.stroke(n)},rectfill(e,t,a,l,n,i){u.beginPath(),u[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),T.fill(n)},circ(e,t,a,l){u.beginPath(),u.arc(~~e,~~t,~~a,0,n),T.stroke(l)},circfill(e,t,a,l){u.beginPath(),u.arc(~~e,~~t,~~a,0,n),T.fill(l)},line(e,t,a,l,n){u.beginPath();let i=.5*(0!==h&&~~e==~~a),o=.5*(0!==h&&~~t==~~l);u.moveTo(~~e+i,~~t+o),u.lineTo(~~a+i,~~l+o),T.stroke(n)},linewidth(e){u.lineWidth=~~e,h=.5*(0!=~~e%2)},linedash(e,t=0){u.setLineDash(e),u.lineDashOffset=t},text(e,t,a,l=3,n="normal"){u.font=`${n} ${y}px ${b}`,u.fillStyle=E[~~l%E.length],u.fillText(a,~~e,~~t)},textfont(e){b=e},textsize(e){y=e},textalign(e,t){e&&(u.textAlign=e),t&&(u.textBaseline=t)},image(e,t,a){u.drawImage(a,~~e,~~t)},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,o=u;if(n.width=e*i,n.height=t*i,(u=n.getContext("2d")).scale(i,i),a.push){let e=0,t=0;for(let l of(u.imageSmoothingEnabled=!1,a)){for(let a of l)" "!==a&&"."!==a&&T.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(u);return u=o,n.transferToImageBitmap()},ctx:e=>(e&&(u=e),u),push:()=>u.save(),pop:()=>u.restore(),translate:(e,t)=>u.translate(~~e,~~t),scale:(e,t)=>u.scale(e,t||e),rotate:e=>u.rotate(e),alpha(e){u.globalAlpha=T.clamp(e,0,1)},path:e=>new Path2D(e),fill(e,t){u.fillStyle=E[~~e%E.length],t?u.fill(t):u.fill()},stroke(e,t){u.strokeStyle=E[~~e%E.length],t?u.stroke(t):u.stroke()},clip(e){u.clip(e)},sfx:(e,t=0,l=1)=>!(a.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||z,(0!==t||1!==l)&&((e=e.slice())[0]=l*(e[0]||1),e[10]=~~e[10]+t),s.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>d,use(e,t={}){f?L(e,t):c.push([e,t])},listen:(e,t)=>(C[e=e.toLowerCase()]=C[e]||new Set,C[e].add(t),()=>C[e].delete(t)),emit(e,t,a,l,n){f&&(A("before:"+(e=e.toLowerCase()),t,a,l,n),A(e,t,a,l,n),A("after:"+e,t,a,l,n))},pal(t=e){E=t},def(e,l){T[e]=l,t.global&&(a[e]=l)},timescale(e){g=e},framerate(e){w=1/~~e},stat(e){let l={index:e,value:[t,f,x,p,C,E,z,g,a.zzfxV||1,k,y,b][e]};return T.emit("stat",l),l.value},quit(){for(let e of(cancelAnimationFrame(x),x=0,T.emit("quit"),o))e();if(C={},t.global){for(let e in T)delete a[e];delete a.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))T[e]=l[e];function I(){let e=t.loop?t.loop:a;for(let t in C)e[t]&&T.listen(t,e[t]);for(let[e,t]of c)L(e,t);if(t.autoscale&&r(a,"resize",S),t.tapEvents){let e=(e,t)=>[(e-d.offsetLeft)/p,(t-d.offsetTop)/p],t=new Map,l=(e,a,l)=>{let n={x:a,y:l,startX:a,startY:l,ts:performance.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&&performance.now()-e.ts<=300,o=e=>e.preventDefault(),s=!1;r(d,"mousedown",t=>{if(0===t.button){o(t);let[a,n]=e(t.pageX,t.pageY);T.emit("tap",a,n,0),l(0,a,n),s=!0}}),r(d,"mouseup",a=>{if(0===a.button){o(a);let l=t.get(0),[n,r]=e(a.pageX,a.pageY);i(l)&&T.emit("tapped",l.startX,l.startY,0),T.emit("untap",n,r,0),t.delete(0),s=!1}}),r(d,"mousemove",t=>{o(t);let[a,l]=e(t.pageX,t.pageY);T.def("MX",a),T.def("MY",l),s&&(T.emit("tapping",a,l,0),n(0,a,l))}),r(d,"touchstart",t=>{for(let a of(o(t),t.changedTouches)){let[t,n]=e(a.pageX,a.pageY);T.emit("tap",t,n,a.identifier+1),l(a.identifier+1,t,n)}}),r(d,"touchmove",t=>{for(let a of(o(t),t.changedTouches)){let[t,l]=e(a.pageX,a.pageY);T.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let f=e=>{o(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)&&T.emit("tapped",l.startX,l.startY,e),T.emit("untap",l.x,l.y,e),t.delete(e))};r(d,"touchend",f),r(d,"touchcancel",f),r(a,"blur",()=>{for(let[e,a]of(s=!1,t))T.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,l=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0;r(a,"keydown",a=>{let l=a.key.toLowerCase();e.has(l)||(e.add(l),t.add(l))}),r(a,"keyup",t=>{e.delete(t.key.toLowerCase())}),r(a,"blur",()=>e.clear()),T.listen("after:update",()=>t.clear()),T.def("iskeydown",t=>l(e,t)),T.def("iskeypressed",e=>l(t,e))}f=!0,T.emit("init",T),m=performance.now(),x=i(P)}function P(e){let a=0;if(t.animate){for(v+=l.min(.1,(e-m)/1e3),m=e;v>=w;)a++,T.emit("update",w*g,a),T.def("T",T.T+w*g),v-=w;x&&(x=i(P))}else a=1;a&&(T.textalign("start","top"),T.emit("draw"))}function S(){let e=t.width||a.innerWidth,n=t.height||t.width||a.innerHeight;T.def("W",d.width=e),T.def("H",d.height=n),T.def("CX",T.W/2),T.def("CY",T.H/2),t.autoscale&&(d.style.display||(d.style.display="block",d.style.margin="auto"),p=l.min(a.innerWidth/T.W,a.innerHeight/T.H),p=(t.pixelart?~~p:p)||1,d.style.width=T.W*p+"px",d.style.height=T.H*p+"px"),(!t.antialias||t.pixelart)&&(u.imageSmoothingEnabled=!1,d.style.imageRendering="pixelated"),T.emit("resized",p),T.cls(0),t.animate||i(P)}function A(e,t,a,l,n){if(C[e])for(let i of C[e])i(t,a,l,n)}function L(e,t){let a=e(T,t);for(let e in a)T.def(e,a[e])}if(t.global){if(a.ENGINE)throw Error("two global litecanvas detected");Object.assign(a,T),a.ENGINE=T}return t.canvas&&(d=document.querySelector(t.canvas)),d=d||document.createElement("canvas"),T.def("CANVAS",d),u=d.getContext("2d"),r(d,"click",()=>a.focus()),d.style="",S(),d.parentNode||document.body.appendChild(d),"loading"===document.readyState?r(a,"DOMContentLoaded",()=>i(I)):i(I),T}})();
|
package/package.json
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "litecanvas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.84.0",
|
|
4
4
|
"description": "Lightweight HTML5 canvas 2D game engine suitable for small projects and creative coding. Inspired by PICO-8 and P5/Processing.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Luiz Bills <luizbills@pm.me>",
|
|
7
7
|
"contributors": [],
|
|
8
8
|
"devDependencies": {
|
|
9
|
-
"@swc/core": "^1.12.
|
|
9
|
+
"@swc/core": "^1.12.5",
|
|
10
10
|
"ava": "^6.4.0",
|
|
11
11
|
"esbuild": "^0.25.5",
|
|
12
12
|
"gzip-size": "^7.0.0",
|
|
13
13
|
"prettier": "^3.5.3",
|
|
14
|
-
"tap-min": "^3.0.0"
|
|
14
|
+
"tap-min": "^3.0.0",
|
|
15
|
+
"@types/jsdom": "^21.1.7",
|
|
16
|
+
"jsdom": "^26.1.0"
|
|
15
17
|
},
|
|
16
18
|
"homepage": "https://litecanvas.github.io/about.html",
|
|
17
19
|
"repository": {
|
|
@@ -26,7 +28,7 @@
|
|
|
26
28
|
"types": "types/index.d.ts",
|
|
27
29
|
"scripts": {
|
|
28
30
|
"test": "ava --tap | tap-min",
|
|
29
|
-
"
|
|
31
|
+
"test:watch": "ava --watch",
|
|
30
32
|
"dev": "esbuild src/web.js --bundle --watch --outfile=dist/dist.dev.js --servedir=.",
|
|
31
33
|
"build": "node script/build.js",
|
|
32
34
|
"gzip-size": "gzip -c dist/dist.min.js | wc -c | xargs printf \" Gzip size: %s bytes\n\"",
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
import {
|
|
2
|
+
import { setupZzFX } from './zzfx.js'
|
|
3
3
|
import { defaultPalette } from './palette.js'
|
|
4
4
|
import { assert } from './dev.js'
|
|
5
5
|
|
|
@@ -22,6 +22,7 @@ export default function litecanvas(settings = {}) {
|
|
|
22
22
|
elem.addEventListener(evt, callback, false)
|
|
23
23
|
_browserEventListeners.push(() => elem.removeEventListener(evt, callback, false))
|
|
24
24
|
},
|
|
25
|
+
zzfx = setupZzFX(root),
|
|
25
26
|
isNumber = Number.isFinite,
|
|
26
27
|
/** @type {LitecanvasOptions} */
|
|
27
28
|
defaults = {
|
|
@@ -91,9 +92,6 @@ export default function litecanvas(settings = {}) {
|
|
|
91
92
|
|
|
92
93
|
/** @type {Omit<LitecanvasInstance,'PI'|'sin'|'cos'|'atan2'|'hypot'|'tan'|'abs'|'ceil'|'floor'|'trunc'|'min'|'max'|'pow'|'sqrt'|'sign'|'exp'|'iskeydown'|'iskeypressed'>} */
|
|
93
94
|
const instance = {
|
|
94
|
-
/** @type {HTMLCanvasElement} */
|
|
95
|
-
CANVAS: null,
|
|
96
|
-
|
|
97
95
|
/** @type {number} */
|
|
98
96
|
W: 0,
|
|
99
97
|
|
|
@@ -103,12 +101,6 @@ export default function litecanvas(settings = {}) {
|
|
|
103
101
|
/** @type {number} */
|
|
104
102
|
T: 0,
|
|
105
103
|
|
|
106
|
-
/** @type {number} */
|
|
107
|
-
CX: 0,
|
|
108
|
-
|
|
109
|
-
/** @type {number} */
|
|
110
|
-
CY: 0,
|
|
111
|
-
|
|
112
104
|
/** @type {number} */
|
|
113
105
|
MX: -1,
|
|
114
106
|
|
|
@@ -251,7 +243,7 @@ export default function litecanvas(settings = {}) {
|
|
|
251
243
|
DEV: assert(isNumber(stop1), 'map: 3rd param must be a number')
|
|
252
244
|
DEV: assert(isNumber(start2), 'map: 4th param must be a number')
|
|
253
245
|
DEV: assert(isNumber(stop2), 'map: 5th param must be a number')
|
|
254
|
-
DEV: assert(
|
|
246
|
+
DEV: assert(stop1 !== start1, 'map: the 2nd param must be different than the 3rd param')
|
|
255
247
|
|
|
256
248
|
// prettier-ignore
|
|
257
249
|
const result = ((value - start1) / (stop1 - start1)) * (stop2 - start2) + start2
|
|
@@ -921,6 +913,14 @@ export default function litecanvas(settings = {}) {
|
|
|
921
913
|
},
|
|
922
914
|
|
|
923
915
|
/** PLUGINS API */
|
|
916
|
+
/**
|
|
917
|
+
* Returns the canvas
|
|
918
|
+
*
|
|
919
|
+
* @returns {HTMLCanvasElement}
|
|
920
|
+
*/
|
|
921
|
+
canvas() {
|
|
922
|
+
return _canvas
|
|
923
|
+
},
|
|
924
924
|
/**
|
|
925
925
|
* Prepares a plugin to be loaded
|
|
926
926
|
*
|
|
@@ -1405,7 +1405,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1405
1405
|
|
|
1406
1406
|
if (settings.animate) {
|
|
1407
1407
|
// prevents too long frames
|
|
1408
|
-
_accumulated += math.min(0.
|
|
1408
|
+
_accumulated += math.min(0.1, (now - _lastFrameTime) / 1000)
|
|
1409
1409
|
_lastFrameTime = now
|
|
1410
1410
|
|
|
1411
1411
|
while (_accumulated >= _deltaTime) {
|
package/src/zzfx.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
// ZzFXMicro - Zuper Zmall Zound Zynth - v1.3.0 by Frank Force | https://github.com/KilledByAPixel/ZzFX
|
|
2
|
-
const zzfxX = /** @__PURE__ */ new AudioContext()
|
|
3
|
-
|
|
4
2
|
// prettier-ignore
|
|
5
|
-
|
|
3
|
+
/**
|
|
4
|
+
* @param {typeof globalThis} global
|
|
5
|
+
* @returns {Function} the `zzfx()` function
|
|
6
|
+
*/
|
|
7
|
+
export const setupZzFX = (global) => {
|
|
8
|
+
const zzfxX = new AudioContext()
|
|
9
|
+
|
|
10
|
+
global.zzfxV = 1
|
|
11
|
+
|
|
12
|
+
return (i=1,d=.05,z=220,e=0,P=0,S=.1,I=0,c=1,T=0,H=0,V=0,J=0,h=0,j=0,K=0,E=0,r=0,B=1,X=0,L=0,D=0)=>{let n=Math,t=2*n.PI,a=44100,F=T*=500*t/a/a,O=z*=(1-d+2*d*n.random(d=[]))*t/a,x=0,_=0,f=0,g=1,$=0,l=0,o=0,s=D<0?-1:1,u=t*s*D*2/a,G=n.cos(u),C=n.sin,Q=C(u)/4,M=1+Q,m=-2*G/M,y=(1-Q)/M,R=(1+s*G)/2/M,A=-(s+G)/M,v=R,U=0,W=0,Y=0,Z=0;for(e=a*e+9,X*=a,P*=a,S*=a,r*=a,H*=500*t/a**3,K*=t/a,V*=t/a,J*=a,h=a*h|0,i*=.3*global.zzfxV,s=e+X+P+S+r|0;f<s;d[f++]=o*i)++l%(100*E|0)||(o=I?1<I?2<I?3<I?C(x*x):n.max(n.min(n.tan(x),1),-1):1-(2*x/t%2+2)%2:1-4*n.abs(n.round(x/t)-x/t):C(x),o=(h?1-L+L*C(t*f/h):1)*(o<0?-1:1)*n.abs(o)**c*(f<e?f/e:f<e+X?1-(f-e)/X*(1-B):f<e+X+P?B:f<s-r?(s-f-r)/S*B:0),o=r?o/2+(r>f?0:(f<s-r?1:(s-f)/r)*d[f-r|0]/2/i):o,D&&(o=Z=v*U+A*(U=W)+R*(W=o)-y*Y-m*(Y=Z))),u=(z+=T+=H)*n.cos(K*_++),x+=u+u*j*C(f**5),g&&++g>J&&(z+=V,O+=V,g=0),!h||++$%h||(z=O,T=F,g=g||1);i=zzfxX.createBuffer(1,s,a),i.getChannelData(0).set(d),z=zzfxX.createBufferSource(),z.buffer=i,z.connect(zzfxX.destination),z.start()};
|
|
13
|
+
}
|
package/types/index.d.ts
CHANGED
|
@@ -8,18 +8,12 @@ export default function litecanvas(settings?: LitecanvasOptions): LitecanvasInst
|
|
|
8
8
|
declare global {
|
|
9
9
|
function litecanvas(settings?: LitecanvasOptions): LitecanvasInstance
|
|
10
10
|
|
|
11
|
-
/** The game canvas HTML element */
|
|
12
|
-
var CANVAS: HTMLCanvasElement
|
|
13
11
|
/** The game screen width */
|
|
14
12
|
var W: number
|
|
15
13
|
/** The game screen height */
|
|
16
14
|
var H: number
|
|
17
15
|
/** the amount of time (in seconds) since the game started */
|
|
18
16
|
var T: number
|
|
19
|
-
/** the center X of the game screen */
|
|
20
|
-
var CX: number
|
|
21
|
-
/** the center Y of the game screen */
|
|
22
|
-
var CY: number
|
|
23
17
|
/** The current mouse's horizontal (X) position or -1 (if the mouse was not used or detected) */
|
|
24
18
|
var MX: number
|
|
25
19
|
/** The current mouse's vertical (Y) position or -1 (if the mouse was not used or detected) */
|
|
@@ -504,6 +498,10 @@ declare global {
|
|
|
504
498
|
function iskeypressed(key: string): boolean
|
|
505
499
|
|
|
506
500
|
/** PLUGINS API */
|
|
501
|
+
/**
|
|
502
|
+
* Returns the canvas
|
|
503
|
+
*/
|
|
504
|
+
function canvas(): HTMLCanvasElement
|
|
507
505
|
/**
|
|
508
506
|
* Prepares a plugin to be loaded
|
|
509
507
|
*
|
package/types/types.d.ts
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
type LitecanvasInstance = {
|
|
2
|
-
/** The game canvas HTML element */
|
|
3
|
-
CANVAS: HTMLCanvasElement
|
|
4
2
|
/** The game screen width */
|
|
5
3
|
W: number
|
|
6
4
|
/** The game screen height */
|
|
7
5
|
H: number
|
|
8
6
|
/** the amount of time (in seconds) since the game started */
|
|
9
7
|
T: number
|
|
10
|
-
/** the center X of the game screen */
|
|
11
|
-
CX: number
|
|
12
|
-
/** the center Y of the game screen */
|
|
13
|
-
CY: number
|
|
14
8
|
/** The current mouse's horizontal (X) position or -1 (if the mouse was not used or detected) */
|
|
15
9
|
MX: number
|
|
16
10
|
/** The current mouse's vertical (Y) position or -1 (if the mouse was not used or detected) */
|
|
@@ -491,6 +485,10 @@ type LitecanvasInstance = {
|
|
|
491
485
|
iskeypressed(key: string): boolean
|
|
492
486
|
|
|
493
487
|
/** PLUGINS API */
|
|
488
|
+
/**
|
|
489
|
+
* Returns the canvas
|
|
490
|
+
*/
|
|
491
|
+
canvas(): HTMLCanvasElement
|
|
494
492
|
/**
|
|
495
493
|
* Prepares a plugin to be loaded
|
|
496
494
|
*
|