asciify-engine 1.0.16 → 1.0.18
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/index.cjs +594 -317
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +160 -186
- package/dist/index.d.ts +160 -186
- package/dist/index.js +592 -318
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -111,6 +111,8 @@ var HOVER_PRESETS = {
|
|
|
111
111
|
options: { hoverStrength: 0.5, hoverEffect: "glow", hoverRadius: 0.15, hoverColor: "#60d5f7" }
|
|
112
112
|
}
|
|
113
113
|
};
|
|
114
|
+
|
|
115
|
+
// src/core/utils.ts
|
|
114
116
|
function createOffscreenCanvas(width, height) {
|
|
115
117
|
const canvas = document.createElement("canvas");
|
|
116
118
|
canvas.width = width;
|
|
@@ -141,29 +143,29 @@ var BAYER_4X4 = [
|
|
|
141
143
|
[3, 11, 1, 9],
|
|
142
144
|
[15, 7, 13, 5]
|
|
143
145
|
];
|
|
144
|
-
var _GRAY_LUT = new Array(256);
|
|
145
|
-
var _GREEN_LUT = new Array(256);
|
|
146
|
-
for (let _i = 0; _i < 256; _i++) {
|
|
147
|
-
_GRAY_LUT[_i] = `rgb(${_i},${_i},${_i})`;
|
|
148
|
-
_GREEN_LUT[_i] = `rgb(0,${_i},0)`;
|
|
149
|
-
}
|
|
150
146
|
function applyDither(lum, x, y, strength) {
|
|
151
147
|
if (strength <= 0) return lum;
|
|
152
148
|
const threshold = (BAYER_4X4[y % 4][x % 4] / 16 - 0.5) * strength * 128;
|
|
153
149
|
return Math.max(0, Math.min(255, lum + threshold));
|
|
154
150
|
}
|
|
151
|
+
var GRAY_LUT = new Array(256);
|
|
152
|
+
var GREEN_LUT = new Array(256);
|
|
153
|
+
for (let _i = 0; _i < 256; _i++) {
|
|
154
|
+
GRAY_LUT[_i] = `rgb(${_i},${_i},${_i})`;
|
|
155
|
+
GREEN_LUT[_i] = `rgb(0,${_i},0)`;
|
|
156
|
+
}
|
|
155
157
|
function getCellColorStr(cell, colorMode, acR, acG, acB) {
|
|
156
158
|
switch (colorMode) {
|
|
157
159
|
case "fullcolor":
|
|
158
160
|
return `rgb(${cell.r},${cell.g},${cell.b})`;
|
|
159
161
|
case "matrix":
|
|
160
|
-
return
|
|
162
|
+
return GREEN_LUT[0.299 * cell.r + 0.587 * cell.g + 0.114 * cell.b | 0];
|
|
161
163
|
case "accent": {
|
|
162
164
|
const ab = (0.299 * cell.r + 0.587 * cell.g + 0.114 * cell.b) / 255;
|
|
163
165
|
return `rgb(${acR * ab | 0},${acG * ab | 0},${acB * ab | 0})`;
|
|
164
166
|
}
|
|
165
167
|
default:
|
|
166
|
-
return
|
|
168
|
+
return GRAY_LUT[0.299 * cell.r + 0.587 * cell.g + 0.114 * cell.b | 0];
|
|
167
169
|
}
|
|
168
170
|
}
|
|
169
171
|
var _colorRGB = [0, 0, 0];
|
|
@@ -198,6 +200,11 @@ function getCellColorRGB(cell, colorMode, acR, acG, acB) {
|
|
|
198
200
|
}
|
|
199
201
|
return _colorRGB;
|
|
200
202
|
}
|
|
203
|
+
|
|
204
|
+
// src/core/animation.ts
|
|
205
|
+
function smoothstep(t) {
|
|
206
|
+
return t * t * (3 - 2 * t);
|
|
207
|
+
}
|
|
201
208
|
function getAnimationMultiplier(x, y, cols, rows, time, style, speed) {
|
|
202
209
|
if (style === "none") return 1;
|
|
203
210
|
const t = time * speed;
|
|
@@ -217,8 +224,8 @@ function getAnimationMultiplier(x, y, cols, rows, time, style, speed) {
|
|
|
217
224
|
}
|
|
218
225
|
case "rain": {
|
|
219
226
|
const drop = Math.sin(y / rows * Math.PI * 8 - t * 5 + x * 0.3) * 0.5 + 0.5;
|
|
220
|
-
const
|
|
221
|
-
return 0.1 + 0.9 * drop *
|
|
227
|
+
const fade2 = Math.sin(x / cols * Math.PI * 2 + t) * 0.3 + 0.7;
|
|
228
|
+
return 0.1 + 0.9 * drop * fade2;
|
|
222
229
|
}
|
|
223
230
|
case "breathe": {
|
|
224
231
|
const breathe = Math.sin(t * 2) * 0.3 + 0.7;
|
|
@@ -258,8 +265,8 @@ function getAnimationMultiplier(x, y, cols, rows, time, style, speed) {
|
|
|
258
265
|
const revealPoint = progress * totalCells * 1.3;
|
|
259
266
|
const dist = cellIndex - revealPoint;
|
|
260
267
|
if (dist > 0) return 0;
|
|
261
|
-
const
|
|
262
|
-
return Math.min(1,
|
|
268
|
+
const fade2 = Math.max(0, 1 + dist / (totalCells * 0.15));
|
|
269
|
+
return Math.min(1, fade2);
|
|
263
270
|
}
|
|
264
271
|
case "scatter": {
|
|
265
272
|
const scx = cols / 2;
|
|
@@ -280,6 +287,200 @@ function getAnimationMultiplier(x, y, cols, rows, time, style, speed) {
|
|
|
280
287
|
return 1;
|
|
281
288
|
}
|
|
282
289
|
}
|
|
290
|
+
var _hoverResult = { scale: 1, offsetX: 0, offsetY: 0, glow: 0, colorBlend: 0, proximity: 0 };
|
|
291
|
+
function computeHoverEffect(nx, ny, hoverX, hoverY, hoverIntensity, strength, cellW, cellH, effect = "spotlight", radiusFactor = 0.5) {
|
|
292
|
+
const dx = nx - hoverX;
|
|
293
|
+
const dy = ny - hoverY;
|
|
294
|
+
const distSq = dx * dx + dy * dy;
|
|
295
|
+
const radius = 0.08 + radiusFactor * 0.35 + strength * 0.04;
|
|
296
|
+
if (distSq >= radius * radius) {
|
|
297
|
+
_hoverResult.scale = 1;
|
|
298
|
+
_hoverResult.offsetX = 0;
|
|
299
|
+
_hoverResult.offsetY = 0;
|
|
300
|
+
_hoverResult.glow = 0;
|
|
301
|
+
_hoverResult.colorBlend = 0;
|
|
302
|
+
_hoverResult.proximity = 0;
|
|
303
|
+
return _hoverResult;
|
|
304
|
+
}
|
|
305
|
+
const dist = Math.sqrt(distSq);
|
|
306
|
+
const t = 1 - dist / radius;
|
|
307
|
+
const eased = smoothstep(t) * hoverIntensity;
|
|
308
|
+
let scale = 1;
|
|
309
|
+
let offsetX = 0;
|
|
310
|
+
let offsetY = 0;
|
|
311
|
+
let glow = 0;
|
|
312
|
+
let colorBlend = 0;
|
|
313
|
+
switch (effect) {
|
|
314
|
+
case "spotlight": {
|
|
315
|
+
scale = 1 + eased * strength * 1.8;
|
|
316
|
+
const angle = Math.atan2(dy, dx);
|
|
317
|
+
const pushForce = eased * eased * strength * 0.6;
|
|
318
|
+
offsetX = Math.cos(angle) * pushForce * cellW;
|
|
319
|
+
offsetY = Math.sin(angle) * pushForce * cellH;
|
|
320
|
+
glow = eased * strength * 0.4;
|
|
321
|
+
colorBlend = eased * eased * strength * 0.25;
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
case "magnify":
|
|
325
|
+
scale = 1 + eased * strength * 2.5;
|
|
326
|
+
glow = eased * strength * 0.15;
|
|
327
|
+
break;
|
|
328
|
+
case "repel": {
|
|
329
|
+
scale = 1 + eased * strength * 0.3;
|
|
330
|
+
const angle2 = Math.atan2(dy, dx);
|
|
331
|
+
const push = eased * eased * strength * 1.2;
|
|
332
|
+
offsetX = Math.cos(angle2) * push * cellW;
|
|
333
|
+
offsetY = Math.sin(angle2) * push * cellH;
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
case "glow":
|
|
337
|
+
glow = eased * strength * 0.8;
|
|
338
|
+
colorBlend = eased * strength * 0.4;
|
|
339
|
+
break;
|
|
340
|
+
case "colorShift":
|
|
341
|
+
scale = 1 + eased * strength * 0.4;
|
|
342
|
+
glow = eased * strength * 0.2;
|
|
343
|
+
colorBlend = eased * strength * 0.7;
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
_hoverResult.scale = scale;
|
|
347
|
+
_hoverResult.offsetX = offsetX;
|
|
348
|
+
_hoverResult.offsetY = offsetY;
|
|
349
|
+
_hoverResult.glow = glow;
|
|
350
|
+
_hoverResult.colorBlend = colorBlend;
|
|
351
|
+
_hoverResult.proximity = eased;
|
|
352
|
+
return _hoverResult;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// src/backgrounds/_shared.ts
|
|
356
|
+
function parseColor(c) {
|
|
357
|
+
const hex = c.match(/^#([0-9a-f]{3,8})$/i)?.[1];
|
|
358
|
+
if (hex) {
|
|
359
|
+
const h = hex.length <= 4 ? hex.split("").map((x) => parseInt(x + x, 16)) : [parseInt(hex.slice(0, 2), 16), parseInt(hex.slice(2, 4), 16), parseInt(hex.slice(4, 6), 16)];
|
|
360
|
+
return { r: h[0], g: h[1], b: h[2] };
|
|
361
|
+
}
|
|
362
|
+
const rgb = c.match(/rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)/i);
|
|
363
|
+
if (rgb) return { r: +rgb[1], g: +rgb[2], b: +rgb[3] };
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
function fade(t) {
|
|
367
|
+
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
368
|
+
}
|
|
369
|
+
function lerp(a, b, t) {
|
|
370
|
+
return a + (b - a) * t;
|
|
371
|
+
}
|
|
372
|
+
function hash2(ix, iy) {
|
|
373
|
+
let n = ix * 127 + iy * 311;
|
|
374
|
+
n = n >> 13 ^ n;
|
|
375
|
+
return (n * (n * n * 15731 + 789221) + 1376312589 & 2147483647) / 1073741823 - 1;
|
|
376
|
+
}
|
|
377
|
+
function vnoise(x, y) {
|
|
378
|
+
const ix = Math.floor(x), iy = Math.floor(y);
|
|
379
|
+
const fx = x - ix, fy = y - iy;
|
|
380
|
+
const ux = fade(fx), uy = fade(fy);
|
|
381
|
+
const v00 = hash2(ix, iy);
|
|
382
|
+
const v10 = hash2(ix + 1, iy);
|
|
383
|
+
const v01 = hash2(ix, iy + 1);
|
|
384
|
+
const v11 = hash2(ix + 1, iy + 1);
|
|
385
|
+
return lerp(lerp(v00, v10, ux), lerp(v01, v11, ux), uy);
|
|
386
|
+
}
|
|
387
|
+
function fbm(x, y) {
|
|
388
|
+
return (vnoise(x, y) * 0.5 + vnoise(x * 2.1, y * 2.1) * 0.25 + vnoise(x * 4.3, y * 4.3) * 0.125) / 0.875;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// src/backgrounds/wave.ts
|
|
392
|
+
function renderWaveBackground(ctx, width, height, time, mousePos = { x: 0.5, y: 0.5 }, options = {}) {
|
|
393
|
+
const {
|
|
394
|
+
fontSize = 13,
|
|
395
|
+
charAspect = 0.62,
|
|
396
|
+
lineHeightRatio = 1.4,
|
|
397
|
+
chars = " .:-=+*#%@",
|
|
398
|
+
baseColor = null,
|
|
399
|
+
accentColor = "#d4ff00",
|
|
400
|
+
accentThreshold = 0.52,
|
|
401
|
+
mouseInfluence = 0.55,
|
|
402
|
+
mouseFalloff = 2.8,
|
|
403
|
+
speed = 1,
|
|
404
|
+
vortex = true,
|
|
405
|
+
sparkles = true,
|
|
406
|
+
breathe = true,
|
|
407
|
+
lightMode = false
|
|
408
|
+
} = options;
|
|
409
|
+
const charW = fontSize * charAspect;
|
|
410
|
+
const lineH = fontSize * lineHeightRatio;
|
|
411
|
+
const cols = Math.ceil(width / charW);
|
|
412
|
+
const rows = Math.ceil(height / lineH);
|
|
413
|
+
const mx = mousePos.x;
|
|
414
|
+
const my = mousePos.y;
|
|
415
|
+
let acR = 212, acG = 255, acB = 0;
|
|
416
|
+
if (accentColor) {
|
|
417
|
+
const hex = accentColor.replace("#", "");
|
|
418
|
+
if (hex.length === 6) {
|
|
419
|
+
acR = parseInt(hex.slice(0, 2), 16);
|
|
420
|
+
acG = parseInt(hex.slice(2, 4), 16);
|
|
421
|
+
acB = parseInt(hex.slice(4, 6), 16);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
ctx.clearRect(0, 0, width, height);
|
|
425
|
+
ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
|
|
426
|
+
ctx.textBaseline = "top";
|
|
427
|
+
const t = time * speed;
|
|
428
|
+
const breatheAmp = breathe ? (Math.sin(t * 0.22) * 0.5 + 0.5) * 0.12 : 0;
|
|
429
|
+
for (let row = 0; row < rows; row++) {
|
|
430
|
+
for (let col = 0; col < cols; col++) {
|
|
431
|
+
const nx = col / cols;
|
|
432
|
+
const ny = row / rows;
|
|
433
|
+
const w1 = Math.sin(col * 0.08 + row * 0.05 + t * 0.6) * 0.5 + 0.5;
|
|
434
|
+
const w2 = Math.sin(col * 0.03 - row * 0.07 + t * 0.4) * 0.5 + 0.5;
|
|
435
|
+
const w3 = Math.sin(col * 0.05 + row * 0.03 + t * 0.8) * 0.5 + 0.5;
|
|
436
|
+
const sinePart = (w1 + w2 + w3) / 3;
|
|
437
|
+
const noiseScale = 0.045;
|
|
438
|
+
const noiseShift = t * 0.08;
|
|
439
|
+
const noisePart = fbm(col * noiseScale + noiseShift, row * noiseScale * 1.4 - noiseShift * 0.7) * 0.5 + 0.5;
|
|
440
|
+
const driftFreq = 0.06;
|
|
441
|
+
const driftPart = Math.sin((col + row * 0.65) * driftFreq + t * 1.1) * 0.5 + 0.5;
|
|
442
|
+
const wavePart = sinePart * 0.45 + noisePart * 0.35 + driftPart * 0.2 + breatheAmp;
|
|
443
|
+
const dxRaw = nx - mx;
|
|
444
|
+
const dyRaw = ny - my;
|
|
445
|
+
const distRaw = Math.sqrt(dxRaw * dxRaw + dyRaw * dyRaw);
|
|
446
|
+
let vortexBump = 0;
|
|
447
|
+
if (vortex && distRaw < 0.35) {
|
|
448
|
+
const angle = Math.atan2(dyRaw, dxRaw);
|
|
449
|
+
const swirl = Math.sin(angle * 4 + t * 2.2 - distRaw * 14);
|
|
450
|
+
const falloff = Math.max(0, 1 - distRaw / 0.35);
|
|
451
|
+
vortexBump = swirl * falloff * falloff * 0.22;
|
|
452
|
+
}
|
|
453
|
+
const proximity = Math.max(0, 1 - distRaw * mouseFalloff);
|
|
454
|
+
const intensity = wavePart * (1 - mouseInfluence) + (proximity + vortexBump * 0.5) * mouseInfluence + vortexBump * 0.15;
|
|
455
|
+
const clamped = Math.min(1, Math.max(0, intensity));
|
|
456
|
+
let finalIntensity = clamped;
|
|
457
|
+
if (sparkles && clamped > 0.72) {
|
|
458
|
+
const bucket = Math.floor(t * 8);
|
|
459
|
+
const sparkleSeed = hash2(col * 7 + bucket * 3, row * 11 + bucket);
|
|
460
|
+
if (sparkleSeed > 0.88) {
|
|
461
|
+
finalIntensity = Math.min(1, clamped + (sparkleSeed - 0.88) * 4);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
const charIdx = Math.floor(finalIntensity * (chars.length - 1));
|
|
465
|
+
if (chars[charIdx] === " ") continue;
|
|
466
|
+
const alpha = 0.015 + finalIntensity * 0.07;
|
|
467
|
+
const isAccent = finalIntensity > accentThreshold;
|
|
468
|
+
if (isAccent) {
|
|
469
|
+
const accentAlpha = Math.min(lightMode ? 0.4 : 0.28, alpha * (lightMode ? 4 : 2.8));
|
|
470
|
+
ctx.fillStyle = `rgba(${acR},${acG},${acB},${accentAlpha})`;
|
|
471
|
+
} else if (baseColor) {
|
|
472
|
+
ctx.fillStyle = baseColor.replace("{a}", String(alpha));
|
|
473
|
+
} else if (lightMode) {
|
|
474
|
+
ctx.fillStyle = `rgba(0,0,0,${alpha * 1.3})`;
|
|
475
|
+
} else {
|
|
476
|
+
ctx.fillStyle = `rgba(255,255,255,${alpha})`;
|
|
477
|
+
}
|
|
478
|
+
ctx.fillText(chars[charIdx], col * charW, row * lineH);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// src/core/renderer.ts
|
|
283
484
|
function imageToAsciiFrame(source, options, targetWidth, targetHeight) {
|
|
284
485
|
const srcWidth = source instanceof HTMLVideoElement ? source.videoWidth : source.width;
|
|
285
486
|
const srcHeight = source instanceof HTMLVideoElement ? source.videoHeight : source.height;
|
|
@@ -400,80 +601,10 @@ async function gifToAsciiFrames(buffer, options, targetWidth, targetHeight, onPr
|
|
|
400
601
|
}
|
|
401
602
|
return { frames, cols, rows, fps };
|
|
402
603
|
}
|
|
403
|
-
function smoothstep(t) {
|
|
404
|
-
return t * t * (3 - 2 * t);
|
|
405
|
-
}
|
|
406
|
-
var _hoverResult = { scale: 1, offsetX: 0, offsetY: 0, glow: 0, colorBlend: 0, proximity: 0 };
|
|
407
|
-
function computeHoverEffect(nx, ny, hoverX, hoverY, hoverIntensity, strength, cellW, cellH, effect = "spotlight", radiusFactor = 0.5) {
|
|
408
|
-
const dx = nx - hoverX;
|
|
409
|
-
const dy = ny - hoverY;
|
|
410
|
-
const distSq = dx * dx + dy * dy;
|
|
411
|
-
const radius = 0.08 + radiusFactor * 0.35 + strength * 0.04;
|
|
412
|
-
if (distSq >= radius * radius) {
|
|
413
|
-
_hoverResult.scale = 1;
|
|
414
|
-
_hoverResult.offsetX = 0;
|
|
415
|
-
_hoverResult.offsetY = 0;
|
|
416
|
-
_hoverResult.glow = 0;
|
|
417
|
-
_hoverResult.colorBlend = 0;
|
|
418
|
-
_hoverResult.proximity = 0;
|
|
419
|
-
return _hoverResult;
|
|
420
|
-
}
|
|
421
|
-
const dist = Math.sqrt(distSq);
|
|
422
|
-
const t = 1 - dist / radius;
|
|
423
|
-
const eased = smoothstep(t) * hoverIntensity;
|
|
424
|
-
let scale = 1;
|
|
425
|
-
let offsetX = 0;
|
|
426
|
-
let offsetY = 0;
|
|
427
|
-
let glow = 0;
|
|
428
|
-
let colorBlend = 0;
|
|
429
|
-
switch (effect) {
|
|
430
|
-
case "spotlight": {
|
|
431
|
-
scale = 1 + eased * strength * 1.8;
|
|
432
|
-
const angle = Math.atan2(dy, dx);
|
|
433
|
-
const pushForce = eased * eased * strength * 0.6;
|
|
434
|
-
offsetX = Math.cos(angle) * pushForce * cellW;
|
|
435
|
-
offsetY = Math.sin(angle) * pushForce * cellH;
|
|
436
|
-
glow = eased * strength * 0.4;
|
|
437
|
-
colorBlend = eased * eased * strength * 0.25;
|
|
438
|
-
break;
|
|
439
|
-
}
|
|
440
|
-
case "magnify":
|
|
441
|
-
scale = 1 + eased * strength * 2.5;
|
|
442
|
-
glow = eased * strength * 0.15;
|
|
443
|
-
break;
|
|
444
|
-
case "repel": {
|
|
445
|
-
scale = 1 + eased * strength * 0.3;
|
|
446
|
-
const angle2 = Math.atan2(dy, dx);
|
|
447
|
-
const push = eased * eased * strength * 1.2;
|
|
448
|
-
offsetX = Math.cos(angle2) * push * cellW;
|
|
449
|
-
offsetY = Math.sin(angle2) * push * cellH;
|
|
450
|
-
break;
|
|
451
|
-
}
|
|
452
|
-
case "glow":
|
|
453
|
-
glow = eased * strength * 0.8;
|
|
454
|
-
colorBlend = eased * strength * 0.4;
|
|
455
|
-
break;
|
|
456
|
-
case "colorShift":
|
|
457
|
-
scale = 1 + eased * strength * 0.4;
|
|
458
|
-
glow = eased * strength * 0.2;
|
|
459
|
-
colorBlend = eased * strength * 0.7;
|
|
460
|
-
break;
|
|
461
|
-
}
|
|
462
|
-
_hoverResult.scale = scale;
|
|
463
|
-
_hoverResult.offsetX = offsetX;
|
|
464
|
-
_hoverResult.offsetY = offsetY;
|
|
465
|
-
_hoverResult.glow = glow;
|
|
466
|
-
_hoverResult.colorBlend = colorBlend;
|
|
467
|
-
_hoverResult.proximity = eased;
|
|
468
|
-
return _hoverResult;
|
|
469
|
-
}
|
|
470
604
|
function renderFrameToCanvas(ctx, frame, options, canvasWidth, canvasHeight, time = 0, hoverPos) {
|
|
471
605
|
if (options.animationStyle === "waveField") {
|
|
472
606
|
const mouseNorm = hoverPos ? { x: hoverPos.x, y: hoverPos.y } : { x: 0.5, y: 0.5 };
|
|
473
607
|
const acHexWF = (options.accentColor || "#d4ff00").replace("#", "");
|
|
474
|
-
parseInt(acHexWF.substring(0, 2), 16) || 212;
|
|
475
|
-
parseInt(acHexWF.substring(2, 4), 16) || 255;
|
|
476
|
-
parseInt(acHexWF.substring(4, 6), 16) || 0;
|
|
477
608
|
renderWaveBackground(ctx, canvasWidth, canvasHeight, time, mouseNorm, {
|
|
478
609
|
accentColor: `#${acHexWF}`,
|
|
479
610
|
accentThreshold: 0.52,
|
|
@@ -717,6 +848,9 @@ function renderFrameToCanvas(ctx, frame, options, canvasWidth, canvasHeight, tim
|
|
|
717
848
|
}
|
|
718
849
|
ctx.globalAlpha = 1;
|
|
719
850
|
}
|
|
851
|
+
|
|
852
|
+
// src/core/embed-gen.ts
|
|
853
|
+
var EMBED_CDN_VERSION = "1.0.17";
|
|
720
854
|
function serializeFrame(frame, fullColor) {
|
|
721
855
|
const rows = frame.length;
|
|
722
856
|
const cols = rows > 0 ? frame[0].length : 0;
|
|
@@ -740,9 +874,63 @@ function serializeFrame(frame, fullColor) {
|
|
|
740
874
|
for (let j = 0; j < buf.length; j++) binary += String.fromCharCode(buf[j]);
|
|
741
875
|
return btoa(binary);
|
|
742
876
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
877
|
+
function buildEmbedOpts(options, rows, cols, width, height, fps, animated) {
|
|
878
|
+
const o = {
|
|
879
|
+
r: rows,
|
|
880
|
+
c: cols,
|
|
881
|
+
w: width,
|
|
882
|
+
h: height,
|
|
883
|
+
cs: options.charset,
|
|
884
|
+
cm: options.colorMode,
|
|
885
|
+
as: options.animationStyle,
|
|
886
|
+
sp: options.animationSpeed,
|
|
887
|
+
inv: options.invert,
|
|
888
|
+
hs: options.hoverStrength,
|
|
889
|
+
hr: options.hoverRadius,
|
|
890
|
+
he: options.hoverEffect,
|
|
891
|
+
hc: options.hoverColor,
|
|
892
|
+
dr: options.dotSizeRatio,
|
|
893
|
+
dots: options.renderMode === "dots"
|
|
894
|
+
};
|
|
895
|
+
if (options.colorMode === "accent") o.ac = options.accentColor;
|
|
896
|
+
if (fps !== void 0) o.fps = fps;
|
|
897
|
+
if (animated) o.anim = true;
|
|
898
|
+
return JSON.stringify(o);
|
|
899
|
+
}
|
|
900
|
+
var CDN_SCRIPT = `<script src="https://cdn.jsdelivr.net/npm/asciify-engine@${EMBED_CDN_VERSION}/dist/embed.js" async></script>`;
|
|
901
|
+
function generateEmbedCode(frame, options, width, height) {
|
|
902
|
+
const rows = frame.length;
|
|
903
|
+
if (rows === 0) return "";
|
|
904
|
+
const cols = frame[0].length;
|
|
905
|
+
const isFullColor = options.colorMode === "fullcolor";
|
|
906
|
+
const data = serializeFrame(frame, isFullColor);
|
|
907
|
+
const id = `ar-${Math.random().toString(36).slice(2, 9)}`;
|
|
908
|
+
const opts = buildEmbedOpts(options, rows, cols, width, height);
|
|
909
|
+
return `<!-- Asciify Embed -->
|
|
910
|
+
<canvas id="${id}" data-asciify-opts='${opts}' width="${width}" height="${height}"></canvas>
|
|
911
|
+
<script type="application/json" id="${id}-d">"${data}"</script>
|
|
912
|
+
${CDN_SCRIPT}
|
|
913
|
+
<!-- /Asciify Embed -->`;
|
|
914
|
+
}
|
|
915
|
+
function generateAnimatedEmbedCode(frames, options, fps, width, height) {
|
|
916
|
+
if (frames.length === 0) return "";
|
|
917
|
+
const rows = frames[0].length;
|
|
918
|
+
const cols = frames[0][0].length;
|
|
919
|
+
const isFullColor = options.colorMode === "fullcolor";
|
|
920
|
+
const allData = frames.map((f) => serializeFrame(f, isFullColor));
|
|
921
|
+
const id = `ar-${Math.random().toString(36).slice(2, 9)}`;
|
|
922
|
+
const opts = buildEmbedOpts(options, rows, cols, width, height, fps, true);
|
|
923
|
+
return `<!-- Asciify Animated Embed -->
|
|
924
|
+
<canvas id="${id}" data-asciify-opts='${opts}' width="${width}" height="${height}"></canvas>
|
|
925
|
+
<script type="application/json" id="${id}-d">${JSON.stringify(allData)}</script>
|
|
926
|
+
${CDN_SCRIPT}
|
|
927
|
+
<!-- /Asciify Animated Embed -->`;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// src/core/simple-api.ts
|
|
931
|
+
async function asciify(source, canvas, { fontSize = 10, style = "classic", options = {} } = {}) {
|
|
932
|
+
let el;
|
|
933
|
+
if (typeof source === "string") {
|
|
746
934
|
const img = new Image();
|
|
747
935
|
img.crossOrigin = "anonymous";
|
|
748
936
|
await new Promise((resolve, reject) => {
|
|
@@ -830,173 +1018,8 @@ async function asciifyVideo(source, canvas, { fontSize = 10, style = "classic",
|
|
|
830
1018
|
cancelAnimationFrame(animId);
|
|
831
1019
|
};
|
|
832
1020
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
const o = {
|
|
836
|
-
r: rows,
|
|
837
|
-
c: cols,
|
|
838
|
-
w: width,
|
|
839
|
-
h: height,
|
|
840
|
-
cs: options.charset,
|
|
841
|
-
cm: options.colorMode,
|
|
842
|
-
as: options.animationStyle,
|
|
843
|
-
sp: options.animationSpeed,
|
|
844
|
-
inv: options.invert,
|
|
845
|
-
hs: options.hoverStrength,
|
|
846
|
-
hr: options.hoverRadius,
|
|
847
|
-
he: options.hoverEffect,
|
|
848
|
-
hc: options.hoverColor,
|
|
849
|
-
dr: options.dotSizeRatio,
|
|
850
|
-
dots: options.renderMode === "dots"
|
|
851
|
-
};
|
|
852
|
-
if (options.colorMode === "accent") o.ac = options.accentColor;
|
|
853
|
-
if (fps !== void 0) o.fps = fps;
|
|
854
|
-
if (animated) o.anim = true;
|
|
855
|
-
return JSON.stringify(o);
|
|
856
|
-
}
|
|
857
|
-
var CDN_SCRIPT = `<script src="https://cdn.jsdelivr.net/npm/asciify-engine@${EMBED_CDN_VERSION}/dist/embed.js" async></script>`;
|
|
858
|
-
function generateEmbedCode(frame, options, width, height) {
|
|
859
|
-
const rows = frame.length;
|
|
860
|
-
if (rows === 0) return "";
|
|
861
|
-
const cols = frame[0].length;
|
|
862
|
-
const isFullColor = options.colorMode === "fullcolor";
|
|
863
|
-
const data = serializeFrame(frame, isFullColor);
|
|
864
|
-
const id = `ar-${Math.random().toString(36).slice(2, 9)}`;
|
|
865
|
-
const opts = buildEmbedOpts(options, rows, cols, width, height);
|
|
866
|
-
return `<!-- Asciify Embed -->
|
|
867
|
-
<canvas id="${id}" data-asciify-opts='${opts}' width="${width}" height="${height}"></canvas>
|
|
868
|
-
<script type="application/json" id="${id}-d">"${data}"</script>
|
|
869
|
-
${CDN_SCRIPT}
|
|
870
|
-
<!-- /Asciify Embed -->`;
|
|
871
|
-
}
|
|
872
|
-
function generateAnimatedEmbedCode(frames, options, fps, width, height) {
|
|
873
|
-
if (frames.length === 0) return "";
|
|
874
|
-
const rows = frames[0].length;
|
|
875
|
-
const cols = frames[0][0].length;
|
|
876
|
-
const isFullColor = options.colorMode === "fullcolor";
|
|
877
|
-
const allData = frames.map((f) => serializeFrame(f, isFullColor));
|
|
878
|
-
const id = `ar-${Math.random().toString(36).slice(2, 9)}`;
|
|
879
|
-
const opts = buildEmbedOpts(options, rows, cols, width, height, fps, true);
|
|
880
|
-
return `<!-- Asciify Animated Embed -->
|
|
881
|
-
<canvas id="${id}" data-asciify-opts='${opts}' width="${width}" height="${height}"></canvas>
|
|
882
|
-
<script type="application/json" id="${id}-d">${JSON.stringify(allData)}</script>
|
|
883
|
-
${CDN_SCRIPT}
|
|
884
|
-
<!-- /Asciify Animated Embed -->`;
|
|
885
|
-
}
|
|
886
|
-
function _fade(t) {
|
|
887
|
-
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
888
|
-
}
|
|
889
|
-
function _lerp(a, b, t) {
|
|
890
|
-
return a + (b - a) * t;
|
|
891
|
-
}
|
|
892
|
-
function _hash2(ix, iy) {
|
|
893
|
-
let n = ix * 127 + iy * 311;
|
|
894
|
-
n = n >> 13 ^ n;
|
|
895
|
-
return (n * (n * n * 15731 + 789221) + 1376312589 & 2147483647) / 1073741823 - 1;
|
|
896
|
-
}
|
|
897
|
-
function _vnoise(x, y) {
|
|
898
|
-
const ix = Math.floor(x), iy = Math.floor(y);
|
|
899
|
-
const fx = x - ix, fy = y - iy;
|
|
900
|
-
const ux = _fade(fx), uy = _fade(fy);
|
|
901
|
-
const v00 = _hash2(ix, iy);
|
|
902
|
-
const v10 = _hash2(ix + 1, iy);
|
|
903
|
-
const v01 = _hash2(ix, iy + 1);
|
|
904
|
-
const v11 = _hash2(ix + 1, iy + 1);
|
|
905
|
-
return _lerp(_lerp(v00, v10, ux), _lerp(v01, v11, ux), uy);
|
|
906
|
-
}
|
|
907
|
-
function _fbm(x, y) {
|
|
908
|
-
return (_vnoise(x, y) * 0.5 + _vnoise(x * 2.1, y * 2.1) * 0.25 + _vnoise(x * 4.3, y * 4.3) * 0.125) / 0.875;
|
|
909
|
-
}
|
|
910
|
-
function renderWaveBackground(ctx, width, height, time, mousePos = { x: 0.5, y: 0.5 }, options = {}) {
|
|
911
|
-
const {
|
|
912
|
-
fontSize = 13,
|
|
913
|
-
charAspect = 0.62,
|
|
914
|
-
lineHeightRatio = 1.4,
|
|
915
|
-
chars = " .:-=+*#%@",
|
|
916
|
-
baseColor = null,
|
|
917
|
-
accentColor = "#d4ff00",
|
|
918
|
-
accentThreshold = 0.52,
|
|
919
|
-
mouseInfluence = 0.55,
|
|
920
|
-
mouseFalloff = 2.8,
|
|
921
|
-
speed = 1,
|
|
922
|
-
vortex = true,
|
|
923
|
-
sparkles = true,
|
|
924
|
-
breathe = true,
|
|
925
|
-
lightMode = false
|
|
926
|
-
} = options;
|
|
927
|
-
const charW = fontSize * charAspect;
|
|
928
|
-
const lineH = fontSize * lineHeightRatio;
|
|
929
|
-
const cols = Math.ceil(width / charW);
|
|
930
|
-
const rows = Math.ceil(height / lineH);
|
|
931
|
-
const mx = mousePos.x;
|
|
932
|
-
const my = mousePos.y;
|
|
933
|
-
let acR = 212, acG = 255, acB = 0;
|
|
934
|
-
if (accentColor) {
|
|
935
|
-
const hex = accentColor.replace("#", "");
|
|
936
|
-
if (hex.length === 6) {
|
|
937
|
-
acR = parseInt(hex.slice(0, 2), 16);
|
|
938
|
-
acG = parseInt(hex.slice(2, 4), 16);
|
|
939
|
-
acB = parseInt(hex.slice(4, 6), 16);
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
ctx.clearRect(0, 0, width, height);
|
|
943
|
-
ctx.font = `${fontSize}px "JetBrains Mono", monospace`;
|
|
944
|
-
ctx.textBaseline = "top";
|
|
945
|
-
const t = time * speed;
|
|
946
|
-
const breatheAmp = breathe ? (Math.sin(t * 0.22) * 0.5 + 0.5) * 0.12 : 0;
|
|
947
|
-
for (let row = 0; row < rows; row++) {
|
|
948
|
-
for (let col = 0; col < cols; col++) {
|
|
949
|
-
const nx = col / cols;
|
|
950
|
-
const ny = row / rows;
|
|
951
|
-
const w1 = Math.sin(col * 0.08 + row * 0.05 + t * 0.6) * 0.5 + 0.5;
|
|
952
|
-
const w2 = Math.sin(col * 0.03 - row * 0.07 + t * 0.4) * 0.5 + 0.5;
|
|
953
|
-
const w3 = Math.sin(col * 0.05 + row * 0.03 + t * 0.8) * 0.5 + 0.5;
|
|
954
|
-
const sinePart = (w1 + w2 + w3) / 3;
|
|
955
|
-
const noiseScale = 0.045;
|
|
956
|
-
const noiseShift = t * 0.08;
|
|
957
|
-
const noisePart = _fbm(col * noiseScale + noiseShift, row * noiseScale * 1.4 - noiseShift * 0.7) * 0.5 + 0.5;
|
|
958
|
-
const driftFreq = 0.06;
|
|
959
|
-
const driftPart = Math.sin((col + row * 0.65) * driftFreq + t * 1.1) * 0.5 + 0.5;
|
|
960
|
-
const wavePart = sinePart * 0.45 + noisePart * 0.35 + driftPart * 0.2 + breatheAmp;
|
|
961
|
-
const dxRaw = nx - mx;
|
|
962
|
-
const dyRaw = ny - my;
|
|
963
|
-
const distRaw = Math.sqrt(dxRaw * dxRaw + dyRaw * dyRaw);
|
|
964
|
-
let vortexBump = 0;
|
|
965
|
-
if (vortex && distRaw < 0.35) {
|
|
966
|
-
const angle = Math.atan2(dyRaw, dxRaw);
|
|
967
|
-
const swirl = Math.sin(angle * 4 + t * 2.2 - distRaw * 14);
|
|
968
|
-
const falloff = Math.max(0, 1 - distRaw / 0.35);
|
|
969
|
-
vortexBump = swirl * falloff * falloff * 0.22;
|
|
970
|
-
}
|
|
971
|
-
const proximity = Math.max(0, 1 - distRaw * mouseFalloff);
|
|
972
|
-
const intensity = wavePart * (1 - mouseInfluence) + (proximity + vortexBump * 0.5) * mouseInfluence + vortexBump * 0.15;
|
|
973
|
-
const clamped = Math.min(1, Math.max(0, intensity));
|
|
974
|
-
let finalIntensity = clamped;
|
|
975
|
-
if (sparkles && clamped > 0.72) {
|
|
976
|
-
const bucket = Math.floor(t * 8);
|
|
977
|
-
const sparkleSeed = _hash2(col * 7 + bucket * 3, row * 11 + bucket);
|
|
978
|
-
if (sparkleSeed > 0.88) {
|
|
979
|
-
finalIntensity = Math.min(1, clamped + (sparkleSeed - 0.88) * 4);
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
const charIdx = Math.floor(finalIntensity * (chars.length - 1));
|
|
983
|
-
if (chars[charIdx] === " ") continue;
|
|
984
|
-
const alpha = 0.015 + finalIntensity * 0.07;
|
|
985
|
-
const isAccent = finalIntensity > accentThreshold;
|
|
986
|
-
if (isAccent) {
|
|
987
|
-
const accentAlpha = Math.min(lightMode ? 0.4 : 0.28, alpha * (lightMode ? 4 : 2.8));
|
|
988
|
-
ctx.fillStyle = `rgba(${acR},${acG},${acB},${accentAlpha})`;
|
|
989
|
-
} else if (baseColor) {
|
|
990
|
-
ctx.fillStyle = baseColor.replace("{a}", String(alpha));
|
|
991
|
-
} else if (lightMode) {
|
|
992
|
-
ctx.fillStyle = `rgba(0,0,0,${alpha * 1.3})`;
|
|
993
|
-
} else {
|
|
994
|
-
ctx.fillStyle = `rgba(255,255,255,${alpha})`;
|
|
995
|
-
}
|
|
996
|
-
ctx.fillText(chars[charIdx], col * charW, row * lineH);
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1021
|
+
|
|
1022
|
+
// src/backgrounds/rain.ts
|
|
1000
1023
|
function renderRainBackground(ctx, width, height, time, options = {}) {
|
|
1001
1024
|
const {
|
|
1002
1025
|
fontSize = 13,
|
|
@@ -1022,7 +1045,7 @@ function renderRainBackground(ctx, width, height, time, options = {}) {
|
|
|
1022
1045
|
bb = 0;
|
|
1023
1046
|
}
|
|
1024
1047
|
if (color) {
|
|
1025
|
-
const p =
|
|
1048
|
+
const p = parseColor(color);
|
|
1026
1049
|
if (p) {
|
|
1027
1050
|
br = p.r;
|
|
1028
1051
|
bg = p.g;
|
|
@@ -1030,7 +1053,7 @@ function renderRainBackground(ctx, width, height, time, options = {}) {
|
|
|
1030
1053
|
}
|
|
1031
1054
|
}
|
|
1032
1055
|
let acR = 212, acG = 255, acB = 0;
|
|
1033
|
-
const ap =
|
|
1056
|
+
const ap = parseColor(accentColor);
|
|
1034
1057
|
if (ap) {
|
|
1035
1058
|
acR = ap.r;
|
|
1036
1059
|
acG = ap.g;
|
|
@@ -1038,28 +1061,30 @@ function renderRainBackground(ctx, width, height, time, options = {}) {
|
|
|
1038
1061
|
}
|
|
1039
1062
|
const period = rows + tailLength;
|
|
1040
1063
|
for (let c = 0; c < cols; c++) {
|
|
1041
|
-
if (
|
|
1042
|
-
const colSpeed = (0.5 +
|
|
1043
|
-
const phase =
|
|
1064
|
+
if (hash2(c * 17, 3) > density) continue;
|
|
1065
|
+
const colSpeed = (0.5 + hash2(c * 31, 7) * 1.5) * speed;
|
|
1066
|
+
const phase = hash2(c * 13, 11) * period;
|
|
1044
1067
|
const headRow = Math.floor((time * colSpeed * 7 + phase) % period);
|
|
1045
1068
|
const x = c * charW;
|
|
1046
1069
|
for (let k = 0; k <= tailLength; k++) {
|
|
1047
1070
|
const row = headRow - (tailLength - k);
|
|
1048
1071
|
if (row < 0 || row >= rows) continue;
|
|
1049
1072
|
const y = row * lineH;
|
|
1050
|
-
const charSeed =
|
|
1073
|
+
const charSeed = hash2(c * 53 + Math.floor(time * 5 + k), row * 7);
|
|
1051
1074
|
const ch = chars[Math.floor(charSeed * chars.length)];
|
|
1052
|
-
const
|
|
1075
|
+
const tRatio = k / tailLength;
|
|
1053
1076
|
if (k === tailLength) {
|
|
1054
1077
|
ctx.fillStyle = `rgba(${acR},${acG},${acB},${lightMode ? 0.7 : 0.85})`;
|
|
1055
1078
|
} else {
|
|
1056
|
-
const alpha = lightMode ?
|
|
1079
|
+
const alpha = lightMode ? tRatio * 0.22 : tRatio * 0.15;
|
|
1057
1080
|
ctx.fillStyle = `rgba(${br},${bg},${bb},${alpha})`;
|
|
1058
1081
|
}
|
|
1059
1082
|
ctx.fillText(ch, x, y);
|
|
1060
1083
|
}
|
|
1061
1084
|
}
|
|
1062
1085
|
}
|
|
1086
|
+
|
|
1087
|
+
// src/backgrounds/stars.ts
|
|
1063
1088
|
function renderStarsBackground(ctx, width, height, time, mousePos = { x: 0.5, y: 0.5 }, options = {}) {
|
|
1064
1089
|
const {
|
|
1065
1090
|
fontSize = 14,
|
|
@@ -1083,7 +1108,7 @@ function renderStarsBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1083
1108
|
bb = 0;
|
|
1084
1109
|
}
|
|
1085
1110
|
if (color) {
|
|
1086
|
-
const p =
|
|
1111
|
+
const p = parseColor(color);
|
|
1087
1112
|
if (p) {
|
|
1088
1113
|
br = p.r;
|
|
1089
1114
|
bg = p.g;
|
|
@@ -1091,7 +1116,7 @@ function renderStarsBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1091
1116
|
}
|
|
1092
1117
|
}
|
|
1093
1118
|
let acR = 212, acG = 255, acB = 0;
|
|
1094
|
-
const ap =
|
|
1119
|
+
const ap = parseColor(accentColor);
|
|
1095
1120
|
if (ap) {
|
|
1096
1121
|
acR = ap.r;
|
|
1097
1122
|
acG = ap.g;
|
|
@@ -1100,9 +1125,9 @@ function renderStarsBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1100
1125
|
const charArr = chars.replace(/ /g, "").split("");
|
|
1101
1126
|
if (charArr.length === 0) return;
|
|
1102
1127
|
for (let i = 0; i < count; i++) {
|
|
1103
|
-
const angle =
|
|
1104
|
-
const baseSpd = 0.15 +
|
|
1105
|
-
const phase =
|
|
1128
|
+
const angle = hash2(i * 17, 3) * Math.PI * 2;
|
|
1129
|
+
const baseSpd = 0.15 + hash2(i * 31, 7) * 0.85;
|
|
1130
|
+
const phase = hash2(i * 13, 11);
|
|
1106
1131
|
const r = (time * baseSpd * speed * 0.22 + phase) % 1;
|
|
1107
1132
|
const x = cx + Math.cos(angle) * r * maxR;
|
|
1108
1133
|
const y = cy + Math.sin(angle) * r * maxR;
|
|
@@ -1118,68 +1143,81 @@ function renderStarsBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1118
1143
|
}
|
|
1119
1144
|
ctx.textAlign = "left";
|
|
1120
1145
|
}
|
|
1146
|
+
|
|
1147
|
+
// src/backgrounds/pulse.ts
|
|
1121
1148
|
function renderPulseBackground(ctx, width, height, time, mousePos = { x: 0.5, y: 0.5 }, options = {}) {
|
|
1122
1149
|
const {
|
|
1123
|
-
fontSize =
|
|
1124
|
-
chars = "
|
|
1125
|
-
accentColor = "#
|
|
1150
|
+
fontSize = 14,
|
|
1151
|
+
chars = ". \xB7 \u25CB \u25CE \u25CF",
|
|
1152
|
+
accentColor = "#00ffcc",
|
|
1126
1153
|
color,
|
|
1127
|
-
rings =
|
|
1154
|
+
rings = 5,
|
|
1128
1155
|
speed = 1,
|
|
1129
1156
|
sharpness = 4,
|
|
1130
1157
|
lightMode = false
|
|
1131
1158
|
} = options;
|
|
1132
|
-
const charW = fontSize * 0.62;
|
|
1133
|
-
const lineH = fontSize * 1.4;
|
|
1134
|
-
const cols = Math.ceil(width / charW);
|
|
1135
|
-
const rows = Math.ceil(height / lineH);
|
|
1136
1159
|
ctx.clearRect(0, 0, width, height);
|
|
1137
|
-
ctx.
|
|
1138
|
-
ctx.
|
|
1139
|
-
const
|
|
1140
|
-
const
|
|
1141
|
-
const
|
|
1142
|
-
let br = 255,
|
|
1160
|
+
ctx.textBaseline = "middle";
|
|
1161
|
+
ctx.textAlign = "center";
|
|
1162
|
+
const cx = width * mousePos.x;
|
|
1163
|
+
const cy = height * mousePos.y;
|
|
1164
|
+
const maxDist = Math.sqrt(cx * cx + cy * cy) * 1.6 + Math.sqrt(width * width + height * height) * 0.2;
|
|
1165
|
+
let br = 255, bg = 255, bb = 255;
|
|
1143
1166
|
if (lightMode) {
|
|
1144
1167
|
br = 0;
|
|
1145
|
-
|
|
1168
|
+
bg = 0;
|
|
1146
1169
|
bb = 0;
|
|
1147
1170
|
}
|
|
1148
1171
|
if (color) {
|
|
1149
|
-
const p =
|
|
1172
|
+
const p = parseColor(color);
|
|
1150
1173
|
if (p) {
|
|
1151
1174
|
br = p.r;
|
|
1152
|
-
|
|
1175
|
+
bg = p.g;
|
|
1153
1176
|
bb = p.b;
|
|
1154
1177
|
}
|
|
1155
1178
|
}
|
|
1156
|
-
let acR =
|
|
1157
|
-
const ap =
|
|
1179
|
+
let acR = 0, acG = 255, acB = 204;
|
|
1180
|
+
const ap = parseColor(accentColor);
|
|
1158
1181
|
if (ap) {
|
|
1159
1182
|
acR = ap.r;
|
|
1160
1183
|
acG = ap.g;
|
|
1161
1184
|
acB = ap.b;
|
|
1162
1185
|
}
|
|
1163
|
-
const
|
|
1186
|
+
const charArr = chars.replace(/ /g, "").split("");
|
|
1187
|
+
if (charArr.length === 0) return;
|
|
1188
|
+
const cols = Math.ceil(width / fontSize);
|
|
1189
|
+
const rows = Math.ceil(height / fontSize);
|
|
1164
1190
|
for (let row = 0; row < rows; row++) {
|
|
1165
1191
|
for (let col = 0; col < cols; col++) {
|
|
1166
|
-
const px = col *
|
|
1167
|
-
const py = row *
|
|
1168
|
-
const
|
|
1169
|
-
const
|
|
1170
|
-
const
|
|
1171
|
-
const
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1192
|
+
const px = col * fontSize + fontSize * 0.5;
|
|
1193
|
+
const py = row * fontSize + fontSize * 0.5;
|
|
1194
|
+
const dx = px - cx;
|
|
1195
|
+
const dy = py - cy;
|
|
1196
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
1197
|
+
const norm = dist / maxDist;
|
|
1198
|
+
let totalIntensity = 0;
|
|
1199
|
+
for (let r = 0; r < rings; r++) {
|
|
1200
|
+
const phase = r / rings;
|
|
1201
|
+
const t = (time * speed * 0.38 + phase) % 1;
|
|
1202
|
+
const ringDist = Math.abs(norm - t);
|
|
1203
|
+
const ringNorm = Math.max(0, 1 - ringDist * maxDist / (fontSize * (12 - sharpness)));
|
|
1204
|
+
totalIntensity += Math.cos(ringNorm * Math.PI * 0.5) * ringNorm;
|
|
1205
|
+
}
|
|
1206
|
+
totalIntensity = Math.min(1, totalIntensity);
|
|
1207
|
+
if (totalIntensity < 0.02) continue;
|
|
1208
|
+
const isAccent = totalIntensity > 0.6;
|
|
1209
|
+
ctx.font = `${fontSize}px monospace`;
|
|
1210
|
+
const charIdx = Math.floor(totalIntensity * (charArr.length - 1));
|
|
1211
|
+
const ch = charArr[Math.min(charIdx, charArr.length - 1)];
|
|
1212
|
+
const alpha = lightMode ? totalIntensity * 0.32 : totalIntensity * 0.22;
|
|
1213
|
+
ctx.fillStyle = isAccent ? `rgba(${acR},${acG},${acB},${Math.min(lightMode ? 0.5 : 0.4, totalIntensity * 0.55)})` : `rgba(${br},${bg},${bb},${alpha})`;
|
|
1214
|
+
ctx.fillText(ch, px, py);
|
|
1180
1215
|
}
|
|
1181
1216
|
}
|
|
1217
|
+
ctx.textAlign = "left";
|
|
1182
1218
|
}
|
|
1219
|
+
|
|
1220
|
+
// src/backgrounds/noise.ts
|
|
1183
1221
|
function renderNoiseBackground(ctx, width, height, time, mousePos = { x: 0.5, y: 0.5 }, options = {}) {
|
|
1184
1222
|
const {
|
|
1185
1223
|
fontSize = 14,
|
|
@@ -1207,7 +1245,7 @@ function renderNoiseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1207
1245
|
bb = 0;
|
|
1208
1246
|
}
|
|
1209
1247
|
if (color) {
|
|
1210
|
-
const p =
|
|
1248
|
+
const p = parseColor(color);
|
|
1211
1249
|
if (p) {
|
|
1212
1250
|
br = p.r;
|
|
1213
1251
|
bgc = p.g;
|
|
@@ -1215,7 +1253,7 @@ function renderNoiseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1215
1253
|
}
|
|
1216
1254
|
}
|
|
1217
1255
|
let acR = 212, acG = 255, acB = 0;
|
|
1218
|
-
const ap =
|
|
1256
|
+
const ap = parseColor(accentColor);
|
|
1219
1257
|
if (ap) {
|
|
1220
1258
|
acR = ap.r;
|
|
1221
1259
|
acG = ap.g;
|
|
@@ -1227,7 +1265,7 @@ function renderNoiseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1227
1265
|
const fbmN = (x, y) => {
|
|
1228
1266
|
let v = 0, amp = 0.5, freq = 1, norm = 0;
|
|
1229
1267
|
for (let o = 0; o < oct; o++) {
|
|
1230
|
-
v +=
|
|
1268
|
+
v += vnoise(x * freq, y * freq) * amp;
|
|
1231
1269
|
norm += amp;
|
|
1232
1270
|
amp *= 0.5;
|
|
1233
1271
|
freq *= 2.1;
|
|
@@ -1257,6 +1295,8 @@ function renderNoiseBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1257
1295
|
}
|
|
1258
1296
|
}
|
|
1259
1297
|
}
|
|
1298
|
+
|
|
1299
|
+
// src/backgrounds/grid.ts
|
|
1260
1300
|
function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y: 0.5 }, options = {}) {
|
|
1261
1301
|
const {
|
|
1262
1302
|
fontSize = 12,
|
|
@@ -1283,7 +1323,7 @@ function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1283
1323
|
bb = 0;
|
|
1284
1324
|
}
|
|
1285
1325
|
if (color) {
|
|
1286
|
-
const p =
|
|
1326
|
+
const p = parseColor(color);
|
|
1287
1327
|
if (p) {
|
|
1288
1328
|
br = p.r;
|
|
1289
1329
|
bgv = p.g;
|
|
@@ -1291,7 +1331,7 @@ function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1291
1331
|
}
|
|
1292
1332
|
}
|
|
1293
1333
|
let acR = 212, acG = 255, acB = 0;
|
|
1294
|
-
const ap =
|
|
1334
|
+
const ap = parseColor(accentColor);
|
|
1295
1335
|
if (ap) {
|
|
1296
1336
|
acR = ap.r;
|
|
1297
1337
|
acG = ap.g;
|
|
@@ -1303,7 +1343,7 @@ function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1303
1343
|
const ny = row / rows;
|
|
1304
1344
|
const scanPhase = ((ny * bands - t * 0.5) % 1 + 1) % 1;
|
|
1305
1345
|
const bandIntensity = Math.max(0, 1 - scanPhase / bandWidth);
|
|
1306
|
-
const gridSeed =
|
|
1346
|
+
const gridSeed = hash2(col * 3, row * 7);
|
|
1307
1347
|
const gridBase = (gridSeed * 0.5 + 0.5) * 0.35;
|
|
1308
1348
|
let glitchBump = 0;
|
|
1309
1349
|
if (glitch) {
|
|
@@ -1311,7 +1351,7 @@ function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1311
1351
|
const dy = ny - mousePos.y;
|
|
1312
1352
|
const d = Math.sqrt(dx * dx + dy * dy);
|
|
1313
1353
|
if (d < 0.18) {
|
|
1314
|
-
const g =
|
|
1354
|
+
const g = hash2(col * 11 + Math.floor(t * 12), row * 5);
|
|
1315
1355
|
glitchBump = Math.max(0, 1 - d / 0.18) * (g > 0.5 ? g - 0.3 : 0);
|
|
1316
1356
|
}
|
|
1317
1357
|
}
|
|
@@ -1326,6 +1366,8 @@ function renderGridBackground(ctx, width, height, time, mousePos = { x: 0.5, y:
|
|
|
1326
1366
|
}
|
|
1327
1367
|
}
|
|
1328
1368
|
}
|
|
1369
|
+
|
|
1370
|
+
// src/backgrounds/aurora.ts
|
|
1329
1371
|
function renderAuroraBackground(ctx, width, height, time, mousePos = { x: 0.5, y: 0.5 }, options = {}) {
|
|
1330
1372
|
const {
|
|
1331
1373
|
fontSize = 14,
|
|
@@ -1352,7 +1394,7 @@ function renderAuroraBackground(ctx, width, height, time, mousePos = { x: 0.5, y
|
|
|
1352
1394
|
cb = 0;
|
|
1353
1395
|
}
|
|
1354
1396
|
if (color) {
|
|
1355
|
-
const p =
|
|
1397
|
+
const p = parseColor(color);
|
|
1356
1398
|
if (p) {
|
|
1357
1399
|
cr = p.r;
|
|
1358
1400
|
cg = p.g;
|
|
@@ -1360,7 +1402,7 @@ function renderAuroraBackground(ctx, width, height, time, mousePos = { x: 0.5, y
|
|
|
1360
1402
|
}
|
|
1361
1403
|
}
|
|
1362
1404
|
let acR = 212, acG = 255, acB = 0;
|
|
1363
|
-
const ap =
|
|
1405
|
+
const ap = parseColor(accentColor);
|
|
1364
1406
|
if (ap) {
|
|
1365
1407
|
acR = ap.r;
|
|
1366
1408
|
acG = ap.g;
|
|
@@ -1369,19 +1411,14 @@ function renderAuroraBackground(ctx, width, height, time, mousePos = { x: 0.5, y
|
|
|
1369
1411
|
const t = time * speed;
|
|
1370
1412
|
const layerParams = [];
|
|
1371
1413
|
for (let l = 0; l < layers; l++) {
|
|
1372
|
-
const seed =
|
|
1373
|
-
const seed2 =
|
|
1414
|
+
const seed = hash2(l * 17, l * 31 + 7);
|
|
1415
|
+
const seed2 = hash2(l * 23 + 5, l * 11);
|
|
1374
1416
|
layerParams.push({
|
|
1375
1417
|
fx: 0.8 + seed * 2.2,
|
|
1376
|
-
// x spatial frequency
|
|
1377
1418
|
fy: 1.2 + seed2 * 1.8,
|
|
1378
|
-
// y spatial frequency
|
|
1379
1419
|
phase: seed * Math.PI * 4,
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
// drift speed & direction
|
|
1383
|
-
amp: 0.55 + _hash2(l * 29, l * 3) * 0.45
|
|
1384
|
-
// amplitude weight
|
|
1420
|
+
dt: (0.3 + hash2(l * 7, l * 13 + 3) * 0.5) * (l % 2 === 0 ? 1 : -1),
|
|
1421
|
+
amp: 0.55 + hash2(l * 29, l * 3) * 0.45
|
|
1385
1422
|
});
|
|
1386
1423
|
}
|
|
1387
1424
|
for (let row = 0; row < rows; row++) {
|
|
@@ -1415,6 +1452,219 @@ function renderAuroraBackground(ctx, width, height, time, mousePos = { x: 0.5, y
|
|
|
1415
1452
|
}
|
|
1416
1453
|
}
|
|
1417
1454
|
}
|
|
1455
|
+
|
|
1456
|
+
// src/backgrounds/silk.ts
|
|
1457
|
+
function renderSilkBackground(ctx, width, height, time, options = {}) {
|
|
1458
|
+
const {
|
|
1459
|
+
fontSize = 13,
|
|
1460
|
+
color,
|
|
1461
|
+
accentColor = "#d4ff00",
|
|
1462
|
+
speed = 0.4,
|
|
1463
|
+
layers = 4,
|
|
1464
|
+
turbulence = 0.8,
|
|
1465
|
+
lightMode = false
|
|
1466
|
+
} = options;
|
|
1467
|
+
const charW = fontSize * 0.62;
|
|
1468
|
+
const lineH = fontSize * 1.4;
|
|
1469
|
+
const cols = Math.ceil(width / charW);
|
|
1470
|
+
const rows = Math.ceil(height / lineH);
|
|
1471
|
+
ctx.clearRect(0, 0, width, height);
|
|
1472
|
+
ctx.font = `${fontSize}px monospace`;
|
|
1473
|
+
ctx.textBaseline = "top";
|
|
1474
|
+
let cr = 255, cg = 255, cb = 255;
|
|
1475
|
+
if (lightMode) {
|
|
1476
|
+
cr = 0;
|
|
1477
|
+
cg = 0;
|
|
1478
|
+
cb = 0;
|
|
1479
|
+
}
|
|
1480
|
+
if (color) {
|
|
1481
|
+
const p = parseColor(color);
|
|
1482
|
+
if (p) {
|
|
1483
|
+
cr = p.r;
|
|
1484
|
+
cg = p.g;
|
|
1485
|
+
cb = p.b;
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
let acR = 212, acG = 255, acB = 0;
|
|
1489
|
+
const ap = parseColor(accentColor);
|
|
1490
|
+
if (ap) {
|
|
1491
|
+
acR = ap.r;
|
|
1492
|
+
acG = ap.g;
|
|
1493
|
+
acB = ap.b;
|
|
1494
|
+
}
|
|
1495
|
+
const t = time * speed;
|
|
1496
|
+
const dirChars = ["\u2500", "\u2500", "\u254C", "\xB7", "\u254C", "\u2500", "\u2500", "\u254C", "\xB7"];
|
|
1497
|
+
for (let row = 0; row < rows; row++) {
|
|
1498
|
+
const ny = row / rows;
|
|
1499
|
+
for (let col = 0; col < cols; col++) {
|
|
1500
|
+
const nx = col / cols;
|
|
1501
|
+
let angleSum = 0;
|
|
1502
|
+
let intensitySum = 0;
|
|
1503
|
+
for (let l = 0; l < layers; l++) {
|
|
1504
|
+
const ls = hash2(l * 13, l * 7 + 3);
|
|
1505
|
+
const ls2 = hash2(l * 29, l * 11 + 1);
|
|
1506
|
+
const fx = 1.1 + ls * 2.4;
|
|
1507
|
+
const fy = 0.9 + ls2 * 2;
|
|
1508
|
+
const ph = ls * Math.PI * 6;
|
|
1509
|
+
const dr = (0.2 + hash2(l * 41, l * 17) * 0.5) * (l % 2 === 0 ? 1 : -1.3);
|
|
1510
|
+
const u = Math.sin(nx * fx * Math.PI * 2 + t * dr + ph);
|
|
1511
|
+
const v = Math.cos(ny * fy * Math.PI * 2 + t * dr * 0.6 + ph * 1.7);
|
|
1512
|
+
const cross = Math.sin(nx * fy * Math.PI * turbulence + ny * fx * Math.PI * turbulence + t * dr * 0.4);
|
|
1513
|
+
angleSum += Math.atan2(v + cross * 0.3, u);
|
|
1514
|
+
intensitySum += (u * v + 1) * 0.5;
|
|
1515
|
+
}
|
|
1516
|
+
const angle = angleSum / layers;
|
|
1517
|
+
const intensity = Math.min(1, intensitySum / layers);
|
|
1518
|
+
if (intensity < 0.1) continue;
|
|
1519
|
+
const angleNorm = (angle + Math.PI) / (Math.PI * 2);
|
|
1520
|
+
const charIdx = Math.floor(angleNorm * dirChars.length) % dirChars.length;
|
|
1521
|
+
const ch = dirChars[charIdx];
|
|
1522
|
+
const isAccent = intensity > 0.8;
|
|
1523
|
+
const alpha = lightMode ? intensity * 0.16 : intensity * 0.13;
|
|
1524
|
+
ctx.fillStyle = isAccent ? `rgba(${acR},${acG},${acB},${lightMode ? 0.44 : 0.26})` : `rgba(${cr},${cg},${cb},${alpha})`;
|
|
1525
|
+
ctx.fillText(ch, col * charW, row * lineH);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
// src/backgrounds/void.ts
|
|
1531
|
+
function renderVoidBackground(ctx, width, height, time, mousePos = { x: 0.5, y: 0.5 }, options = {}) {
|
|
1532
|
+
const {
|
|
1533
|
+
fontSize = 13,
|
|
1534
|
+
chars = " \xB7:;=+*#%@",
|
|
1535
|
+
color,
|
|
1536
|
+
accentColor = "#d4ff00",
|
|
1537
|
+
speed = 1,
|
|
1538
|
+
radius = 0.38,
|
|
1539
|
+
swirl = 3,
|
|
1540
|
+
lightMode = false
|
|
1541
|
+
} = options;
|
|
1542
|
+
const charW = fontSize * 0.62;
|
|
1543
|
+
const lineH = fontSize * 1.4;
|
|
1544
|
+
const cols = Math.ceil(width / charW);
|
|
1545
|
+
const rows = Math.ceil(height / lineH);
|
|
1546
|
+
const aspect = width / height;
|
|
1547
|
+
ctx.clearRect(0, 0, width, height);
|
|
1548
|
+
ctx.font = `${fontSize}px monospace`;
|
|
1549
|
+
ctx.textBaseline = "top";
|
|
1550
|
+
let cr = 255, cg = 255, cb = 255;
|
|
1551
|
+
if (lightMode) {
|
|
1552
|
+
cr = 0;
|
|
1553
|
+
cg = 0;
|
|
1554
|
+
cb = 0;
|
|
1555
|
+
}
|
|
1556
|
+
if (color) {
|
|
1557
|
+
const p = parseColor(color);
|
|
1558
|
+
if (p) {
|
|
1559
|
+
cr = p.r;
|
|
1560
|
+
cg = p.g;
|
|
1561
|
+
cb = p.b;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
let acR = 212, acG = 255, acB = 0;
|
|
1565
|
+
const ap = parseColor(accentColor);
|
|
1566
|
+
if (ap) {
|
|
1567
|
+
acR = ap.r;
|
|
1568
|
+
acG = ap.g;
|
|
1569
|
+
acB = ap.b;
|
|
1570
|
+
}
|
|
1571
|
+
const t = time * speed;
|
|
1572
|
+
for (let row = 0; row < rows; row++) {
|
|
1573
|
+
const ny = row / rows;
|
|
1574
|
+
for (let col = 0; col < cols; col++) {
|
|
1575
|
+
const nx = col / cols;
|
|
1576
|
+
const dx = (nx - mousePos.x) * aspect;
|
|
1577
|
+
const dy = ny - mousePos.y;
|
|
1578
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
1579
|
+
const r = dist / radius;
|
|
1580
|
+
if (r > 1) {
|
|
1581
|
+
const outerNoise = hash2(col * 3, row * 7) * Math.max(0, 1 - (r - 1) * 3);
|
|
1582
|
+
if (outerNoise < 0.62) continue;
|
|
1583
|
+
const alpha2 = outerNoise * (lightMode ? 0.05 : 0.04);
|
|
1584
|
+
ctx.fillStyle = `rgba(${cr},${cg},${cb},${alpha2})`;
|
|
1585
|
+
ctx.fillText(chars[1], col * charW, row * lineH);
|
|
1586
|
+
continue;
|
|
1587
|
+
}
|
|
1588
|
+
const pulseRing = Math.max(0, 1 - Math.abs(r - (0.15 + 0.12 * Math.sin(t * 1.1))) / 0.07);
|
|
1589
|
+
const gravity = Math.pow(1 - r, 2.2);
|
|
1590
|
+
const intensity = Math.min(1, gravity + pulseRing * 0.6);
|
|
1591
|
+
if (intensity < 0.06) continue;
|
|
1592
|
+
const densityI = Math.floor(intensity * (chars.length - 1));
|
|
1593
|
+
const charIdx = Math.min(chars.length - 1, densityI);
|
|
1594
|
+
const ch = chars[charIdx];
|
|
1595
|
+
const isAccent = pulseRing > 0.35 || r < 0.08;
|
|
1596
|
+
const alpha = lightMode ? intensity * 0.22 : intensity * 0.18;
|
|
1597
|
+
ctx.fillStyle = isAccent ? `rgba(${acR},${acG},${acB},${lightMode ? 0.55 : 0.38})` : `rgba(${cr},${cg},${cb},${alpha})`;
|
|
1598
|
+
ctx.fillText(ch, col * charW, row * lineH);
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
// src/backgrounds/morph.ts
|
|
1604
|
+
function renderMorphBackground(ctx, width, height, time, options = {}) {
|
|
1605
|
+
const {
|
|
1606
|
+
fontSize = 14,
|
|
1607
|
+
chars = " \xB7\u2219\u2022:-=+*#",
|
|
1608
|
+
color,
|
|
1609
|
+
accentColor = "#d4ff00",
|
|
1610
|
+
speed = 0.5,
|
|
1611
|
+
harmonics = 3,
|
|
1612
|
+
lightMode = false
|
|
1613
|
+
} = options;
|
|
1614
|
+
const charW = fontSize * 0.62;
|
|
1615
|
+
const lineH = fontSize * 1.4;
|
|
1616
|
+
const cols = Math.ceil(width / charW);
|
|
1617
|
+
const rows = Math.ceil(height / lineH);
|
|
1618
|
+
ctx.clearRect(0, 0, width, height);
|
|
1619
|
+
ctx.font = `${fontSize}px monospace`;
|
|
1620
|
+
ctx.textBaseline = "top";
|
|
1621
|
+
let cr = 255, cg = 255, cb = 255;
|
|
1622
|
+
if (lightMode) {
|
|
1623
|
+
cr = 0;
|
|
1624
|
+
cg = 0;
|
|
1625
|
+
cb = 0;
|
|
1626
|
+
}
|
|
1627
|
+
if (color) {
|
|
1628
|
+
const p = parseColor(color);
|
|
1629
|
+
if (p) {
|
|
1630
|
+
cr = p.r;
|
|
1631
|
+
cg = p.g;
|
|
1632
|
+
cb = p.b;
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
let acR = 212, acG = 255, acB = 0;
|
|
1636
|
+
const ap = parseColor(accentColor);
|
|
1637
|
+
if (ap) {
|
|
1638
|
+
acR = ap.r;
|
|
1639
|
+
acG = ap.g;
|
|
1640
|
+
acB = ap.b;
|
|
1641
|
+
}
|
|
1642
|
+
const t = time * speed;
|
|
1643
|
+
const maxV = Array.from({ length: harmonics }, (_, h) => 1 / (h + 1)).reduce((a, b) => a + b, 0);
|
|
1644
|
+
for (let row = 0; row < rows; row++) {
|
|
1645
|
+
for (let col = 0; col < cols; col++) {
|
|
1646
|
+
let v = 0;
|
|
1647
|
+
for (let h = 0; h < harmonics; h++) {
|
|
1648
|
+
const fBase = hash2(col * (h + 3) + 7, row * (h + 5) + 11);
|
|
1649
|
+
const fineF = 0.18 + fBase * 1.4;
|
|
1650
|
+
const phase = hash2(col * (h + 7), row * (h + 9) + 3) * Math.PI * 2;
|
|
1651
|
+
const weight = 1 / (h + 1);
|
|
1652
|
+
v += Math.sin(t * fineF + phase) * weight;
|
|
1653
|
+
}
|
|
1654
|
+
const norm = (v / maxV + 1) * 0.5;
|
|
1655
|
+
if (norm < 0.28) continue;
|
|
1656
|
+
const remapped = (norm - 0.28) / 0.72;
|
|
1657
|
+
const charIdx = Math.min(chars.length - 1, Math.floor(remapped * chars.length));
|
|
1658
|
+
const ch = chars[charIdx];
|
|
1659
|
+
const isAccent = norm > 0.88;
|
|
1660
|
+
const alpha = lightMode ? remapped * 0.17 : remapped * 0.13;
|
|
1661
|
+
ctx.fillStyle = isAccent ? `rgba(${acR},${acG},${acB},${lightMode ? 0.45 : 0.28})` : `rgba(${cr},${cg},${cb},${alpha})`;
|
|
1662
|
+
ctx.fillText(ch, col * charW, row * lineH);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
// src/backgrounds/index.ts
|
|
1418
1668
|
function _parseColor(c) {
|
|
1419
1669
|
const hex = c.match(/^#([0-9a-f]{3,8})$/i)?.[1];
|
|
1420
1670
|
if (hex) {
|
|
@@ -1503,6 +1753,21 @@ function asciiBackground(target, options = {}) {
|
|
|
1503
1753
|
lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
|
|
1504
1754
|
color: color ?? renderOpts.color
|
|
1505
1755
|
});
|
|
1756
|
+
const buildSilkOpts = () => ({
|
|
1757
|
+
...renderOpts,
|
|
1758
|
+
lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
|
|
1759
|
+
color: color ?? renderOpts.color
|
|
1760
|
+
});
|
|
1761
|
+
const buildVoidOpts = () => ({
|
|
1762
|
+
...renderOpts,
|
|
1763
|
+
lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
|
|
1764
|
+
color: color ?? renderOpts.color
|
|
1765
|
+
});
|
|
1766
|
+
const buildMorphOpts = () => ({
|
|
1767
|
+
...renderOpts,
|
|
1768
|
+
lightMode: renderOpts.lightMode !== void 0 ? renderOpts.lightMode : isLight(),
|
|
1769
|
+
color: color ?? renderOpts.color
|
|
1770
|
+
});
|
|
1506
1771
|
const optsRef = { current: buildWaveOpts() };
|
|
1507
1772
|
const rebuildOpts = () => {
|
|
1508
1773
|
if (type === "rain") optsRef.current = buildRainOpts();
|
|
@@ -1511,6 +1776,9 @@ function asciiBackground(target, options = {}) {
|
|
|
1511
1776
|
else if (type === "noise") optsRef.current = buildNoiseOpts();
|
|
1512
1777
|
else if (type === "grid") optsRef.current = buildGridOpts();
|
|
1513
1778
|
else if (type === "aurora") optsRef.current = buildAuroraOpts();
|
|
1779
|
+
else if (type === "silk") optsRef.current = buildSilkOpts();
|
|
1780
|
+
else if (type === "void") optsRef.current = buildVoidOpts();
|
|
1781
|
+
else if (type === "morph") optsRef.current = buildMorphOpts();
|
|
1514
1782
|
else optsRef.current = buildWaveOpts();
|
|
1515
1783
|
};
|
|
1516
1784
|
rebuildOpts();
|
|
@@ -1551,6 +1819,12 @@ function asciiBackground(target, options = {}) {
|
|
|
1551
1819
|
renderGridBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
|
|
1552
1820
|
} else if (type === "aurora") {
|
|
1553
1821
|
renderAuroraBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
|
|
1822
|
+
} else if (type === "silk") {
|
|
1823
|
+
renderSilkBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
1824
|
+
} else if (type === "void") {
|
|
1825
|
+
renderVoidBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
|
|
1826
|
+
} else if (type === "morph") {
|
|
1827
|
+
renderMorphBackground(ctx, r.width, r.height, time, optsRef.current);
|
|
1554
1828
|
} else {
|
|
1555
1829
|
renderWaveBackground(ctx, r.width, r.height, time, smoothMouse, optsRef.current);
|
|
1556
1830
|
}
|
|
@@ -2019,6 +2293,6 @@ function tryCreateWebGLRenderer(canvas) {
|
|
|
2019
2293
|
}
|
|
2020
2294
|
}
|
|
2021
2295
|
|
|
2022
|
-
export { ART_STYLE_PRESETS, CHARSETS, DEFAULT_OPTIONS, HOVER_PRESETS, asciiBackground, asciify, asciifyGif, asciifyVideo, generateAnimatedEmbedCode, generateEmbedCode, gifToAsciiFrames, imageToAsciiFrame, mountWaveBackground, renderAuroraBackground, renderFrameToCanvas, renderGridBackground, renderNoiseBackground, renderPulseBackground, renderRainBackground, renderStarsBackground, renderWaveBackground, tryCreateWebGLRenderer, videoToAsciiFrames };
|
|
2296
|
+
export { ART_STYLE_PRESETS, CHARSETS, DEFAULT_OPTIONS, HOVER_PRESETS, asciiBackground, asciify, asciifyGif, asciifyVideo, generateAnimatedEmbedCode, generateEmbedCode, gifToAsciiFrames, imageToAsciiFrame, mountWaveBackground, renderAuroraBackground, renderFrameToCanvas, renderGridBackground, renderMorphBackground, renderNoiseBackground, renderPulseBackground, renderRainBackground, renderSilkBackground, renderStarsBackground, renderVoidBackground, renderWaveBackground, tryCreateWebGLRenderer, videoToAsciiFrames };
|
|
2023
2297
|
//# sourceMappingURL=index.js.map
|
|
2024
2298
|
//# sourceMappingURL=index.js.map
|