litecanvas 0.74.2 → 0.75.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dist.dev.js CHANGED
@@ -1,27 +1,10 @@
1
1
  (() => {
2
2
  // src/zzfx.js
3
3
  var zzfxX = /* @__PURE__ */ new AudioContext();
4
- var zzfx = (p = 1, k = 0.05, b = 220, e = 0, r = 0, t = 0.1, q = 0, D = 1, u = 0, y = 0, v = 0, z = 0, l = 0, E = 0, A = 0, F = 0, c = 0, w = 1, m = 0, B = 0, N = 0) => {
5
- let M = Math, d = 2 * M.PI, R = 44100, G = u *= 500 * d / R / R, C = b *= (1 - k + 2 * k * M.random(k = [])) * d / R, g = 0, H = 0, a = 0, n = 1, I = 0, J = 0, f = 0, h = N < 0 ? -1 : 1, x = d * h * N * 2 / R, L = M.cos(x), Z = M.sin, K = Z(x) / 4, O = 1 + K, X = -2 * L / O, Y = (1 - K) / O, P = (1 + h * L) / 2 / O, Q = -(h + L) / O, S = P, T = 0, U = 0, V = 0, W = 0;
6
- e = R * e + 9;
7
- m *= R;
8
- r *= R;
9
- t *= R;
10
- c *= R;
11
- y *= 500 * d / R ** 3;
12
- A *= d / R;
13
- v *= d / R;
14
- z *= R;
15
- l = R * l | 0;
16
- p *= 0.3 * (globalThis.zzfxV || 1);
17
- for (h = e + m + r + t + c | 0; a < h; k[a++] = f * p)
18
- ++J % (100 * F | 0) || (f = q ? 1 < q ? 2 < q ? 3 < q ? Z(g * g) : M.max(M.min(M.tan(g), 1), -1) : 1 - (2 * g / d % 2 + 2) % 2 : 1 - 4 * M.abs(M.round(g / d) - g / d) : Z(g), f = (l ? 1 - B + B * Z(d * a / l) : 1) * (f < 0 ? -1 : 1) * M.abs(f) ** D * (a < e ? a / e : a < e + m ? 1 - (a - e) / m * (1 - w) : a < e + m + r ? w : a < h - c ? (h - a - c) / t * w : 0), f = c ? f / 2 + (c > a ? 0 : (a < h - c ? 1 : (h - a) / c) * k[a - c | 0] / 2 / p) : f, N ? f = W = S * T + Q * (T = U) + P * (U = f) - Y * V - X * (V = W) : 0), x = (b += u += y) * M.cos(A * H++), g += x + x * E * Z(a ** 5), n && ++n > z && (b += v, C += v, n = 0), !l || ++I % l || (b = C, u = G, n = n || 1);
19
- p = zzfxX.createBuffer(1, h, R);
20
- p.getChannelData(0).set(k);
21
- b = zzfxX.createBufferSource();
22
- b.buffer = p;
23
- b.connect(zzfxX.destination);
24
- b.start();
4
+ var zzfx = (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) => {
5
+ 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;
6
+ 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 * (globalThis.zzfxV || 1), 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);
7
+ i = zzfxX.createBuffer(1, s, a), i.getChannelData(0).set(d), z = zzfxX.createBufferSource(), z.buffer = i, z.connect(zzfxX.destination), z.start();
25
8
  };
26
9
 
27
10
  // src/palette.js
@@ -101,13 +84,6 @@
101
84
  /** @type {number[]} */
102
85
  DEFAULT_SFX: [0.5, , 1675, , 0.06, 0.2, 1, 1.8, , , 637, 0.06],
103
86
  /** MATH API */
104
- /**
105
- * The value of the mathematical constant PI (π).
106
- * Approximately 3.14159
107
- *
108
- * @type {number}
109
- */
110
- PI,
111
87
  /**
112
88
  * Twice the value of the mathematical constant PI (π).
113
89
  * Approximately 6.28318
@@ -124,7 +100,7 @@
124
100
  *
125
101
  * @type {number}
126
102
  */
127
- HALF_PI: PI * 0.5,
103
+ HALF_PI: PI / 2,
128
104
  /**
129
105
  * Calculates a linear (interpolation) value over t%.
130
106
  *
@@ -140,7 +116,7 @@
140
116
  assert(isFinite(end), "lerp: 2nd param must be a number");
141
117
  assert(isFinite(t), "lerp: 3rd param must be a number");
142
118
  }
143
- return start + t * (end - start);
119
+ return t * (end - start) + start;
144
120
  },
145
121
  /**
146
122
  * Convert degrees to radians
@@ -1096,24 +1072,7 @@
1096
1072
  }
1097
1073
  }
1098
1074
  };
1099
- for (const k of [
1100
- "sin",
1101
- "cos",
1102
- "atan2",
1103
- "hypot",
1104
- "tan",
1105
- "abs",
1106
- "ceil",
1107
- "round",
1108
- "floor",
1109
- "trunc",
1110
- "min",
1111
- "max",
1112
- "pow",
1113
- "sqrt",
1114
- "sign",
1115
- "exp"
1116
- ]) {
1075
+ for (const k of "PI,sin,cos,atan2,hypot,tan,abs,ceil,round,floor,trunc,min,max,pow,sqrt,sign,exp".split(",")) {
1117
1076
  instance[k] = Math[k];
1118
1077
  }
1119
1078
  function init() {
@@ -1148,17 +1107,32 @@
1148
1107
  const tap = _taps.get(id) || _registerTap(id);
1149
1108
  tap.x = x;
1150
1109
  tap.y = y;
1151
- }, _checkTapped = (tap) => tap && performance.now() - tap.ts <= 200;
1110
+ }, _checkTapped = (tap) => tap && performance.now() - tap.ts <= 200, preventDefault = (ev) => ev.preventDefault();
1152
1111
  let _pressingMouse = false;
1153
1112
  on(_canvas, "mousedown", (ev) => {
1154
- ev.preventDefault();
1155
- const [x, y] = _getXY(ev.pageX, ev.pageY);
1156
- instance.emit("tap", x, y, 0);
1157
- _registerTap(0, x, y);
1158
- _pressingMouse = true;
1113
+ if (ev.button === 0) {
1114
+ preventDefault(ev);
1115
+ const [x, y] = _getXY(ev.pageX, ev.pageY);
1116
+ instance.emit("tap", x, y, 0);
1117
+ _registerTap(0, x, y);
1118
+ _pressingMouse = true;
1119
+ }
1120
+ });
1121
+ on(_canvas, "mouseup", (ev) => {
1122
+ if (ev.button === 0) {
1123
+ preventDefault(ev);
1124
+ const tap = _taps.get(0);
1125
+ const [x, y] = _getXY(ev.pageX, ev.pageY);
1126
+ if (_checkTapped(tap)) {
1127
+ instance.emit("tapped", tap.startX, tap.startY, 0);
1128
+ }
1129
+ instance.emit("untap", x, y, 0);
1130
+ _taps.delete(0);
1131
+ _pressingMouse = false;
1132
+ }
1159
1133
  });
1160
1134
  on(_canvas, "mousemove", (ev) => {
1161
- ev.preventDefault();
1135
+ preventDefault(ev);
1162
1136
  const [x, y] = _getXY(ev.pageX, ev.pageY);
1163
1137
  instance.setvar("MOUSEX", x);
1164
1138
  instance.setvar("MOUSEY", y);
@@ -1166,19 +1140,8 @@
1166
1140
  instance.emit("tapping", x, y, 0);
1167
1141
  _updateTap(0, x, y);
1168
1142
  });
1169
- on(_canvas, "mouseup", (ev) => {
1170
- ev.preventDefault();
1171
- const tap = _taps.get(0);
1172
- const [x, y] = _getXY(ev.pageX, ev.pageY);
1173
- if (_checkTapped(tap)) {
1174
- instance.emit("tapped", tap.startX, tap.startY, 0);
1175
- }
1176
- instance.emit("untap", x, y, 0);
1177
- _taps.delete(0);
1178
- _pressingMouse = false;
1179
- });
1180
1143
  on(_canvas, "touchstart", (ev) => {
1181
- ev.preventDefault();
1144
+ preventDefault(ev);
1182
1145
  const touches = ev.changedTouches;
1183
1146
  for (const touch of touches) {
1184
1147
  const [x, y] = _getXY(touch.pageX, touch.pageY);
@@ -1187,7 +1150,7 @@
1187
1150
  }
1188
1151
  });
1189
1152
  on(_canvas, "touchmove", (ev) => {
1190
- ev.preventDefault();
1153
+ preventDefault(ev);
1191
1154
  const touches = ev.changedTouches;
1192
1155
  for (const touch of touches) {
1193
1156
  const [x, y] = _getXY(touch.pageX, touch.pageY);
@@ -1196,7 +1159,7 @@
1196
1159
  }
1197
1160
  });
1198
1161
  const _touchEndHandler = (ev) => {
1199
- ev.preventDefault();
1162
+ preventDefault(ev);
1200
1163
  const existing = [];
1201
1164
  if (ev.targetTouches.length > 0) {
1202
1165
  for (const touch of ev.targetTouches) {
@@ -1248,7 +1211,6 @@
1248
1211
  });
1249
1212
  on(root, "focus", () => {
1250
1213
  if (!_rafid) {
1251
- _lastFrameTime = performance.now();
1252
1214
  _rafid = raf(drawFrame);
1253
1215
  }
1254
1216
  });
@@ -1263,8 +1225,10 @@
1263
1225
  _rafid = raf(drawFrame);
1264
1226
  }
1265
1227
  let updated = 0, frameTime = (now - _lastFrameTime) / 1e3;
1266
- _accumulated += frameTime;
1267
1228
  _lastFrameTime = now;
1229
+ if (frameTime > _deltaTime * 30)
1230
+ return void 0;
1231
+ _accumulated += frameTime;
1268
1232
  if (!_animated) {
1269
1233
  _accumulated = _deltaTime;
1270
1234
  }
@@ -1283,6 +1247,20 @@
1283
1247
  }
1284
1248
  function setupCanvas() {
1285
1249
  _canvas = "string" === typeof _canvas ? document.querySelector(_canvas) : _canvas;
1250
+ if (true) {
1251
+ assert(
1252
+ _canvas && _canvas.tagName === "CANVAS",
1253
+ "Invalid canvas element"
1254
+ );
1255
+ assert(
1256
+ null === instance.WIDTH || instance.WIDTH > 0,
1257
+ `Litecanvas' "width" option should be null or a positive number`
1258
+ );
1259
+ assert(
1260
+ null === instance.HEIGHT || instance.HEIGHT > 0,
1261
+ `Litecanvas' "width" option should be null or a positive number`
1262
+ );
1263
+ }
1286
1264
  instance.setvar("CANVAS", _canvas);
1287
1265
  _ctx = _canvas.getContext("2d");
1288
1266
  on(_canvas, "click", () => root.focus());
@@ -1316,7 +1294,7 @@
1316
1294
  instance.setvar("CENTERY", instance.HEIGHT / 2);
1317
1295
  if (!settings.antialias || settings.pixelart) {
1318
1296
  _ctx.imageSmoothingEnabled = false;
1319
- _canvas.style.imageRendering = "pixelated";
1297
+ styles.imageRendering = "pixelated";
1320
1298
  }
1321
1299
  instance.emit("resized", _scale);
1322
1300
  if (!_animated) {
@@ -1331,10 +1309,14 @@
1331
1309
  }
1332
1310
  function loadPlugin(callback, config) {
1333
1311
  const pluginData = callback(instance, _helpers, config);
1334
- if ("object" === typeof pluginData) {
1335
- for (const key of Object.keys(pluginData)) {
1336
- instance.setvar(key, pluginData[key]);
1337
- }
1312
+ if (true) {
1313
+ assert(
1314
+ null == pluginData || "object" === typeof pluginData,
1315
+ "Litecanvas plugins should return an object or nothing"
1316
+ );
1317
+ }
1318
+ for (const key in pluginData) {
1319
+ instance.setvar(key, pluginData[key]);
1338
1320
  }
1339
1321
  }
1340
1322
  if (_global) {
package/dist/dist.js CHANGED
@@ -1,27 +1,10 @@
1
1
  (() => {
2
2
  // src/zzfx.js
3
3
  var zzfxX = /* @__PURE__ */ new AudioContext();
4
- var zzfx = (p = 1, k = 0.05, b = 220, e = 0, r = 0, t = 0.1, q = 0, D = 1, u = 0, y = 0, v = 0, z = 0, l = 0, E = 0, A = 0, F = 0, c = 0, w = 1, m = 0, B = 0, N = 0) => {
5
- let M = Math, d = 2 * M.PI, R = 44100, G = u *= 500 * d / R / R, C = b *= (1 - k + 2 * k * M.random(k = [])) * d / R, g = 0, H = 0, a = 0, n = 1, I = 0, J = 0, f = 0, h = N < 0 ? -1 : 1, x = d * h * N * 2 / R, L = M.cos(x), Z = M.sin, K = Z(x) / 4, O = 1 + K, X = -2 * L / O, Y = (1 - K) / O, P = (1 + h * L) / 2 / O, Q = -(h + L) / O, S = P, T = 0, U = 0, V = 0, W = 0;
6
- e = R * e + 9;
7
- m *= R;
8
- r *= R;
9
- t *= R;
10
- c *= R;
11
- y *= 500 * d / R ** 3;
12
- A *= d / R;
13
- v *= d / R;
14
- z *= R;
15
- l = R * l | 0;
16
- p *= 0.3 * (globalThis.zzfxV || 1);
17
- for (h = e + m + r + t + c | 0; a < h; k[a++] = f * p)
18
- ++J % (100 * F | 0) || (f = q ? 1 < q ? 2 < q ? 3 < q ? Z(g * g) : M.max(M.min(M.tan(g), 1), -1) : 1 - (2 * g / d % 2 + 2) % 2 : 1 - 4 * M.abs(M.round(g / d) - g / d) : Z(g), f = (l ? 1 - B + B * Z(d * a / l) : 1) * (f < 0 ? -1 : 1) * M.abs(f) ** D * (a < e ? a / e : a < e + m ? 1 - (a - e) / m * (1 - w) : a < e + m + r ? w : a < h - c ? (h - a - c) / t * w : 0), f = c ? f / 2 + (c > a ? 0 : (a < h - c ? 1 : (h - a) / c) * k[a - c | 0] / 2 / p) : f, N ? f = W = S * T + Q * (T = U) + P * (U = f) - Y * V - X * (V = W) : 0), x = (b += u += y) * M.cos(A * H++), g += x + x * E * Z(a ** 5), n && ++n > z && (b += v, C += v, n = 0), !l || ++I % l || (b = C, u = G, n = n || 1);
19
- p = zzfxX.createBuffer(1, h, R);
20
- p.getChannelData(0).set(k);
21
- b = zzfxX.createBufferSource();
22
- b.buffer = p;
23
- b.connect(zzfxX.destination);
24
- b.start();
4
+ var zzfx = (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) => {
5
+ 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;
6
+ 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 * (globalThis.zzfxV || 1), 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);
7
+ i = zzfxX.createBuffer(1, s, a), i.getChannelData(0).set(d), z = zzfxX.createBufferSource(), z.buffer = i, z.connect(zzfxX.destination), z.start();
25
8
  };
26
9
 
27
10
  // src/palette.js
@@ -96,13 +79,6 @@
96
79
  /** @type {number[]} */
97
80
  DEFAULT_SFX: [0.5, , 1675, , 0.06, 0.2, 1, 1.8, , , 637, 0.06],
98
81
  /** MATH API */
99
- /**
100
- * The value of the mathematical constant PI (π).
101
- * Approximately 3.14159
102
- *
103
- * @type {number}
104
- */
105
- PI,
106
82
  /**
107
83
  * Twice the value of the mathematical constant PI (π).
108
84
  * Approximately 6.28318
@@ -119,7 +95,7 @@
119
95
  *
120
96
  * @type {number}
121
97
  */
122
- HALF_PI: PI * 0.5,
98
+ HALF_PI: PI / 2,
123
99
  /**
124
100
  * Calculates a linear (interpolation) value over t%.
125
101
  *
@@ -135,7 +111,7 @@
135
111
  assert(isFinite(end), "lerp: 2nd param must be a number");
136
112
  assert(isFinite(t), "lerp: 3rd param must be a number");
137
113
  }
138
- return start + t * (end - start);
114
+ return t * (end - start) + start;
139
115
  },
140
116
  /**
141
117
  * Convert degrees to radians
@@ -1091,24 +1067,7 @@
1091
1067
  }
1092
1068
  }
1093
1069
  };
1094
- for (const k of [
1095
- "sin",
1096
- "cos",
1097
- "atan2",
1098
- "hypot",
1099
- "tan",
1100
- "abs",
1101
- "ceil",
1102
- "round",
1103
- "floor",
1104
- "trunc",
1105
- "min",
1106
- "max",
1107
- "pow",
1108
- "sqrt",
1109
- "sign",
1110
- "exp"
1111
- ]) {
1070
+ for (const k of "PI,sin,cos,atan2,hypot,tan,abs,ceil,round,floor,trunc,min,max,pow,sqrt,sign,exp".split(",")) {
1112
1071
  instance[k] = Math[k];
1113
1072
  }
1114
1073
  function init() {
@@ -1143,17 +1102,32 @@
1143
1102
  const tap = _taps.get(id) || _registerTap(id);
1144
1103
  tap.x = x;
1145
1104
  tap.y = y;
1146
- }, _checkTapped = (tap) => tap && performance.now() - tap.ts <= 200;
1105
+ }, _checkTapped = (tap) => tap && performance.now() - tap.ts <= 200, preventDefault = (ev) => ev.preventDefault();
1147
1106
  let _pressingMouse = false;
1148
1107
  on(_canvas, "mousedown", (ev) => {
1149
- ev.preventDefault();
1150
- const [x, y] = _getXY(ev.pageX, ev.pageY);
1151
- instance.emit("tap", x, y, 0);
1152
- _registerTap(0, x, y);
1153
- _pressingMouse = true;
1108
+ if (ev.button === 0) {
1109
+ preventDefault(ev);
1110
+ const [x, y] = _getXY(ev.pageX, ev.pageY);
1111
+ instance.emit("tap", x, y, 0);
1112
+ _registerTap(0, x, y);
1113
+ _pressingMouse = true;
1114
+ }
1115
+ });
1116
+ on(_canvas, "mouseup", (ev) => {
1117
+ if (ev.button === 0) {
1118
+ preventDefault(ev);
1119
+ const tap = _taps.get(0);
1120
+ const [x, y] = _getXY(ev.pageX, ev.pageY);
1121
+ if (_checkTapped(tap)) {
1122
+ instance.emit("tapped", tap.startX, tap.startY, 0);
1123
+ }
1124
+ instance.emit("untap", x, y, 0);
1125
+ _taps.delete(0);
1126
+ _pressingMouse = false;
1127
+ }
1154
1128
  });
1155
1129
  on(_canvas, "mousemove", (ev) => {
1156
- ev.preventDefault();
1130
+ preventDefault(ev);
1157
1131
  const [x, y] = _getXY(ev.pageX, ev.pageY);
1158
1132
  instance.setvar("MOUSEX", x);
1159
1133
  instance.setvar("MOUSEY", y);
@@ -1161,19 +1135,8 @@
1161
1135
  instance.emit("tapping", x, y, 0);
1162
1136
  _updateTap(0, x, y);
1163
1137
  });
1164
- on(_canvas, "mouseup", (ev) => {
1165
- ev.preventDefault();
1166
- const tap = _taps.get(0);
1167
- const [x, y] = _getXY(ev.pageX, ev.pageY);
1168
- if (_checkTapped(tap)) {
1169
- instance.emit("tapped", tap.startX, tap.startY, 0);
1170
- }
1171
- instance.emit("untap", x, y, 0);
1172
- _taps.delete(0);
1173
- _pressingMouse = false;
1174
- });
1175
1138
  on(_canvas, "touchstart", (ev) => {
1176
- ev.preventDefault();
1139
+ preventDefault(ev);
1177
1140
  const touches = ev.changedTouches;
1178
1141
  for (const touch of touches) {
1179
1142
  const [x, y] = _getXY(touch.pageX, touch.pageY);
@@ -1182,7 +1145,7 @@
1182
1145
  }
1183
1146
  });
1184
1147
  on(_canvas, "touchmove", (ev) => {
1185
- ev.preventDefault();
1148
+ preventDefault(ev);
1186
1149
  const touches = ev.changedTouches;
1187
1150
  for (const touch of touches) {
1188
1151
  const [x, y] = _getXY(touch.pageX, touch.pageY);
@@ -1191,7 +1154,7 @@
1191
1154
  }
1192
1155
  });
1193
1156
  const _touchEndHandler = (ev) => {
1194
- ev.preventDefault();
1157
+ preventDefault(ev);
1195
1158
  const existing = [];
1196
1159
  if (ev.targetTouches.length > 0) {
1197
1160
  for (const touch of ev.targetTouches) {
@@ -1243,7 +1206,6 @@
1243
1206
  });
1244
1207
  on(root, "focus", () => {
1245
1208
  if (!_rafid) {
1246
- _lastFrameTime = performance.now();
1247
1209
  _rafid = raf(drawFrame);
1248
1210
  }
1249
1211
  });
@@ -1258,8 +1220,10 @@
1258
1220
  _rafid = raf(drawFrame);
1259
1221
  }
1260
1222
  let updated = 0, frameTime = (now - _lastFrameTime) / 1e3;
1261
- _accumulated += frameTime;
1262
1223
  _lastFrameTime = now;
1224
+ if (frameTime > _deltaTime * 30)
1225
+ return void 0;
1226
+ _accumulated += frameTime;
1263
1227
  if (!_animated) {
1264
1228
  _accumulated = _deltaTime;
1265
1229
  }
@@ -1278,6 +1242,20 @@
1278
1242
  }
1279
1243
  function setupCanvas() {
1280
1244
  _canvas = "string" === typeof _canvas ? document.querySelector(_canvas) : _canvas;
1245
+ if (false) {
1246
+ assert(
1247
+ _canvas && _canvas.tagName === "CANVAS",
1248
+ "Invalid canvas element"
1249
+ );
1250
+ assert(
1251
+ null === instance.WIDTH || instance.WIDTH > 0,
1252
+ `Litecanvas' "width" option should be null or a positive number`
1253
+ );
1254
+ assert(
1255
+ null === instance.HEIGHT || instance.HEIGHT > 0,
1256
+ `Litecanvas' "width" option should be null or a positive number`
1257
+ );
1258
+ }
1281
1259
  instance.setvar("CANVAS", _canvas);
1282
1260
  _ctx = _canvas.getContext("2d");
1283
1261
  on(_canvas, "click", () => root.focus());
@@ -1311,7 +1289,7 @@
1311
1289
  instance.setvar("CENTERY", instance.HEIGHT / 2);
1312
1290
  if (!settings.antialias || settings.pixelart) {
1313
1291
  _ctx.imageSmoothingEnabled = false;
1314
- _canvas.style.imageRendering = "pixelated";
1292
+ styles.imageRendering = "pixelated";
1315
1293
  }
1316
1294
  instance.emit("resized", _scale);
1317
1295
  if (!_animated) {
@@ -1326,10 +1304,14 @@
1326
1304
  }
1327
1305
  function loadPlugin(callback, config) {
1328
1306
  const pluginData = callback(instance, _helpers, config);
1329
- if ("object" === typeof pluginData) {
1330
- for (const key of Object.keys(pluginData)) {
1331
- instance.setvar(key, pluginData[key]);
1332
- }
1307
+ if (false) {
1308
+ assert(
1309
+ null == pluginData || "object" === typeof pluginData,
1310
+ "Litecanvas plugins should return an object or nothing"
1311
+ );
1312
+ }
1313
+ for (const key in pluginData) {
1314
+ instance.setvar(key, pluginData[key]);
1333
1315
  }
1334
1316
  }
1335
1317
  if (_global) {
package/dist/dist.min.js CHANGED
@@ -1 +1 @@
1
- (()=>{var e=new AudioContext,t=(t=1,a=.05,l=220,n=0,i=0,r=.1,o=0,s=1,c=0,f=0,p=0,u=0,d=0,h=0,g=0,m=0,v=0,E=1,b=0,w=0,T=0)=>{let x=Math,y=2*x.PI,D=c*=500*y/44100/44100,H=l*=(1-a+2*a*x.random(a=[]))*y/44100,I=0,S=0,A=0,k=1,C=0,L=0,X=0,O=T<0?-1:1,z=y*O*T*2/44100,M=x.cos(z),P=x.sin,Y=P(z)/4,F=1+Y,W=-2*M/F,_=(1-Y)/F,R=(1+O*M)/2/F,G=-(O+M)/F,N=0,B=0,U=0,j=0;for(n=44100*n+9,b*=44100,i*=44100,r*=44100,v*=44100,f*=500*y/85766121e6,g*=y/44100,p*=y/44100,u*=44100,d=44100*d|0,t*=.3*(globalThis.zzfxV||1),O=n+b+i+r+v|0;A<O;a[A++]=X*t)++L%(100*m|0)||(X=o?1<o?2<o?3<o?P(I*I):x.max(x.min(x.tan(I),1),-1):1-(2*I/y%2+2)%2:1-4*x.abs(x.round(I/y)-I/y):P(I),X=(d?1-w+w*P(y*A/d):1)*(X<0?-1:1)*x.abs(X)**s*(A<n?A/n:A<n+b?1-(A-n)/b*(1-E):A<n+b+i?E:A<O-v?(O-A-v)/r*E:0),X=v?X/2+(v>A?0:(A<O-v?1:(O-A)/v)*a[A-v|0]/2/t):X,T&&(X=j=R*N+G*(N=B)+R*(B=X)-_*U-W*(U=j))),I+=(z=(l+=c+=f)*x.cos(g*S++))+z*h*P(A**5),k&&++k>u&&(l+=p,H+=p,k=0),!d||++C%d||(l=H,c=D,k=k||1);(t=e.createBuffer(1,O,44100)).getChannelData(0).set(a),(l=e.createBufferSource()).buffer=t,l.connect(e.destination),l.start()},a=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(e={}){let l=globalThis,n=Math.PI,i=2*n,r=requestAnimationFrame,o=[],s=(e,t,a)=>{e.addEventListener(t,a,!1),o.push(()=>e.removeEventListener(t,a,!1))};e=Object.assign({fullscreen:!0,width:null,height:null,autoscale:!0,pixelart:!1,antialias:!1,canvas:null,global:!0,loop:null,pauseOnBlur:!0,tapEvents:!0,keyboardEvents:!0,animate:!0},e);let c=!1,f=[],p=e.canvas||document.createElement("canvas"),u=e.fullscreen,d=e.autoscale,h=e.animate,g=1,m,v=.5,E=1,b,w,T=0,x,y="sans-serif",D=32,H=Date.now(),I=e.global,S={init:null,update:null,draw:null,resized:null,tap:null,untap:null,tapping:null,tapped:null},A={settings:Object.assign({},e),colors:a},k={WIDTH:e.width,HEIGHT:e.height||e.width,CANVAS:null,ELAPSED:0,CENTERX:0,CENTERY:0,MOUSEX:-1,MOUSEY:-1,DEFAULT_SFX:[.5,,1675,,.06,.2,1,1.8,,,637,.06],PI:n,TWO_PI:i,HALF_PI:.5*n,lerp:(e,t,a)=>e+a*(t-e),deg2rad:e=>n/180*e,rad2deg:e=>180/n*e,clamp:(e,t,a)=>e<t?t:e>a?a:e,wrap:(e,t,a)=>e-(a-t)*Math.floor((e-t)/(a-t)),map(e,t,a,l,n,i){let r=(e-t)/(a-t)*(n-l)+l;return i?k.clamp(r,l,n):r},norm:(e,t,a)=>k.map(e,t,a,0,1),rand:(e=0,t=1)=>(H=(1664525*H+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>Math.floor(k.rand(e,t+1)),seed:e=>null==e?H:H=~~e,cls(e){null==e?m.clearRect(0,0,m.canvas.width,m.canvas.height):k.rectfill(0,0,m.canvas.width,m.canvas.height,e)},rect(e,t,a,l,n,i=null){m.beginPath(),m[i?"roundRect":"rect"](~~e-v,~~t-v,~~a+2*v,~~l+2*v,i),k.stroke(n)},rectfill(e,t,a,l,n,i=null){m.beginPath(),m[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),k.fill(n)},circ(e,t,a,l){m.beginPath(),m.arc(~~e,~~t,~~a,0,i),k.stroke(l)},circfill(e,t,a,l){m.beginPath(),m.arc(~~e,~~t,~~a,0,i),k.fill(l)},line(e,t,a,l,n){m.beginPath();let i=.5*(0!==v&&~~e==~~a),r=.5*(0!==v&&~~t==~~l);m.moveTo(~~e+i,~~t+r),m.lineTo(~~a+i,~~l+r),k.stroke(n)},linewidth(e){m.lineWidth=~~e,v=.5*(~~e%2!=0)},linedash(e,t=0){m.setLineDash(e),m.lineDashOffset=t},text(e,t,a,l=3,n="normal"){m.font=`${n} ${D}px ${y}`,m.fillStyle=k.getcolor(l),m.fillText(a,~~e,~~t)},textfont(e){y=e},textsize(e){D=e},textalign(e,t){e&&(m.textAlign=e),t&&(m.textBaseline=t)},image(e,t,a){m.drawImage(a,~~e,~~t)},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,r=m;if(n.width=e*i,n.height=t*i,(m=n.getContext("2d")).scale(i,i),a.push){let e=0,t=0;for(let l of(m.imageSmoothingEnabled=!1,a)){for(let a of l)" "!==a&&"."!==a&&k.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(m);return m=r,n},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=k.clamp(e,0,1)},path:e=>new Path2D(e),fill(e,t){m.fillStyle=k.getcolor(e),t?m.fill(t):m.fill()},stroke(e,t){m.strokeStyle=k.getcolor(e),t?m.stroke(t):m.stroke()},clip(e){m.clip(e)},sfx:(e,a=0,n=1)=>!(l.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||k.DEFAULT_SFX,(0!==a||1!==n)&&((e=e.slice())[0]=n*(e[0]||1),e[10]=~~e[10]+a),t.apply(0,e),e),volume(e){l.zzfxV=e},colrect:(e,t,a,l,n,i,r,o)=>e<n+r&&e+a>n&&t<i+o&&t+l>i,colcirc:(e,t,a,l,n,i)=>(l-e)*(l-e)+(n-t)*(n-t)<=(a+i)*(a+i),use(e,t={}){c?z(e,t):f.push([e,t])},listen:(e,t)=>(S[e]=S[e]||new Set,S[e].add(t),()=>S[e].delete(t)),emit(e,t,a,l,n){O("before:"+e,t,a,l,n),O(e,t,a,l,n),O("after:"+e,t,a,l,n)},getcolor:e=>a[~~e%a.length],setvar(e,t){k[e]=t,I&&(l[e]=t)},resize(e,t){k.setvar("WIDTH",p.width=e),k.setvar("HEIGHT",p.height=t),X()},timescale(e){E=e},setfps(e){w=1/~~e},quit(){for(let e of(k.emit("quit"),o))e();if(cancelAnimationFrame(x),S=!1,I){for(let e in k)delete l[e];delete l.__litecanvas}}};for(let e of["sin","cos","atan2","hypot","tan","abs","ceil","round","floor","trunc","min","max","pow","sqrt","sign","exp"])k[e]=Math[e];function C(){c=!0;let t=e.loop?e.loop:l;for(let e in S)t[e]&&k.listen(e,t[e]);for(let[e,t]of f)z(e,t);if((u||d)&&s(l,"resize",X),X(),e.tapEvents){let e=(e,t)=>[(e-p.offsetLeft)/g,(t-p.offsetTop)/g],t=new Map,a=(e,a,l)=>{let n={x:a,y:l,startX:a,startY:l,ts:performance.now()};return t.set(e,n),n},n=(e,l,n)=>{let i=t.get(e)||a(e);i.x=l,i.y=n},i=e=>e&&performance.now()-e.ts<=200,r=!1;s(p,"mousedown",t=>{t.preventDefault();let[l,n]=e(t.pageX,t.pageY);k.emit("tap",l,n,0),a(0,l,n),r=!0}),s(p,"mousemove",t=>{t.preventDefault();let[a,l]=e(t.pageX,t.pageY);k.setvar("MOUSEX",a),k.setvar("MOUSEY",l),r&&(k.emit("tapping",a,l,0),n(0,a,l))}),s(p,"mouseup",a=>{a.preventDefault();let l=t.get(0),[n,o]=e(a.pageX,a.pageY);i(l)&&k.emit("tapped",l.startX,l.startY,0),k.emit("untap",n,o,0),t.delete(0),r=!1}),s(p,"touchstart",t=>{for(let l of(t.preventDefault(),t.changedTouches)){let[t,n]=e(l.pageX,l.pageY);k.emit("tap",t,n,l.identifier+1),a(l.identifier+1,t,n)}}),s(p,"touchmove",t=>{for(let a of(t.preventDefault(),t.changedTouches)){let[t,l]=e(a.pageX,a.pageY);k.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let o=e=>{e.preventDefault();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)&&k.emit("tapped",l.startX,l.startY,e),k.emit("untap",l.x,l.y,e),t.delete(e))};s(p,"touchend",o),s(p,"touchcancel",o),s(l,"blur",()=>{for(let[e,a]of(r=!1,t))k.emit("untap",a.x,a.y,e),t.delete(e)})}if(e.keyboardEvents){let e=new Set;k.setvar("iskeydown",t=>"any"===t?e.size>0:e.has(t.toLowerCase())),s(l,"keydown",t=>{e.add(t.key.toLowerCase())}),s(l,"keyup",t=>{e.delete(t.key.toLowerCase())}),s(l,"blur",()=>e.clear())}e.pauseOnBlur&&(s(l,"blur",()=>{x=cancelAnimationFrame(x)}),s(l,"focus",()=>{x||(b=performance.now(),x=r(L))})),k.setfps(60),k.emit("init",k),b=performance.now(),x=r(L)}function L(e){h&&(x=r(L));let t=0;for(T+=(e-b)/1e3,b=e,h||(T=w);T>=w;T-=w)k.emit("update",w*E),k.setvar("ELAPSED",k.ELAPSED+w*E),t++;t&&(k.textalign("start","top"),k.emit("draw"))}function X(){let t=l.innerWidth,a=l.innerHeight,n=p.style;n.display="block",u?(n.position="absolute",n.inset=0,k.setvar("WIDTH",p.width=t),k.setvar("HEIGHT",p.height=a)):d&&(n.margin="auto",g=Math.min(t/k.WIDTH,a/k.HEIGHT),g=(e.pixelart?~~g:g)||1,n.width=k.WIDTH*g+"px",n.height=k.HEIGHT*g+"px"),k.setvar("CENTERX",k.WIDTH/2),k.setvar("CENTERY",k.HEIGHT/2),(!e.antialias||e.pixelart)&&(m.imageSmoothingEnabled=!1,p.style.imageRendering="pixelated"),k.emit("resized",g),h||r(L)}function O(e,t,a,l,n){if(S[e])for(let i of S[e])i(t,a,l,n)}function z(e,t){let a=e(k,A,t);if("object"==typeof a)for(let e of Object.keys(a))k.setvar(e,a[e])}if(I){if(l.__litecanvas)throw"global litecanvas already instantiated";Object.assign(l,k),l.__litecanvas=k}return p="string"==typeof p?document.querySelector(p):p,k.setvar("CANVAS",p),m=p.getContext("2d"),s(p,"click",()=>l.focus()),k.WIDTH>0&&(u=!1),p.style="",p.width=k.WIDTH,p.height=k.HEIGHT||k.WIDTH,p.parentNode||document.body.appendChild(p),"loading"===document.readyState?s(l,"DOMContentLoaded",()=>r(C)):r(C),k}})();
1
+ (()=>{var e=new AudioContext,t=(t=1,a=.05,l=220,n=0,i=0,o=.1,r=0,s=1,c=0,f=0,u=0,d=0,p=0,h=0,g=0,m=0,v=0,E=1,b=0,w=0,T=0)=>{let x=Math,H=2*x.PI,y=c*=500*H/44100/44100,I=l*=(1-a+2*a*x.random(a=[]))*H/44100,D=0,S=0,A=0,k=1,C=0,L=0,X=0,P=T<0?-1:1,z=H*P*T*2/44100,M=x.cos(z),O=x.sin,Y=O(z)/4,F=1+Y,W=-2*M/F,_=(1-Y)/F,R=(1+P*M)/2/F,G=-(P+M)/F,N=0,B=0,U=0,q=0;for(n=44100*n+9,b*=44100,i*=44100,o*=44100,v*=44100,f*=500*H/85766121e6,g*=H/44100,u*=H/44100,d*=44100,p=44100*p|0,t*=.3*(globalThis.zzfxV||1),P=n+b+i+o+v|0;A<P;a[A++]=X*t)++L%(100*m|0)||(X=r?1<r?2<r?3<r?O(D*D):x.max(x.min(x.tan(D),1),-1):1-(2*D/H%2+2)%2:1-4*x.abs(x.round(D/H)-D/H):O(D),X=(p?1-w+w*O(H*A/p):1)*(X<0?-1:1)*x.abs(X)**s*(A<n?A/n:A<n+b?1-(A-n)/b*(1-E):A<n+b+i?E:A<P-v?(P-A-v)/o*E:0),X=v?X/2+(v>A?0:(A<P-v?1:(P-A)/v)*a[A-v|0]/2/t):X,T&&(X=q=R*N+G*(N=B)+R*(B=X)-_*U-W*(U=q))),D+=(z=(l+=c+=f)*x.cos(g*S++))+z*h*O(A**5),k&&++k>d&&(l+=u,I+=u,k=0),!p||++C%p||(l=I,c=y,k=k||1);(t=e.createBuffer(1,P,44100)).getChannelData(0).set(a),(l=e.createBufferSource()).buffer=t,l.connect(e.destination),l.start()},a=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(e={}){let l=globalThis,n=Math.PI,i=2*n,o=requestAnimationFrame,r=[],s=(e,t,a)=>{e.addEventListener(t,a,!1),r.push(()=>e.removeEventListener(t,a,!1))};e=Object.assign({fullscreen:!0,width:null,height:null,autoscale:!0,pixelart:!1,antialias:!1,canvas:null,global:!0,loop:null,pauseOnBlur:!0,tapEvents:!0,keyboardEvents:!0,animate:!0},e);let c=!1,f=[],u=e.canvas||document.createElement("canvas"),d=e.fullscreen,p=e.autoscale,h=e.animate,g=1,m,v=.5,E=1,b,w,T=0,x,H="sans-serif",y=32,I=Date.now(),D=e.global,S={init:null,update:null,draw:null,resized:null,tap:null,untap:null,tapping:null,tapped:null},A={settings:Object.assign({},e),colors:a},k={WIDTH:e.width,HEIGHT:e.height||e.width,CANVAS:null,ELAPSED:0,CENTERX:0,CENTERY:0,MOUSEX:-1,MOUSEY:-1,DEFAULT_SFX:[.5,,1675,,.06,.2,1,1.8,,,637,.06],TWO_PI:i,HALF_PI:n/2,lerp:(e,t,a)=>a*(t-e)+e,deg2rad:e=>n/180*e,rad2deg:e=>180/n*e,clamp:(e,t,a)=>e<t?t:e>a?a:e,wrap:(e,t,a)=>e-(a-t)*Math.floor((e-t)/(a-t)),map(e,t,a,l,n,i){let o=(e-t)/(a-t)*(n-l)+l;return i?k.clamp(o,l,n):o},norm:(e,t,a)=>k.map(e,t,a,0,1),rand:(e=0,t=1)=>(I=(1664525*I+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>Math.floor(k.rand(e,t+1)),seed:e=>null==e?I:I=~~e,cls(e){null==e?m.clearRect(0,0,m.canvas.width,m.canvas.height):k.rectfill(0,0,m.canvas.width,m.canvas.height,e)},rect(e,t,a,l,n,i=null){m.beginPath(),m[i?"roundRect":"rect"](~~e-v,~~t-v,~~a+2*v,~~l+2*v,i),k.stroke(n)},rectfill(e,t,a,l,n,i=null){m.beginPath(),m[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),k.fill(n)},circ(e,t,a,l){m.beginPath(),m.arc(~~e,~~t,~~a,0,i),k.stroke(l)},circfill(e,t,a,l){m.beginPath(),m.arc(~~e,~~t,~~a,0,i),k.fill(l)},line(e,t,a,l,n){m.beginPath();let i=.5*(0!==v&&~~e==~~a),o=.5*(0!==v&&~~t==~~l);m.moveTo(~~e+i,~~t+o),m.lineTo(~~a+i,~~l+o),k.stroke(n)},linewidth(e){m.lineWidth=~~e,v=.5*(~~e%2!=0)},linedash(e,t=0){m.setLineDash(e),m.lineDashOffset=t},text(e,t,a,l=3,n="normal"){m.font=`${n} ${y}px ${H}`,m.fillStyle=k.getcolor(l),m.fillText(a,~~e,~~t)},textfont(e){H=e},textsize(e){y=e},textalign(e,t){e&&(m.textAlign=e),t&&(m.textBaseline=t)},image(e,t,a){m.drawImage(a,~~e,~~t)},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,o=m;if(n.width=e*i,n.height=t*i,(m=n.getContext("2d")).scale(i,i),a.push){let e=0,t=0;for(let l of(m.imageSmoothingEnabled=!1,a)){for(let a of l)" "!==a&&"."!==a&&k.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(m);return m=o,n},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=k.clamp(e,0,1)},path:e=>new Path2D(e),fill(e,t){m.fillStyle=k.getcolor(e),t?m.fill(t):m.fill()},stroke(e,t){m.strokeStyle=k.getcolor(e),t?m.stroke(t):m.stroke()},clip(e){m.clip(e)},sfx:(e,a=0,n=1)=>!(l.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||k.DEFAULT_SFX,(0!==a||1!==n)&&((e=e.slice())[0]=n*(e[0]||1),e[10]=~~e[10]+a),t.apply(0,e),e),volume(e){l.zzfxV=e},colrect:(e,t,a,l,n,i,o,r)=>e<n+o&&e+a>n&&t<i+r&&t+l>i,colcirc:(e,t,a,l,n,i)=>(l-e)*(l-e)+(n-t)*(n-t)<=(a+i)*(a+i),use(e,t={}){c?z(e,t):f.push([e,t])},listen:(e,t)=>(S[e]=S[e]||new Set,S[e].add(t),()=>S[e].delete(t)),emit(e,t,a,l,n){P("before:"+e,t,a,l,n),P(e,t,a,l,n),P("after:"+e,t,a,l,n)},getcolor:e=>a[~~e%a.length],setvar(e,t){k[e]=t,D&&(l[e]=t)},resize(e,t){k.setvar("WIDTH",u.width=e),k.setvar("HEIGHT",u.height=t),X()},timescale(e){E=e},setfps(e){w=1/~~e},quit(){for(let e of(k.emit("quit"),r))e();if(cancelAnimationFrame(x),S=!1,D){for(let e in k)delete l[e];delete l.__litecanvas}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,round,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))k[e]=Math[e];function C(){c=!0;let t=e.loop?e.loop:l;for(let e in S)t[e]&&k.listen(e,t[e]);for(let[e,t]of f)z(e,t);if((d||p)&&s(l,"resize",X),X(),e.tapEvents){let e=(e,t)=>[(e-u.offsetLeft)/g,(t-u.offsetTop)/g],t=new Map,a=(e,a,l)=>{let n={x:a,y:l,startX:a,startY:l,ts:performance.now()};return t.set(e,n),n},n=(e,l,n)=>{let i=t.get(e)||a(e);i.x=l,i.y=n},i=e=>e&&performance.now()-e.ts<=200,o=e=>e.preventDefault(),r=!1;s(u,"mousedown",t=>{if(0===t.button){o(t);let[l,n]=e(t.pageX,t.pageY);k.emit("tap",l,n,0),a(0,l,n),r=!0}}),s(u,"mouseup",a=>{if(0===a.button){o(a);let l=t.get(0),[n,s]=e(a.pageX,a.pageY);i(l)&&k.emit("tapped",l.startX,l.startY,0),k.emit("untap",n,s,0),t.delete(0),r=!1}}),s(u,"mousemove",t=>{o(t);let[a,l]=e(t.pageX,t.pageY);k.setvar("MOUSEX",a),k.setvar("MOUSEY",l),r&&(k.emit("tapping",a,l,0),n(0,a,l))}),s(u,"touchstart",t=>{for(let l of(o(t),t.changedTouches)){let[t,n]=e(l.pageX,l.pageY);k.emit("tap",t,n,l.identifier+1),a(l.identifier+1,t,n)}}),s(u,"touchmove",t=>{for(let a of(o(t),t.changedTouches)){let[t,l]=e(a.pageX,a.pageY);k.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let c=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)&&k.emit("tapped",l.startX,l.startY,e),k.emit("untap",l.x,l.y,e),t.delete(e))};s(u,"touchend",c),s(u,"touchcancel",c),s(l,"blur",()=>{for(let[e,a]of(r=!1,t))k.emit("untap",a.x,a.y,e),t.delete(e)})}if(e.keyboardEvents){let e=new Set;k.setvar("iskeydown",t=>"any"===t?e.size>0:e.has(t.toLowerCase())),s(l,"keydown",t=>{e.add(t.key.toLowerCase())}),s(l,"keyup",t=>{e.delete(t.key.toLowerCase())}),s(l,"blur",()=>e.clear())}e.pauseOnBlur&&(s(l,"blur",()=>{x=cancelAnimationFrame(x)}),s(l,"focus",()=>{x||(x=o(L))})),k.setfps(60),k.emit("init",k),b=performance.now(),x=o(L)}function L(e){h&&(x=o(L));let t=0,a=(e-b)/1e3;if(b=e,!(a>30*w)){for(T+=a,h||(T=w);T>=w;T-=w)k.emit("update",w*E),k.setvar("ELAPSED",k.ELAPSED+w*E),t++;t&&(k.textalign("start","top"),k.emit("draw"))}}function X(){let t=l.innerWidth,a=l.innerHeight,n=u.style;n.display="block",d?(n.position="absolute",n.inset=0,k.setvar("WIDTH",u.width=t),k.setvar("HEIGHT",u.height=a)):p&&(n.margin="auto",g=Math.min(t/k.WIDTH,a/k.HEIGHT),g=(e.pixelart?~~g:g)||1,n.width=k.WIDTH*g+"px",n.height=k.HEIGHT*g+"px"),k.setvar("CENTERX",k.WIDTH/2),k.setvar("CENTERY",k.HEIGHT/2),(!e.antialias||e.pixelart)&&(m.imageSmoothingEnabled=!1,n.imageRendering="pixelated"),k.emit("resized",g),h||o(L)}function P(e,t,a,l,n){if(S[e])for(let i of S[e])i(t,a,l,n)}function z(e,t){let a=e(k,A,t);for(let e in a)k.setvar(e,a[e])}if(D){if(l.__litecanvas)throw"global litecanvas already instantiated";Object.assign(l,k),l.__litecanvas=k}return u="string"==typeof u?document.querySelector(u):u,k.setvar("CANVAS",u),m=u.getContext("2d"),s(u,"click",()=>l.focus()),k.WIDTH>0&&(d=!1),u.style="",u.width=k.WIDTH,u.height=k.HEIGHT||k.WIDTH,u.parentNode||document.body.appendChild(u),"loading"===document.readyState?s(l,"DOMContentLoaded",()=>o(C)):o(C),k}})();
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.74.2",
4
- "description": "Lightweight HTML5 canvas engine suitable for small games and animations.",
3
+ "version": "0.75.0",
4
+ "description": "Lightweight HTML5 canvas 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.11.4",
9
+ "@swc/core": "^1.11.7",
10
10
  "ava": "^6.2.0",
11
11
  "esbuild": "^0.25.0",
12
12
  "gzip-size": "^7.0.0",
13
- "prettier": "^3.5.2"
13
+ "prettier": "^3.5.3"
14
14
  },
15
15
  "homepage": "https://litecanvas.github.io/about.html",
16
16
  "repository": {
@@ -41,8 +41,11 @@
41
41
  "gamedev",
42
42
  "game",
43
43
  "engine",
44
- "library",
45
- "framework"
44
+ "javascript",
45
+ "js13k",
46
+ "creative-coding",
47
+ "pico-8",
48
+ "p5"
46
49
  ],
47
50
  "files": [
48
51
  "dist",
package/src/dev.js CHANGED
@@ -1,5 +1,4 @@
1
1
  /**
2
- *
3
2
  * @param {any} condition
4
3
  * @param {string} message
5
4
  */
package/src/index.js CHANGED
@@ -134,14 +134,6 @@ export default function litecanvas(settings = {}) {
134
134
  DEFAULT_SFX: [0.5, , 1675, , 0.06, 0.2, 1, 1.8, , , 637, 0.06],
135
135
 
136
136
  /** MATH API */
137
- /**
138
- * The value of the mathematical constant PI (π).
139
- * Approximately 3.14159
140
- *
141
- * @type {number}
142
- */
143
- PI,
144
-
145
137
  /**
146
138
  * Twice the value of the mathematical constant PI (π).
147
139
  * Approximately 6.28318
@@ -159,7 +151,7 @@ export default function litecanvas(settings = {}) {
159
151
  *
160
152
  * @type {number}
161
153
  */
162
- HALF_PI: PI * 0.5,
154
+ HALF_PI: PI / 2,
163
155
 
164
156
  /**
165
157
  * Calculates a linear (interpolation) value over t%.
@@ -176,7 +168,7 @@ export default function litecanvas(settings = {}) {
176
168
  assert(isFinite(end), 'lerp: 2nd param must be a number')
177
169
  assert(isFinite(t), 'lerp: 3rd param must be a number')
178
170
  }
179
- return start + t * (end - start)
171
+ return t * (end - start) + start
180
172
  },
181
173
 
182
174
  /**
@@ -1228,26 +1220,9 @@ export default function litecanvas(settings = {}) {
1228
1220
  },
1229
1221
  }
1230
1222
 
1231
- /** Copy some functions from native `Math` object */
1232
- for (const k of [
1233
- 'sin',
1234
- 'cos',
1235
- 'atan2',
1236
- 'hypot',
1237
- 'tan',
1238
- 'abs',
1239
- 'ceil',
1240
- 'round',
1241
- 'floor',
1242
- 'trunc',
1243
- 'min',
1244
- 'max',
1245
- 'pow',
1246
- 'sqrt',
1247
- 'sign',
1248
- 'exp',
1249
- ]) {
1250
- // import some native Math functions
1223
+ // prettier-ignore
1224
+ for (const k of 'PI,sin,cos,atan2,hypot,tan,abs,ceil,round,floor,trunc,min,max,pow,sqrt,sign,exp'.split(',')) {
1225
+ // import native Math functions
1251
1226
  instance[k] = Math[k]
1252
1227
  }
1253
1228
 
@@ -1296,20 +1271,38 @@ export default function litecanvas(settings = {}) {
1296
1271
  tap.x = x
1297
1272
  tap.y = y
1298
1273
  },
1299
- _checkTapped = (tap) => tap && performance.now() - tap.ts <= 200
1274
+ _checkTapped = (tap) =>
1275
+ tap && performance.now() - tap.ts <= 200,
1276
+ preventDefault = (ev) => ev.preventDefault()
1300
1277
 
1301
1278
  let _pressingMouse = false
1302
1279
 
1303
1280
  on(_canvas, 'mousedown', (ev) => {
1304
- ev.preventDefault()
1305
- const [x, y] = _getXY(ev.pageX, ev.pageY)
1306
- instance.emit('tap', x, y, 0)
1307
- _registerTap(0, x, y)
1308
- _pressingMouse = true
1281
+ if (ev.button === 0) {
1282
+ preventDefault(ev)
1283
+ const [x, y] = _getXY(ev.pageX, ev.pageY)
1284
+ instance.emit('tap', x, y, 0)
1285
+ _registerTap(0, x, y)
1286
+ _pressingMouse = true
1287
+ }
1288
+ })
1289
+
1290
+ on(_canvas, 'mouseup', (ev) => {
1291
+ if (ev.button === 0) {
1292
+ preventDefault(ev)
1293
+ const tap = _taps.get(0)
1294
+ const [x, y] = _getXY(ev.pageX, ev.pageY)
1295
+ if (_checkTapped(tap)) {
1296
+ instance.emit('tapped', tap.startX, tap.startY, 0)
1297
+ }
1298
+ instance.emit('untap', x, y, 0)
1299
+ _taps.delete(0)
1300
+ _pressingMouse = false
1301
+ }
1309
1302
  })
1310
1303
 
1311
1304
  on(_canvas, 'mousemove', (ev) => {
1312
- ev.preventDefault()
1305
+ preventDefault(ev)
1313
1306
 
1314
1307
  const [x, y] = _getXY(ev.pageX, ev.pageY)
1315
1308
  instance.setvar('MOUSEX', x)
@@ -1321,20 +1314,8 @@ export default function litecanvas(settings = {}) {
1321
1314
  _updateTap(0, x, y)
1322
1315
  })
1323
1316
 
1324
- on(_canvas, 'mouseup', (ev) => {
1325
- ev.preventDefault()
1326
- const tap = _taps.get(0)
1327
- const [x, y] = _getXY(ev.pageX, ev.pageY)
1328
- if (_checkTapped(tap)) {
1329
- instance.emit('tapped', tap.startX, tap.startY, 0)
1330
- }
1331
- instance.emit('untap', x, y, 0)
1332
- _taps.delete(0)
1333
- _pressingMouse = false
1334
- })
1335
-
1336
1317
  on(_canvas, 'touchstart', (ev) => {
1337
- ev.preventDefault()
1318
+ preventDefault(ev)
1338
1319
  /** @type {TouchList} touches */
1339
1320
  const touches = ev.changedTouches
1340
1321
  for (const touch of touches) {
@@ -1345,7 +1326,7 @@ export default function litecanvas(settings = {}) {
1345
1326
  })
1346
1327
 
1347
1328
  on(_canvas, 'touchmove', (ev) => {
1348
- ev.preventDefault()
1329
+ preventDefault(ev)
1349
1330
  /** @type {TouchList} touches */
1350
1331
  const touches = ev.changedTouches
1351
1332
  for (const touch of touches) {
@@ -1356,7 +1337,7 @@ export default function litecanvas(settings = {}) {
1356
1337
  })
1357
1338
 
1358
1339
  const _touchEndHandler = (ev) => {
1359
- ev.preventDefault()
1340
+ preventDefault(ev)
1360
1341
  const existing = []
1361
1342
 
1362
1343
  if (ev.targetTouches.length > 0) {
@@ -1433,7 +1414,6 @@ export default function litecanvas(settings = {}) {
1433
1414
 
1434
1415
  on(root, 'focus', () => {
1435
1416
  if (!_rafid) {
1436
- _lastFrameTime = performance.now()
1437
1417
  _rafid = raf(drawFrame)
1438
1418
  }
1439
1419
  })
@@ -1459,9 +1439,13 @@ export default function litecanvas(settings = {}) {
1459
1439
  let updated = 0,
1460
1440
  frameTime = (now - _lastFrameTime) / 1000
1461
1441
 
1462
- _accumulated += frameTime
1463
1442
  _lastFrameTime = now
1464
1443
 
1444
+ if (frameTime > _deltaTime * 30)
1445
+ return console.log('skipping too long frame')
1446
+
1447
+ _accumulated += frameTime
1448
+
1465
1449
  if (!_animated) {
1466
1450
  _accumulated = _deltaTime
1467
1451
  }
@@ -1478,16 +1462,31 @@ export default function litecanvas(settings = {}) {
1478
1462
  if (updated) {
1479
1463
  instance.textalign('start', 'top') // default values for textAlign & textBaseline
1480
1464
  instance.emit('draw')
1481
- console.log(updated)
1482
1465
  }
1483
1466
  }
1484
1467
 
1485
1468
  function setupCanvas() {
1469
+ /** @type {HTMLCanvasElement} */
1486
1470
  _canvas =
1487
1471
  'string' === typeof _canvas
1488
1472
  ? document.querySelector(_canvas)
1489
1473
  : _canvas
1490
1474
 
1475
+ if (DEV_BUILD) {
1476
+ assert(
1477
+ _canvas && _canvas.tagName === 'CANVAS',
1478
+ 'Invalid canvas element'
1479
+ )
1480
+ assert(
1481
+ null === instance.WIDTH || instance.WIDTH > 0,
1482
+ 'Litecanvas\' "width" option should be null or a positive number'
1483
+ )
1484
+ assert(
1485
+ null === instance.HEIGHT || instance.HEIGHT > 0,
1486
+ 'Litecanvas\' "width" option should be null or a positive number'
1487
+ )
1488
+ }
1489
+
1491
1490
  instance.setvar('CANVAS', _canvas)
1492
1491
  _ctx = _canvas.getContext('2d')
1493
1492
 
@@ -1534,7 +1533,7 @@ export default function litecanvas(settings = {}) {
1534
1533
  // restore canvas image rendering properties
1535
1534
  if (!settings.antialias || settings.pixelart) {
1536
1535
  _ctx.imageSmoothingEnabled = false
1537
- _canvas.style.imageRendering = 'pixelated'
1536
+ styles.imageRendering = 'pixelated'
1538
1537
  }
1539
1538
 
1540
1539
  instance.emit('resized', _scale)
@@ -1556,10 +1555,14 @@ export default function litecanvas(settings = {}) {
1556
1555
  */
1557
1556
  function loadPlugin(callback, config) {
1558
1557
  const pluginData = callback(instance, _helpers, config)
1559
- if ('object' === typeof pluginData) {
1560
- for (const key of Object.keys(pluginData)) {
1561
- instance.setvar(key, pluginData[key])
1562
- }
1558
+ if (DEV_BUILD) {
1559
+ assert(
1560
+ null == pluginData || 'object' === typeof pluginData,
1561
+ 'Litecanvas plugins should return an object or nothing'
1562
+ )
1563
+ }
1564
+ for (const key in pluginData) {
1565
+ instance.setvar(key, pluginData[key])
1563
1566
  }
1564
1567
  }
1565
1568
 
package/src/zzfx.js CHANGED
@@ -1,111 +1,5 @@
1
1
  // ZzFXMicro - Zuper Zmall Zound Zynth - v1.3.0 by Frank Force | https://github.com/KilledByAPixel/ZzFX
2
2
  const zzfxX = /** @__PURE__ */ new AudioContext()
3
3
 
4
- export const zzfx = (
5
- p = 1,
6
- k = 0.05,
7
- b = 220,
8
- e = 0,
9
- r = 0,
10
- t = 0.1,
11
- q = 0,
12
- D = 1,
13
- u = 0,
14
- y = 0,
15
- v = 0,
16
- z = 0,
17
- l = 0,
18
- E = 0,
19
- A = 0,
20
- F = 0,
21
- c = 0,
22
- w = 1,
23
- m = 0,
24
- B = 0,
25
- N = 0
26
- ) => {
27
- let M = Math,
28
- d = 2 * M.PI,
29
- R = 44100,
30
- G = (u *= (500 * d) / R / R),
31
- C = (b *= ((1 - k + 2 * k * M.random((k = []))) * d) / R),
32
- g = 0,
33
- H = 0,
34
- a = 0,
35
- n = 1,
36
- I = 0,
37
- J = 0,
38
- f = 0,
39
- h = N < 0 ? -1 : 1,
40
- x = (d * h * N * 2) / R,
41
- L = M.cos(x),
42
- Z = M.sin,
43
- K = Z(x) / 4,
44
- O = 1 + K,
45
- X = (-2 * L) / O,
46
- Y = (1 - K) / O,
47
- P = (1 + h * L) / 2 / O,
48
- Q = -(h + L) / O,
49
- S = P,
50
- T = 0,
51
- U = 0,
52
- V = 0,
53
- W = 0
54
- e = R * e + 9
55
- m *= R
56
- r *= R
57
- t *= R
58
- c *= R
59
- y *= (500 * d) / R ** 3
60
- A *= d / R
61
- v *= d / R
62
- z *= R
63
- l = (R * l) | 0
64
- p *= 0.3 * (globalThis.zzfxV || 1)
65
- for (h = (e + m + r + t + c) | 0; a < h; k[a++] = f * p)
66
- ++J % ((100 * F) | 0) ||
67
- ((f = q
68
- ? 1 < q
69
- ? 2 < q
70
- ? 3 < q
71
- ? Z(g * g)
72
- : M.max(M.min(M.tan(g), 1), -1)
73
- : 1 - (((((2 * g) / d) % 2) + 2) % 2)
74
- : 1 - 4 * M.abs(M.round(g / d) - g / d)
75
- : Z(g)),
76
- (f =
77
- (l ? 1 - B + B * Z((d * a) / l) : 1) *
78
- (f < 0 ? -1 : 1) *
79
- M.abs(f) ** D *
80
- (a < e
81
- ? a / e
82
- : a < e + m
83
- ? 1 - ((a - e) / m) * (1 - w)
84
- : a < e + m + r
85
- ? w
86
- : a < h - c
87
- ? ((h - a - c) / t) * w
88
- : 0)),
89
- (f = c
90
- ? f / 2 +
91
- (c > a
92
- ? 0
93
- : ((a < h - c ? 1 : (h - a) / c) * k[(a - c) | 0]) /
94
- 2 /
95
- p)
96
- : f),
97
- N
98
- ? (f = W =
99
- S * T + Q * (T = U) + P * (U = f) - Y * V - X * (V = W))
100
- : 0),
101
- (x = (b += u += y) * M.cos(A * H++)),
102
- (g += x + x * E * Z(a ** 5)),
103
- n && ++n > z && ((b += v), (C += v), (n = 0)),
104
- !l || ++I % l || ((b = C), (u = G), (n = n || 1))
105
- p = zzfxX.createBuffer(1, h, R)
106
- p.getChannelData(0).set(k)
107
- b = zzfxX.createBufferSource()
108
- b.buffer = p
109
- b.connect(zzfxX.destination)
110
- b.start()
111
- }
4
+ // prettier-ignore
5
+ export const zzfx=(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*(globalThis.zzfxV||1),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()};