trama-ui 0.1.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/CATALOG.md +1237 -0
- package/LICENSE +21 -0
- package/README.md +78 -0
- package/THIRD-PARTY.md +4 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/brown.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/brown_inlay.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/brown_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/grey.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/grey_inlay.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/grey_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/tan.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/tan_inlay.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/tan_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/white.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/white_inlay.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/white_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/blue.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/blue_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/green.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/green_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/grey.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/grey_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/red.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/red_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/yellow.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/yellow_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Outline/blue.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Outline/blue_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Outline/green.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Outline/green_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Outline/red.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Outline/red_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Outline/yellow.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Outline/yellow_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/list.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/space.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/space_inlay.png +0 -0
- package/assets/pixel/kenney-pixel-ui/License.txt +23 -0
- package/assets/pixel/kenney-pixel-ui/spritesheet/sheet.png +0 -0
- package/bin/trama-ui.mjs +27 -0
- package/dist/components/ui/Accordion.d.ts +12 -0
- package/dist/components/ui/Accordion.js +12 -0
- package/dist/components/ui/Alert.d.ts +15 -0
- package/dist/components/ui/Alert.js +15 -0
- package/dist/components/ui/ArticlesSection.d.ts +30 -0
- package/dist/components/ui/ArticlesSection.js +18 -0
- package/dist/components/ui/AsciiBackground.d.ts +58 -0
- package/dist/components/ui/AsciiBackground.js +173 -0
- package/dist/components/ui/AsciiCard.d.ts +78 -0
- package/dist/components/ui/AsciiCard.js +253 -0
- package/dist/components/ui/AsciiChart.d.ts +18 -0
- package/dist/components/ui/AsciiChart.js +47 -0
- package/dist/components/ui/Badge.d.ts +15 -0
- package/dist/components/ui/Badge.js +8 -0
- package/dist/components/ui/BitmapText.d.ts +25 -0
- package/dist/components/ui/BitmapText.js +21 -0
- package/dist/components/ui/BlogCard.d.ts +33 -0
- package/dist/components/ui/BlogCard.js +13 -0
- package/dist/components/ui/Breadcrumbs.d.ts +9 -0
- package/dist/components/ui/Breadcrumbs.js +7 -0
- package/dist/components/ui/Button.d.ts +74 -0
- package/dist/components/ui/Button.js +48 -0
- package/dist/components/ui/CTASection.d.ts +13 -0
- package/dist/components/ui/CTASection.js +7 -0
- package/dist/components/ui/Carousel.d.ts +30 -0
- package/dist/components/ui/Carousel.js +80 -0
- package/dist/components/ui/CheckboxGroup.d.ts +11 -0
- package/dist/components/ui/CheckboxGroup.js +20 -0
- package/dist/components/ui/CodeBlock.d.ts +11 -0
- package/dist/components/ui/CodeBlock.js +20 -0
- package/dist/components/ui/ContactForm.d.ts +25 -0
- package/dist/components/ui/ContactForm.js +26 -0
- package/dist/components/ui/CrtOverlay.d.ts +10 -0
- package/dist/components/ui/CrtOverlay.js +5 -0
- package/dist/components/ui/Divider.d.ts +12 -0
- package/dist/components/ui/Divider.js +7 -0
- package/dist/components/ui/Drawer.d.ts +16 -0
- package/dist/components/ui/Drawer.js +25 -0
- package/dist/components/ui/Dropdown.d.ts +10 -0
- package/dist/components/ui/Dropdown.js +21 -0
- package/dist/components/ui/FAQSection.d.ts +14 -0
- package/dist/components/ui/FAQSection.js +7 -0
- package/dist/components/ui/FeatureGrid.d.ts +18 -0
- package/dist/components/ui/FeatureGrid.js +14 -0
- package/dist/components/ui/Footer.d.ts +14 -0
- package/dist/components/ui/Footer.js +16 -0
- package/dist/components/ui/GlyphCursor.d.ts +22 -0
- package/dist/components/ui/GlyphCursor.js +50 -0
- package/dist/components/ui/GridBackground.d.ts +15 -0
- package/dist/components/ui/GridBackground.js +6 -0
- package/dist/components/ui/Hero.d.ts +21 -0
- package/dist/components/ui/Hero.js +12 -0
- package/dist/components/ui/HoverFX.d.ts +29 -0
- package/dist/components/ui/HoverFX.js +75 -0
- package/dist/components/ui/Icon.d.ts +33 -0
- package/dist/components/ui/Icon.js +29 -0
- package/dist/components/ui/ImageGallery.d.ts +31 -0
- package/dist/components/ui/ImageGallery.js +79 -0
- package/dist/components/ui/LogoCloud.d.ts +12 -0
- package/dist/components/ui/LogoCloud.js +9 -0
- package/dist/components/ui/MagneticButton.d.ts +12 -0
- package/dist/components/ui/MagneticButton.js +49 -0
- package/dist/components/ui/Marquee.d.ts +29 -0
- package/dist/components/ui/Marquee.js +15 -0
- package/dist/components/ui/Modal.d.ts +40 -0
- package/dist/components/ui/Modal.js +51 -0
- package/dist/components/ui/NavBar.d.ts +14 -0
- package/dist/components/ui/NavBar.js +12 -0
- package/dist/components/ui/NeonSign.d.ts +14 -0
- package/dist/components/ui/NeonSign.js +7 -0
- package/dist/components/ui/OtpInput.d.ts +9 -0
- package/dist/components/ui/OtpInput.js +27 -0
- package/dist/components/ui/Pagination.d.ts +10 -0
- package/dist/components/ui/Pagination.js +24 -0
- package/dist/components/ui/Panel.d.ts +21 -0
- package/dist/components/ui/Panel.js +8 -0
- package/dist/components/ui/PixelFrame.d.ts +61 -0
- package/dist/components/ui/PixelFrame.js +29 -0
- package/dist/components/ui/Presence.d.ts +51 -0
- package/dist/components/ui/Presence.js +343 -0
- package/dist/components/ui/PricingCard.d.ts +30 -0
- package/dist/components/ui/PricingCard.js +21 -0
- package/dist/components/ui/PricingSection.d.ts +35 -0
- package/dist/components/ui/PricingSection.js +21 -0
- package/dist/components/ui/Progress.d.ts +17 -0
- package/dist/components/ui/Progress.js +9 -0
- package/dist/components/ui/RadioGroup.d.ts +10 -0
- package/dist/components/ui/RadioGroup.js +11 -0
- package/dist/components/ui/RangeSlider.d.ts +14 -0
- package/dist/components/ui/RangeSlider.js +11 -0
- package/dist/components/ui/Reveal.d.ts +48 -0
- package/dist/components/ui/Reveal.js +181 -0
- package/dist/components/ui/SceneFlash.d.ts +14 -0
- package/dist/components/ui/SceneFlash.js +51 -0
- package/dist/components/ui/ScrambleText.d.ts +31 -0
- package/dist/components/ui/ScrambleText.js +51 -0
- package/dist/components/ui/ScrollArea.d.ts +15 -0
- package/dist/components/ui/ScrollArea.js +18 -0
- package/dist/components/ui/SectionHeader.d.ts +12 -0
- package/dist/components/ui/SectionHeader.js +6 -0
- package/dist/components/ui/Select.d.ts +17 -0
- package/dist/components/ui/Select.js +31 -0
- package/dist/components/ui/Skeleton.d.ts +12 -0
- package/dist/components/ui/Skeleton.js +18 -0
- package/dist/components/ui/Spinner.d.ts +15 -0
- package/dist/components/ui/Spinner.js +24 -0
- package/dist/components/ui/Spotlight.d.ts +16 -0
- package/dist/components/ui/Spotlight.js +16 -0
- package/dist/components/ui/Sprite.d.ts +27 -0
- package/dist/components/ui/Sprite.js +19 -0
- package/dist/components/ui/StatCounter.d.ts +24 -0
- package/dist/components/ui/StatCounter.js +70 -0
- package/dist/components/ui/StatsSection.d.ts +14 -0
- package/dist/components/ui/StatsSection.js +13 -0
- package/dist/components/ui/Stepper.d.ts +10 -0
- package/dist/components/ui/Stepper.js +7 -0
- package/dist/components/ui/Table.d.ts +10 -0
- package/dist/components/ui/Table.js +8 -0
- package/dist/components/ui/Tabs.d.ts +12 -0
- package/dist/components/ui/Tabs.js +18 -0
- package/dist/components/ui/TeamSection.d.ts +18 -0
- package/dist/components/ui/TeamSection.js +19 -0
- package/dist/components/ui/TerminalTyper.d.ts +20 -0
- package/dist/components/ui/TerminalTyper.js +45 -0
- package/dist/components/ui/Testimonial.d.ts +19 -0
- package/dist/components/ui/Testimonial.js +9 -0
- package/dist/components/ui/TestimonialSection.d.ts +20 -0
- package/dist/components/ui/TestimonialSection.js +15 -0
- package/dist/components/ui/TextField.d.ts +26 -0
- package/dist/components/ui/TextField.js +13 -0
- package/dist/components/ui/TextmodeBackground.d.ts +52 -0
- package/dist/components/ui/TextmodeBackground.js +83 -0
- package/dist/components/ui/Timeline.d.ts +13 -0
- package/dist/components/ui/Timeline.js +8 -0
- package/dist/components/ui/Toast.d.ts +30 -0
- package/dist/components/ui/Toast.js +47 -0
- package/dist/components/ui/Toggle.d.ts +13 -0
- package/dist/components/ui/Toggle.js +10 -0
- package/dist/components/ui/Tooltip.d.ts +20 -0
- package/dist/components/ui/Tooltip.js +20 -0
- package/dist/components/ui/Typewriter.d.ts +20 -0
- package/dist/components/ui/Typewriter.js +46 -0
- package/dist/components/ui/VideoPlayer.d.ts +81 -0
- package/dist/components/ui/VideoPlayer.js +379 -0
- package/dist/components/ui/fill.d.ts +32 -0
- package/dist/components/ui/fill.js +27 -0
- package/dist/components/ui/intent.d.ts +20 -0
- package/dist/components/ui/intent.js +23 -0
- package/dist/components/ui/styles/kit.css +31 -0
- package/dist/components/ui/styles/ui-carousel.css +113 -0
- package/dist/components/ui/styles/ui-kit.css +1135 -0
- package/dist/components/ui/styles/ui-video.css +75 -0
- package/dist/components/ui/swiperCss.d.ts +15 -0
- package/dist/components/ui/swiperCss.js +15 -0
- package/dist/components/ui/variants.d.ts +12 -0
- package/dist/components/ui/variants.js +10 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +77 -0
- package/dist/lib/asciify/painters.d.ts +25 -0
- package/dist/lib/asciify/painters.js +604 -0
- package/dist/lib/asciify/pointer.d.ts +12 -0
- package/dist/lib/asciify/pointer.js +54 -0
- package/dist/lib/asciify/shape-painters.d.ts +44 -0
- package/dist/lib/asciify/shape-painters.js +335 -0
- package/dist/lib/asciify/theme-painters.d.ts +8 -0
- package/dist/lib/asciify/theme-painters.js +437 -0
- package/dist/lib/cells.d.ts +46 -0
- package/dist/lib/cells.js +114 -0
- package/dist/lib/scroll/store.d.ts +21 -0
- package/dist/lib/scroll/store.js +25 -0
- package/dist/lib/sketches/aurora.d.ts +3 -0
- package/dist/lib/sketches/aurora.js +42 -0
- package/dist/lib/sketches/fire.d.ts +3 -0
- package/dist/lib/sketches/fire.js +80 -0
- package/dist/lib/sketches/flow.d.ts +3 -0
- package/dist/lib/sketches/flow.js +70 -0
- package/dist/lib/sketches/life.d.ts +3 -0
- package/dist/lib/sketches/life.js +107 -0
- package/dist/lib/sketches/matrix.d.ts +3 -0
- package/dist/lib/sketches/matrix.js +64 -0
- package/dist/lib/sketches/ocean.d.ts +3 -0
- package/dist/lib/sketches/ocean.js +56 -0
- package/dist/lib/sketches/plasma.d.ts +3 -0
- package/dist/lib/sketches/plasma.js +29 -0
- package/dist/lib/sketches/registry.d.ts +6 -0
- package/dist/lib/sketches/registry.js +60 -0
- package/dist/lib/sketches/ripples.d.ts +3 -0
- package/dist/lib/sketches/ripples.js +84 -0
- package/dist/lib/sketches/scroll-assemble.d.ts +8 -0
- package/dist/lib/sketches/scroll-assemble.js +90 -0
- package/dist/lib/sketches/scroll-parallax.d.ts +6 -0
- package/dist/lib/sketches/scroll-parallax.js +76 -0
- package/dist/lib/sketches/scroll-rain.d.ts +6 -0
- package/dist/lib/sketches/scroll-rain.js +82 -0
- package/dist/lib/sketches/scroll-stats.d.ts +7 -0
- package/dist/lib/sketches/scroll-stats.js +81 -0
- package/dist/lib/sketches/scroll-terminal.d.ts +6 -0
- package/dist/lib/sketches/scroll-terminal.js +87 -0
- package/dist/lib/sketches/scroll-zoom.d.ts +7 -0
- package/dist/lib/sketches/scroll-zoom.js +49 -0
- package/dist/lib/sketches/starfield.d.ts +3 -0
- package/dist/lib/sketches/starfield.js +54 -0
- package/dist/lib/sketches/text-3d.d.ts +6 -0
- package/dist/lib/sketches/text-3d.js +62 -0
- package/dist/lib/sketches/text-combo-figlet.d.ts +6 -0
- package/dist/lib/sketches/text-combo-figlet.js +91 -0
- package/dist/lib/sketches/text-glitch.d.ts +3 -0
- package/dist/lib/sketches/text-glitch.js +57 -0
- package/dist/lib/sketches/text-marquee.d.ts +3 -0
- package/dist/lib/sketches/text-marquee.js +35 -0
- package/dist/lib/sketches/text-particles.d.ts +9 -0
- package/dist/lib/sketches/text-particles.js +91 -0
- package/dist/lib/sketches/text-rain.d.ts +3 -0
- package/dist/lib/sketches/text-rain.js +73 -0
- package/dist/lib/sketches/text-scramble.d.ts +6 -0
- package/dist/lib/sketches/text-scramble.js +49 -0
- package/dist/lib/sketches/text-wave.d.ts +3 -0
- package/dist/lib/sketches/text-wave.js +43 -0
- package/dist/lib/sketches/theme-orbit.d.ts +6 -0
- package/dist/lib/sketches/theme-orbit.js +119 -0
- package/dist/lib/sketches/theme-pulse.d.ts +7 -0
- package/dist/lib/sketches/theme-pulse.js +84 -0
- package/dist/lib/sketches/tunnel.d.ts +3 -0
- package/dist/lib/sketches/tunnel.js +28 -0
- package/dist/lib/text/banner.d.ts +5 -0
- package/dist/lib/text/banner.js +35 -0
- package/dist/lib/text/mask.d.ts +40 -0
- package/dist/lib/text/mask.js +143 -0
- package/dist/lib/text/store.d.ts +23 -0
- package/dist/lib/text/store.js +86 -0
- package/dist/lib/ui/card-fields.d.ts +52 -0
- package/dist/lib/ui/card-fields.js +193 -0
- package/dist/lib/ui/fonts.d.ts +56 -0
- package/dist/lib/ui/fonts.js +94 -0
- package/dist/lib/ui/fx.d.ts +42 -0
- package/dist/lib/ui/fx.js +162 -0
- package/dist/lib/ui/icons.d.ts +437 -0
- package/dist/lib/ui/icons.js +314 -0
- package/dist/lib/ui/placeholder.d.ts +11 -0
- package/dist/lib/ui/placeholder.js +65 -0
- package/dist/lib/ui/tokens.d.ts +49 -0
- package/dist/lib/ui/tokens.js +129 -0
- package/dist/lib/ui/useTokens.d.ts +10 -0
- package/dist/lib/ui/useTokens.js +32 -0
- package/package.json +80 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { aspect, createPointer, gridOrigin, hsv, plot } from "../cells.js";
|
|
2
|
+
import { smooth, scrollState, span } from "../scroll/store.js";
|
|
3
|
+
import { getTextMask } from "../text/mask.js";
|
|
4
|
+
import { textStore } from "../text/store.js";
|
|
5
|
+
/**
|
|
6
|
+
* Ensamblaje: las partículas empiezan dispersas, se agrupan formando tu texto a medida
|
|
7
|
+
* que bajas y estallan hacia fuera al final del recorrido.
|
|
8
|
+
*/
|
|
9
|
+
export function scAssemble(t, opts = {}) {
|
|
10
|
+
const ptr = createPointer(t);
|
|
11
|
+
const particles = [];
|
|
12
|
+
let key = "";
|
|
13
|
+
const retarget = (data, cols, rows, x0, y0) => {
|
|
14
|
+
const cells = [];
|
|
15
|
+
for (let j = 0; j < rows; j++)
|
|
16
|
+
for (let i = 0; i < cols; i++)
|
|
17
|
+
if (data[j * cols + i] > 140)
|
|
18
|
+
cells.push(x0 + i, y0 + j);
|
|
19
|
+
const total = cells.length / 2;
|
|
20
|
+
const count = Math.min(2800, total);
|
|
21
|
+
const step = total / Math.max(1, count);
|
|
22
|
+
while (particles.length < count) {
|
|
23
|
+
const ang = Math.random() * Math.PI * 2;
|
|
24
|
+
const r = (0.4 + Math.random() * 0.6) * Math.max(cols, rows) * 0.6;
|
|
25
|
+
particles.push({
|
|
26
|
+
hx: Math.cos(ang) * r,
|
|
27
|
+
hy: Math.sin(ang) * r * 0.6,
|
|
28
|
+
tx: 0,
|
|
29
|
+
ty: 0,
|
|
30
|
+
x: 0,
|
|
31
|
+
y: 0,
|
|
32
|
+
seed: Math.random(),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
particles.length = count;
|
|
36
|
+
particles.forEach((p, k) => {
|
|
37
|
+
const idx = Math.floor(k * step);
|
|
38
|
+
p.tx = cells[idx * 2];
|
|
39
|
+
p.ty = cells[idx * 2 + 1];
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
t.draw(() => {
|
|
43
|
+
ptr.update();
|
|
44
|
+
const p = scrollState.p;
|
|
45
|
+
const s = t.secs;
|
|
46
|
+
const asp = aspect(t);
|
|
47
|
+
const { cols, rows, x0, y0 } = gridOrigin(t);
|
|
48
|
+
const mask = getTextMask(textStore.text(), cols, rows, asp);
|
|
49
|
+
if (mask.key !== key) {
|
|
50
|
+
key = mask.key;
|
|
51
|
+
retarget(mask.data, cols, rows, x0, y0);
|
|
52
|
+
}
|
|
53
|
+
const assemble = smooth(span(p, 0.05, 0.5));
|
|
54
|
+
const explode = smooth(span(p, 0.78, 1));
|
|
55
|
+
const R = 9;
|
|
56
|
+
if (opts.transparent)
|
|
57
|
+
t.clear();
|
|
58
|
+
else
|
|
59
|
+
t.background(4, 5, 12);
|
|
60
|
+
for (const q of particles) {
|
|
61
|
+
// deriva orgánica mientras está lejos de su sitio
|
|
62
|
+
const drift = (1 - assemble) * 1.6;
|
|
63
|
+
let gx = q.hx + Math.sin(s * 0.7 + q.seed * 20) * drift * 3;
|
|
64
|
+
let gy = q.hy + Math.cos(s * 0.6 + q.seed * 14) * drift * 2;
|
|
65
|
+
gx = gx * (1 - assemble) + q.tx * assemble;
|
|
66
|
+
gy = gy * (1 - assemble) + q.ty * assemble;
|
|
67
|
+
const ang = Math.atan2(q.ty, q.tx || 0.001);
|
|
68
|
+
gx += Math.cos(ang) * explode * 60 * (0.5 + q.seed);
|
|
69
|
+
gy += Math.sin(ang) * explode * 40 * (0.5 + q.seed);
|
|
70
|
+
// el puntero aparta las partículas ya ensambladas
|
|
71
|
+
const dx = q.x - ptr.x;
|
|
72
|
+
const dy = (q.y - ptr.y) * asp;
|
|
73
|
+
const d = Math.hypot(dx, dy) + 0.001;
|
|
74
|
+
if (d < R && assemble > 0.5) {
|
|
75
|
+
const k = (R - d) / R;
|
|
76
|
+
gx += (dx / d) * k * 5;
|
|
77
|
+
gy += ((dy / d) * k * 5) / asp;
|
|
78
|
+
}
|
|
79
|
+
q.x += (gx - q.x) * 0.14;
|
|
80
|
+
q.y += (gy - q.y) * 0.14;
|
|
81
|
+
const settled = assemble * (1 - explode);
|
|
82
|
+
t.char(settled > 0.85 ? "#" : settled > 0.4 ? "+" : "*");
|
|
83
|
+
const hue = (q.tx * 0.006 + s * 0.04 + 0.5 + explode * 0.2) % 1;
|
|
84
|
+
t.charColor(...hsv(hue, 0.7 - settled * 0.15, 0.35 + 0.65 * settled));
|
|
85
|
+
if (!opts.transparent)
|
|
86
|
+
t.cellColor(...hsv(hue, 0.8, 0.02 + 0.12 * settled));
|
|
87
|
+
plot(t, Math.round(q.x), Math.round(q.y));
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Textmodifier } from "textmode.js";
|
|
2
|
+
/**
|
|
3
|
+
* Ascenso con paralaje: al bajar la cámara sube desde un valle al atardecer hasta el
|
|
4
|
+
* espacio. Cada capa de montañas se mueve a distinta velocidad y el cielo cambia de color.
|
|
5
|
+
*/
|
|
6
|
+
export declare function scParallax(t: Textmodifier): void;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createPointer, eachCell, hash2, hsv, mix } from "../cells.js";
|
|
2
|
+
import { clamp, scrollState } from "../scroll/store.js";
|
|
3
|
+
/**
|
|
4
|
+
* Ascenso con paralaje: al bajar la cámara sube desde un valle al atardecer hasta el
|
|
5
|
+
* espacio. Cada capa de montañas se mueve a distinta velocidad y el cielo cambia de color.
|
|
6
|
+
*/
|
|
7
|
+
export function scParallax(t) {
|
|
8
|
+
const ptr = createPointer(t);
|
|
9
|
+
const ramp = t.createGlyphRamp(" .:-=+*#%@");
|
|
10
|
+
t.draw(() => {
|
|
11
|
+
ptr.update();
|
|
12
|
+
const p = scrollState.p;
|
|
13
|
+
const s = t.secs;
|
|
14
|
+
const { cols, rows } = t.grid;
|
|
15
|
+
const worldH = rows * 3.2;
|
|
16
|
+
const cam = p * worldH;
|
|
17
|
+
const px = (ptr.x / cols) * 6; // leve paralaje horizontal con el puntero
|
|
18
|
+
// altura de cada capa de montañas por columna (una sola evaluación de ruido por columna)
|
|
19
|
+
const layers = [
|
|
20
|
+
{ f: 0.35, base: rows * 0.55, amp: rows * 0.16, seed: 3, hue: 0.62, v: 0.32 },
|
|
21
|
+
{ f: 0.6, base: rows * 0.42, amp: rows * 0.13, seed: 11, hue: 0.72, v: 0.2 },
|
|
22
|
+
{ f: 1.0, base: rows * 0.3, amp: rows * 0.11, seed: 23, hue: 0.8, v: 0.1 },
|
|
23
|
+
];
|
|
24
|
+
const tops = layers.map((L) => {
|
|
25
|
+
const arr = new Float32Array(cols);
|
|
26
|
+
for (let i = 0; i < cols; i++) {
|
|
27
|
+
const x = i + px * L.f * 4;
|
|
28
|
+
arr[i] = L.base + (t.noise(x * 0.03 + L.seed, L.seed) - 0.5) * 2 * L.amp + t.noise(x * 0.11, L.seed) * 3;
|
|
29
|
+
}
|
|
30
|
+
return arr;
|
|
31
|
+
});
|
|
32
|
+
t.background(0);
|
|
33
|
+
eachCell(t, (x, _y, i, j) => {
|
|
34
|
+
const sy = rows - 1 - j; // altura en pantalla (0 = abajo)
|
|
35
|
+
const wy = sy + cam; // altura en el mundo
|
|
36
|
+
const alt = clamp(wy / (worldH + rows));
|
|
37
|
+
// capas de montaña de cerca a lejos
|
|
38
|
+
for (let k = layers.length - 1; k >= 0; k--) {
|
|
39
|
+
const L = layers[k];
|
|
40
|
+
const top = tops[k][i] - cam * L.f;
|
|
41
|
+
if (sy < top) {
|
|
42
|
+
const edge = top - sy < 1.2;
|
|
43
|
+
t.char(edge ? "^" : ramp.at(0.2 + 0.1 * (top - sy > 6 ? 0 : 1)));
|
|
44
|
+
t.charColor(...hsv(L.hue, 0.4, L.v + (edge ? 0.25 : 0)));
|
|
45
|
+
t.cellColor(...hsv(L.hue, 0.6, L.v * 0.35));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// cielo: del atardecer (abajo) al espacio (arriba)
|
|
50
|
+
const dusk = clamp(1 - alt * 2.2);
|
|
51
|
+
const night = clamp(alt * 1.6);
|
|
52
|
+
const r = mix(mix(15, 255, dusk * dusk), 0, night) * 0.9;
|
|
53
|
+
const g = mix(mix(20, 110, dusk * dusk), 2, night) * 0.9;
|
|
54
|
+
const b = mix(mix(70, 90, dusk), 10, night) * 0.9;
|
|
55
|
+
t.cellColor(r, g, b);
|
|
56
|
+
const star = hash2(x + Math.floor(cam * 0.05), Math.floor(wy * 0.5));
|
|
57
|
+
if (star > 1 - 0.03 * clamp(alt * 3)) {
|
|
58
|
+
const tw = Math.sin(s * 2 + star * 90) * 0.5 + 0.5;
|
|
59
|
+
t.char(tw > 0.55 ? "*" : ".");
|
|
60
|
+
t.charColor(255, 255, 255, 120 + 135 * tw);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// nubes en la franja media
|
|
64
|
+
const cloudBand = Math.exp(-(((alt - 0.28) / 0.08) ** 2));
|
|
65
|
+
if (cloudBand > 0.05) {
|
|
66
|
+
const c = t.noise(x * 0.06 + s * 0.05, wy * 0.09) * cloudBand;
|
|
67
|
+
if (c > 0.42) {
|
|
68
|
+
t.char(ramp.at(c));
|
|
69
|
+
t.charColor(255, 235, 230, 150);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
t.char(" ");
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { aspect, createPointer, gridOrigin, plot } from "../cells.js";
|
|
2
|
+
import { smooth, scrollState, span } from "../scroll/store.js";
|
|
3
|
+
import { getTextMask } from "../text/mask.js";
|
|
4
|
+
import { textStore } from "../text/store.js";
|
|
5
|
+
const GLYPHS = "01ABCDEFGHIJKLMNOPQRSTUVWXYZ<>{}[]/\\|+=*:;#$%&@";
|
|
6
|
+
/**
|
|
7
|
+
* Lluvia sensible al scroll: cae más rápido cuanto más rápido desplazas y, pasada la
|
|
8
|
+
* mitad del recorrido, se va solidificando en tu texto.
|
|
9
|
+
*/
|
|
10
|
+
export function scRain(t) {
|
|
11
|
+
const ptr = createPointer(t);
|
|
12
|
+
let W = 0;
|
|
13
|
+
let H = 0;
|
|
14
|
+
let glow = new Float32Array(0);
|
|
15
|
+
let heads = [];
|
|
16
|
+
const rebuild = () => {
|
|
17
|
+
W = t.grid.cols;
|
|
18
|
+
H = t.grid.rows;
|
|
19
|
+
glow = new Float32Array(W * H);
|
|
20
|
+
heads = Array.from({ length: W }, () => ({ y: Math.random() * H, v: 0.2 + Math.random() * 0.6 }));
|
|
21
|
+
};
|
|
22
|
+
t.draw(() => {
|
|
23
|
+
if (W !== t.grid.cols || H !== t.grid.rows)
|
|
24
|
+
rebuild();
|
|
25
|
+
ptr.update();
|
|
26
|
+
const p = scrollState.p;
|
|
27
|
+
const boost = 1 + Math.abs(scrollState.v) * 5;
|
|
28
|
+
const text = textStore.text();
|
|
29
|
+
const asp = aspect(t);
|
|
30
|
+
const { x0, y0 } = gridOrigin(t);
|
|
31
|
+
const mask = getTextMask(text, W, H, asp);
|
|
32
|
+
const reveal = smooth(span(p, 0.45, 0.85));
|
|
33
|
+
for (let i = 0; i < W; i++) {
|
|
34
|
+
const h = heads[i];
|
|
35
|
+
h.y += h.v * boost * (scrollState.v < 0 ? -1 : 1);
|
|
36
|
+
if (h.y >= H + 4 || h.y < -H * 0.6) {
|
|
37
|
+
h.y = scrollState.v < 0 ? H + 2 : -Math.random() * H * 0.5;
|
|
38
|
+
h.v = 0.2 + Math.random() * 0.6;
|
|
39
|
+
}
|
|
40
|
+
const row = Math.floor(h.y);
|
|
41
|
+
if (row >= 0 && row < H)
|
|
42
|
+
glow[row * W + i] = 1;
|
|
43
|
+
}
|
|
44
|
+
if (ptr.raw) {
|
|
45
|
+
const gx = Math.round(ptr.raw.x - x0);
|
|
46
|
+
const gy = Math.round(ptr.raw.y - y0);
|
|
47
|
+
for (let dy = -3; dy <= 3; dy++)
|
|
48
|
+
for (let dx = -6; dx <= 6; dx++) {
|
|
49
|
+
const x = gx + dx;
|
|
50
|
+
const y = gy + dy;
|
|
51
|
+
if (x >= 0 && y >= 0 && x < W && y < H && Math.hypot(dx / 2, dy) <= 3)
|
|
52
|
+
glow[y * W + x] = 1;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// El verde matrix vira a cian y luego a ámbar al avanzar
|
|
56
|
+
const cr = 0 + p * 60;
|
|
57
|
+
const cg = 255 - p * 60;
|
|
58
|
+
const cb = 90 + p * 130;
|
|
59
|
+
t.background(0, 3, 4);
|
|
60
|
+
for (let j = 0; j < H; j++) {
|
|
61
|
+
for (let i = 0; i < W; i++) {
|
|
62
|
+
const idx = j * W + i;
|
|
63
|
+
const g = glow[idx];
|
|
64
|
+
glow[idx] = g * 0.93;
|
|
65
|
+
const m = (mask.data[idx] / 255) * reveal;
|
|
66
|
+
const b = Math.max(g, m);
|
|
67
|
+
if (b < 0.05)
|
|
68
|
+
continue;
|
|
69
|
+
const lit = m > 0.4;
|
|
70
|
+
t.char(lit
|
|
71
|
+
? text[(i + j * 7) % text.length] || "#"
|
|
72
|
+
: GLYPHS[(i * 31 + j * 17 + (g > 0.9 ? t.frameCount : 0)) % GLYPHS.length]);
|
|
73
|
+
if (lit)
|
|
74
|
+
t.charColor(140 + 115 * g, 255, 170 + 85 * g);
|
|
75
|
+
else
|
|
76
|
+
t.charColor(cr * b, cg * b * 0.9, cb * b * 0.5);
|
|
77
|
+
t.cellColor(0, lit ? 20 + 30 * b : 4 + 10 * b, lit ? 12 : 3);
|
|
78
|
+
plot(t, x0 + i, y0 + j);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Textmodifier } from "textmode.js";
|
|
2
|
+
/**
|
|
3
|
+
* Combinación de ambas librerías: asciify-engine genera los números con su fuente
|
|
4
|
+
* bitmap 7×7 (`asciifyText`) y textmode.js los dibuja celda a celda mientras cuentan
|
|
5
|
+
* hacia arriba con el scroll. Ideal para una sección de cifras de una landing.
|
|
6
|
+
*/
|
|
7
|
+
export declare function scStats(t: Textmodifier): void;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { createPointer, gridOrigin, hsv, plot } from "../cells.js";
|
|
2
|
+
import { smooth, scrollState, span } from "../scroll/store.js";
|
|
3
|
+
import { textStore } from "../text/store.js";
|
|
4
|
+
const STATS = [
|
|
5
|
+
{ to: 12, suffix: "K", label: "USUARIOS", start: 0.08 },
|
|
6
|
+
{ to: 98, suffix: "%", label: "SATISFACCION", start: 0.28 },
|
|
7
|
+
{ to: 250, suffix: "+", label: "PROYECTOS", start: 0.48 },
|
|
8
|
+
];
|
|
9
|
+
/**
|
|
10
|
+
* Combinación de ambas librerías: asciify-engine genera los números con su fuente
|
|
11
|
+
* bitmap 7×7 (`asciifyText`) y textmode.js los dibuja celda a celda mientras cuentan
|
|
12
|
+
* hacia arriba con el scroll. Ideal para una sección de cifras de una landing.
|
|
13
|
+
*/
|
|
14
|
+
export function scStats(t) {
|
|
15
|
+
const ptr = createPointer(t);
|
|
16
|
+
let bitmap = null;
|
|
17
|
+
const cache = new Map();
|
|
18
|
+
import("asciify-engine/core").then((m) => (bitmap = m.asciifyText));
|
|
19
|
+
const art = (str) => {
|
|
20
|
+
let a = cache.get(str);
|
|
21
|
+
if (!a) {
|
|
22
|
+
a = bitmap(str, { char: "#" }).split("\n");
|
|
23
|
+
cache.set(str, a);
|
|
24
|
+
}
|
|
25
|
+
return a;
|
|
26
|
+
};
|
|
27
|
+
t.draw(() => {
|
|
28
|
+
ptr.update();
|
|
29
|
+
const p = scrollState.p;
|
|
30
|
+
const s = t.secs;
|
|
31
|
+
const { cols, rows } = gridOrigin(t);
|
|
32
|
+
t.background(5, 6, 16);
|
|
33
|
+
// línea base decorativa
|
|
34
|
+
for (let x = -Math.floor(cols / 2); x < cols / 2; x++) {
|
|
35
|
+
t.char("-");
|
|
36
|
+
t.charColor(40, 50, 90);
|
|
37
|
+
plot(t, x, Math.floor(rows * 0.18));
|
|
38
|
+
}
|
|
39
|
+
t.printAlign("center", "middle");
|
|
40
|
+
t.charColor(150, 170, 230);
|
|
41
|
+
t.cellColor(5, 6, 16);
|
|
42
|
+
t.print(`${textStore.text()} · EN CIFRAS`, 0, -Math.floor(rows * 0.38), { markup: false });
|
|
43
|
+
if (!bitmap)
|
|
44
|
+
return;
|
|
45
|
+
const colW = Math.floor(cols / 3);
|
|
46
|
+
STATS.forEach((st, k) => {
|
|
47
|
+
const k01 = smooth(span(p, st.start, st.start + 0.28));
|
|
48
|
+
const value = Math.round(st.to * k01);
|
|
49
|
+
const lines = art(`${value}${st.suffix}`);
|
|
50
|
+
const w = Math.max(...lines.map((l) => l.length));
|
|
51
|
+
const h = lines.length;
|
|
52
|
+
const kx = Math.max(1, Math.min(3, Math.floor((colW * 0.9) / Math.max(1, w))));
|
|
53
|
+
const cx = (k - 1) * colW;
|
|
54
|
+
const ox = cx - Math.floor((w * kx) / 2);
|
|
55
|
+
const oy = -Math.floor((h * kx) / 2) - 1;
|
|
56
|
+
const visible = clamp01(span(p, st.start - 0.05, st.start + 0.05));
|
|
57
|
+
if (visible <= 0)
|
|
58
|
+
return;
|
|
59
|
+
const hue = (0.55 + k * 0.17 + s * 0.02) % 1;
|
|
60
|
+
for (let j = 0; j < h; j++) {
|
|
61
|
+
for (let i = 0; i < lines[j].length; i++) {
|
|
62
|
+
if (lines[j][i] === " ")
|
|
63
|
+
continue;
|
|
64
|
+
for (let sy = 0; sy < kx; sy++) {
|
|
65
|
+
for (let sx = 0; sx < kx; sx++) {
|
|
66
|
+
t.char("#");
|
|
67
|
+
t.charColor(...hsv(hue, 0.55, (0.55 + 0.45 * (1 - j / h)) * visible));
|
|
68
|
+
t.cellColor(...hsv(hue, 0.85, 0.12 * visible));
|
|
69
|
+
plot(t, ox + i * kx + sx, oy + j * kx + sy);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
t.printAlign("center", "middle");
|
|
75
|
+
t.charColor(...hsv(hue, 0.3, 0.9 * visible));
|
|
76
|
+
t.cellColor(5, 6, 16);
|
|
77
|
+
t.print(st.label, cx, oy + h * kx + 3, { markup: false });
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const clamp01 = (v) => (v < 0 ? 0 : v > 1 ? 1 : v);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Textmodifier } from "textmode.js";
|
|
2
|
+
/**
|
|
3
|
+
* Terminal que se escribe sola al hacer scroll: cada línea aparece carácter a carácter
|
|
4
|
+
* según el progreso. Usa tu texto como nombre del proyecto.
|
|
5
|
+
*/
|
|
6
|
+
export declare function scTerminal(t: Textmodifier): void;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createPointer, gridOrigin, hash2, hsv, plot } from "../cells.js";
|
|
2
|
+
import { clamp, scrollState } from "../scroll/store.js";
|
|
3
|
+
import { textStore } from "../text/store.js";
|
|
4
|
+
const BAR = [28, 34, 56];
|
|
5
|
+
const BODY = [10, 13, 24];
|
|
6
|
+
const DOTS = [
|
|
7
|
+
[255, 95, 87],
|
|
8
|
+
[255, 189, 46],
|
|
9
|
+
[40, 200, 64],
|
|
10
|
+
];
|
|
11
|
+
/**
|
|
12
|
+
* Terminal que se escribe sola al hacer scroll: cada línea aparece carácter a carácter
|
|
13
|
+
* según el progreso. Usa tu texto como nombre del proyecto.
|
|
14
|
+
*/
|
|
15
|
+
export function scTerminal(t) {
|
|
16
|
+
const ptr = createPointer(t);
|
|
17
|
+
t.draw(() => {
|
|
18
|
+
ptr.update();
|
|
19
|
+
const p = scrollState.p;
|
|
20
|
+
const s = t.secs;
|
|
21
|
+
const { cols, rows, x0, y0 } = gridOrigin(t);
|
|
22
|
+
const name = textStore.text().toLowerCase().replace(/\s+/g, "-").slice(0, 24);
|
|
23
|
+
const lines = [
|
|
24
|
+
{ text: `$ npx create-landing ${name}`, color: [130, 230, 255] },
|
|
25
|
+
{ text: "[ok] plantilla descargada", color: [120, 255, 160] },
|
|
26
|
+
{ text: "[ok] textmode.js + asciify-engine instalados", color: [120, 255, 160] },
|
|
27
|
+
{ text: "[ok] gsap ScrollTrigger configurado", color: [120, 255, 160] },
|
|
28
|
+
{ text: `$ cd ${name} && npm run dev`, color: [130, 230, 255] },
|
|
29
|
+
{ text: "ready - http://localhost:3000", color: [255, 220, 120] },
|
|
30
|
+
{ text: "scroll para ver la magia_", color: [200, 200, 220] },
|
|
31
|
+
];
|
|
32
|
+
const total = lines.reduce((n, l) => n + l.text.length + 4, 0);
|
|
33
|
+
let typed = Math.floor(clamp(p * 1.1) * total);
|
|
34
|
+
const boxW = Math.min(cols - 6, 78);
|
|
35
|
+
const boxH = lines.length + 4;
|
|
36
|
+
const bx = -Math.floor(boxW / 2);
|
|
37
|
+
const by = -Math.floor(boxH / 2);
|
|
38
|
+
t.background(4, 6, 12);
|
|
39
|
+
// fondo tenue de puntos
|
|
40
|
+
for (let n = 0; n < 60; n++) {
|
|
41
|
+
const hx = hash2(n, Math.floor(s * 0.2));
|
|
42
|
+
const hy = hash2(Math.floor(s * 0.2), n * 3);
|
|
43
|
+
t.char(".");
|
|
44
|
+
t.charColor(40, 55, 90);
|
|
45
|
+
plot(t, x0 + Math.floor(hx * cols), y0 + Math.floor(hy * rows));
|
|
46
|
+
}
|
|
47
|
+
// ventana
|
|
48
|
+
for (let j = 0; j < boxH; j++) {
|
|
49
|
+
for (let i = 0; i < boxW; i++) {
|
|
50
|
+
const edge = i === 0 || i === boxW - 1 || j === 0 || j === boxH - 1;
|
|
51
|
+
const bar = j === 1;
|
|
52
|
+
t.cellColor(...(bar ? BAR : BODY));
|
|
53
|
+
t.charColor(90, 105, 150);
|
|
54
|
+
t.char(edge ? (j === 0 || j === boxH - 1 ? (i === 0 || i === boxW - 1 ? "+" : "-") : "|") : " ");
|
|
55
|
+
plot(t, bx + i, by + j);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// botones de la barra de título
|
|
59
|
+
DOTS.forEach((c, k) => {
|
|
60
|
+
t.char("o");
|
|
61
|
+
t.charColor(...c);
|
|
62
|
+
t.cellColor(28, 34, 56);
|
|
63
|
+
plot(t, bx + 2 + k * 2, by + 1);
|
|
64
|
+
});
|
|
65
|
+
// líneas escritas
|
|
66
|
+
t.printAlign("left", "top");
|
|
67
|
+
lines.forEach((l, k) => {
|
|
68
|
+
const n = Math.max(0, Math.min(l.text.length, typed));
|
|
69
|
+
typed -= l.text.length + 4;
|
|
70
|
+
if (n <= 0)
|
|
71
|
+
return;
|
|
72
|
+
t.cellColor(10, 13, 24);
|
|
73
|
+
t.charColor(...l.color);
|
|
74
|
+
t.print(l.text.slice(0, n), bx + 2, by + 3 + k, { markup: false });
|
|
75
|
+
// cursor parpadeante en la línea activa
|
|
76
|
+
if (n < l.text.length || (typed < 0 && k === lines.length - 1)) {
|
|
77
|
+
if (Math.floor(s * 2.5) % 2 === 0) {
|
|
78
|
+
t.charColor(255, 255, 255);
|
|
79
|
+
t.cellColor(220, 220, 240);
|
|
80
|
+
t.char(" ");
|
|
81
|
+
plot(t, bx + 2 + n, by + 3 + k);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
void hsv;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Textmodifier } from "textmode.js";
|
|
2
|
+
/**
|
|
3
|
+
* Zoom tipográfico: al bajar te sumerges dentro de las letras (hasta 50×).
|
|
4
|
+
* La cámara converge hacia un punto interior de una letra, así que al final
|
|
5
|
+
* la pantalla queda llena de tu propio texto.
|
|
6
|
+
*/
|
|
7
|
+
export declare function scZoom(t: Textmodifier): void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { aspect, createPointer, gridOrigin, hsv, plot } from "../cells.js";
|
|
2
|
+
import { scrollState } from "../scroll/store.js";
|
|
3
|
+
import { getFineMask } from "../text/mask.js";
|
|
4
|
+
import { textStore } from "../text/store.js";
|
|
5
|
+
/**
|
|
6
|
+
* Zoom tipográfico: al bajar te sumerges dentro de las letras (hasta 50×).
|
|
7
|
+
* La cámara converge hacia un punto interior de una letra, así que al final
|
|
8
|
+
* la pantalla queda llena de tu propio texto.
|
|
9
|
+
*/
|
|
10
|
+
export function scZoom(t) {
|
|
11
|
+
const ptr = createPointer(t);
|
|
12
|
+
const ramp = t.createGlyphRamp(" .:-=+*#%@");
|
|
13
|
+
t.draw(() => {
|
|
14
|
+
ptr.update();
|
|
15
|
+
const p = scrollState.p;
|
|
16
|
+
const s = t.secs;
|
|
17
|
+
const asp = aspect(t);
|
|
18
|
+
const { cols, rows, x0, y0 } = gridOrigin(t);
|
|
19
|
+
const text = textStore.text();
|
|
20
|
+
const fm = getFineMask(text, cols, rows, asp, 3);
|
|
21
|
+
const scale = Math.exp(p * Math.log(50));
|
|
22
|
+
const a = 1 - 1 / scale;
|
|
23
|
+
// punto de la máscara que cae bajo el centro de pantalla
|
|
24
|
+
const cxm = cols / 2 + (fm.focus.x - cols / 2) * a;
|
|
25
|
+
const cym = rows / 2 + (fm.focus.y - rows / 2) * a;
|
|
26
|
+
t.background(4, 5, 14);
|
|
27
|
+
for (let j = 0; j < rows; j++) {
|
|
28
|
+
for (let i = 0; i < cols; i++) {
|
|
29
|
+
const x = x0 + i;
|
|
30
|
+
const y = y0 + j;
|
|
31
|
+
const mx = Math.floor((cxm + x / scale) * fm.ss);
|
|
32
|
+
const my = Math.floor((cym + y / scale) * fm.ss);
|
|
33
|
+
if (mx < 0 || my < 0 || mx >= fm.w || my >= fm.h)
|
|
34
|
+
continue;
|
|
35
|
+
const v = fm.data[my * fm.w + mx] / 255;
|
|
36
|
+
if (v < 0.25)
|
|
37
|
+
continue;
|
|
38
|
+
const deep = scale > 6;
|
|
39
|
+
t.char(deep ? text[(i * 3 + j * 5 + Math.floor(s * 3)) % text.length] : ramp.at(v));
|
|
40
|
+
const hue = (0.55 + p * 1.1 + x * 0.002 + Math.sin(s * 0.4 + y * 0.05) * 0.03) % 1;
|
|
41
|
+
const glow = 0.45 + 0.55 * v;
|
|
42
|
+
t.charColor(...hsv(hue, 0.6, glow));
|
|
43
|
+
t.cellColor(...hsv(hue, 0.85, 0.05 + 0.13 * v));
|
|
44
|
+
plot(t, x, y);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
void ptr;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { aspect, clamp01, createPointer, hsv, plot } from "../cells.js";
|
|
2
|
+
/** Viaje a velocidad de warp. El puntero orienta el punto de fuga; mantener pulsado activa el hiperespacio. */
|
|
3
|
+
export function starfield(t) {
|
|
4
|
+
const ptr = createPointer(t);
|
|
5
|
+
const COUNT = 1100;
|
|
6
|
+
const stars = Array.from({ length: COUNT }, () => make(true));
|
|
7
|
+
let boost = 0;
|
|
8
|
+
let cx = 0;
|
|
9
|
+
let cy = 0;
|
|
10
|
+
function make(initial) {
|
|
11
|
+
return {
|
|
12
|
+
x: (Math.random() * 2 - 1) * 1.6,
|
|
13
|
+
y: (Math.random() * 2 - 1) * 1.6,
|
|
14
|
+
z: initial ? Math.random() : 1,
|
|
15
|
+
hue: Math.random() < 0.7 ? 0.58 : 0.08,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
t.draw(() => {
|
|
19
|
+
ptr.update();
|
|
20
|
+
const asp = aspect(t);
|
|
21
|
+
const cols = t.grid.cols;
|
|
22
|
+
const rows = t.grid.rows;
|
|
23
|
+
boost += ((ptr.pressed ? 1 : 0) - boost) * 0.06;
|
|
24
|
+
cx += (ptr.x * 0.55 - cx) * 0.08;
|
|
25
|
+
cy += (ptr.y * 0.55 - cy) * 0.08;
|
|
26
|
+
const speed = 0.006 + boost * 0.05;
|
|
27
|
+
const sx = cols * 0.22;
|
|
28
|
+
const sy = (cols * 0.22) / asp;
|
|
29
|
+
const project = (s, z) => [
|
|
30
|
+
Math.round(cx + (s.x / z) * sx),
|
|
31
|
+
Math.round(cy + (s.y / z) * sy),
|
|
32
|
+
];
|
|
33
|
+
t.background(2, 3, 10);
|
|
34
|
+
t.lineWeight(1);
|
|
35
|
+
for (const s of stars) {
|
|
36
|
+
s.z -= speed;
|
|
37
|
+
const [px, py] = project(s, s.z);
|
|
38
|
+
if (s.z <= 0.03 || Math.abs(px) > cols || Math.abs(py) > rows) {
|
|
39
|
+
Object.assign(s, make(false));
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const [tx, ty] = project(s, s.z + speed * (2 + boost * 16));
|
|
43
|
+
const near = 1 - s.z;
|
|
44
|
+
t.char(near > 0.85 ? "@" : near > 0.65 ? "#" : near > 0.4 ? "*" : near > 0.2 ? "+" : ".");
|
|
45
|
+
t.charColor(...hsv(s.hue, 0.5 - near * 0.4, clamp01(0.25 + near * 0.9)));
|
|
46
|
+
t.cellColor(0, 0, 0, 0);
|
|
47
|
+
// En reposo basta un punto; las estelas solo aparecen al acelerar
|
|
48
|
+
if (boost < 0.12)
|
|
49
|
+
plot(t, px, py);
|
|
50
|
+
else
|
|
51
|
+
t.line(tx, ty, px, py);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Textmodifier } from "textmode.js";
|
|
2
|
+
/**
|
|
3
|
+
* Texto extruido en 3D: la máscara del texto se convierte en una nube de puntos con
|
|
4
|
+
* profundidad que gira. El puntero controla la inclinación; pulsar acelera el giro.
|
|
5
|
+
*/
|
|
6
|
+
export declare function tx3d(t: Textmodifier): void;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { aspect, createPointer, gridOrigin, hsv, plot } from "../cells.js";
|
|
2
|
+
import { getTextMask } from "../text/mask.js";
|
|
3
|
+
import { textStore } from "../text/store.js";
|
|
4
|
+
/**
|
|
5
|
+
* Texto extruido en 3D: la máscara del texto se convierte en una nube de puntos con
|
|
6
|
+
* profundidad que gira. El puntero controla la inclinación; pulsar acelera el giro.
|
|
7
|
+
*/
|
|
8
|
+
export function tx3d(t) {
|
|
9
|
+
const ptr = createPointer(t);
|
|
10
|
+
let pts = [];
|
|
11
|
+
let key = "";
|
|
12
|
+
let yaw = 0;
|
|
13
|
+
t.draw(() => {
|
|
14
|
+
ptr.update();
|
|
15
|
+
const s = t.secs;
|
|
16
|
+
const asp = aspect(t);
|
|
17
|
+
const { cols, rows } = gridOrigin(t);
|
|
18
|
+
const mask = getTextMask(textStore.text(), cols, rows, asp);
|
|
19
|
+
if (mask.key !== key) {
|
|
20
|
+
key = mask.key;
|
|
21
|
+
pts = [];
|
|
22
|
+
const depth = Math.max(4, Math.round(rows * 0.09));
|
|
23
|
+
const x0 = -Math.floor((cols - 1) / 2);
|
|
24
|
+
const y0 = -Math.floor(rows / 2);
|
|
25
|
+
for (let j = 0; j < rows; j++)
|
|
26
|
+
for (let i = 0; i < cols; i++)
|
|
27
|
+
if (mask.data[j * cols + i] > 140) {
|
|
28
|
+
for (const z of [-depth, 0, depth]) {
|
|
29
|
+
pts.push({ x: x0 + i, y: y0 + j, z });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (pts.length > 9000)
|
|
33
|
+
pts = pts.filter((_, k) => k % Math.ceil(pts.length / 9000) === 0);
|
|
34
|
+
}
|
|
35
|
+
// pulsar hace girar el texto; al soltar vuelve suavemente a la posición frontal
|
|
36
|
+
yaw = ptr.pressed ? yaw + 0.07 : yaw * 0.96;
|
|
37
|
+
const ay = yaw + Math.sin(s * 0.6) * 0.7 + (ptr.x / cols) * 1.0;
|
|
38
|
+
const ax = (ptr.y / rows) * 0.6;
|
|
39
|
+
const ca = Math.cos(ay), sa = Math.sin(ay), cx = Math.cos(ax), sx = Math.sin(ax);
|
|
40
|
+
const focal = cols * 1.6;
|
|
41
|
+
const out = [];
|
|
42
|
+
for (const q of pts) {
|
|
43
|
+
// giro en Y y luego en X
|
|
44
|
+
const x1 = q.x * ca + q.z * sa;
|
|
45
|
+
const z1 = -q.x * sa + q.z * ca;
|
|
46
|
+
const y2 = q.y * cx - z1 * sx;
|
|
47
|
+
const z2 = q.y * sx + z1 * cx;
|
|
48
|
+
const k = focal / (focal + z2);
|
|
49
|
+
out.push({ px: x1 * k, py: (y2 * k) / 1, z: z2, hue: (q.x / cols + 0.5 + s * 0.05) % 1 });
|
|
50
|
+
}
|
|
51
|
+
out.sort((a, b) => b.z - a.z); // de atrás hacia delante
|
|
52
|
+
t.background(4, 5, 12);
|
|
53
|
+
const near = -focal * 0.05;
|
|
54
|
+
for (const o of out) {
|
|
55
|
+
const f = Math.max(0, Math.min(1, (near - o.z) / (cols * 0.35) + 0.5));
|
|
56
|
+
t.char(f > 0.7 ? "#" : f > 0.4 ? "+" : ".");
|
|
57
|
+
t.charColor(...hsv(o.hue, 0.6, 0.3 + 0.7 * f));
|
|
58
|
+
t.cellColor(...hsv(o.hue, 0.8, 0.02 + 0.1 * f));
|
|
59
|
+
plot(t, Math.round(o.px), Math.round(o.py));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Textmodifier } from "textmode.js";
|
|
2
|
+
/**
|
|
3
|
+
* Combinación de ambas librerías: asciify-engine genera el arte de texto (FIGlet o
|
|
4
|
+
* bitmap 7×7) y textmode.js lo dibuja carácter a carácter con onda y ondas de hover.
|
|
5
|
+
*/
|
|
6
|
+
export declare function txComboFiglet(t: Textmodifier): void;
|