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,437 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escenas temáticas para las landings. Cada una es una imagen procedural que cambia
|
|
3
|
+
* con el progreso 0–1 que recibe como sexto argumento (`s.p`; si no se pasa, el global
|
|
4
|
+
* `scrollState` de las demos): así la transición entre secciones es una metamorfosis de la
|
|
5
|
+
* propia escena y no un simple fundido.
|
|
6
|
+
*/
|
|
7
|
+
import { clamp, scrollState, smooth, span } from "../scroll/store.js";
|
|
8
|
+
const TAU = Math.PI * 2;
|
|
9
|
+
const mixc = (a, b, k) => [a[0] + (b[0] - a[0]) * k, a[1] + (b[1] - a[1]) * k, a[2] + (b[2] - a[2]) * k];
|
|
10
|
+
const css = (c, a = 1) => `rgba(${c[0] | 0},${c[1] | 0},${c[2] | 0},${a})`;
|
|
11
|
+
/** Interpola entre fotogramas clave repartidos uniformemente en 0..1. */
|
|
12
|
+
function keyed(frames, p) {
|
|
13
|
+
const f = clamp(p) * (frames.length - 1);
|
|
14
|
+
const i = Math.min(frames.length - 2, Math.floor(f));
|
|
15
|
+
return mixc(frames[i], frames[i + 1], smooth(f - i));
|
|
16
|
+
}
|
|
17
|
+
/** Pseudo-aleatorio determinista 0..1. */
|
|
18
|
+
const rnd = (n) => {
|
|
19
|
+
const x = Math.sin(n * 127.1 + 311.7) * 43758.5453;
|
|
20
|
+
return x - Math.floor(x);
|
|
21
|
+
};
|
|
22
|
+
/** Surf: del amanecer a la noche, con el sol (y luego la luna) cruzando sobre olas en capas. */
|
|
23
|
+
const tide = () => {
|
|
24
|
+
const SKY_TOP = [[43, 27, 77], [47, 143, 216], [58, 28, 113], [5, 10, 37]];
|
|
25
|
+
const SKY_LOW = [[255, 154, 107], [191, 232, 255], [255, 106, 77], [20, 41, 90]];
|
|
26
|
+
return (ctx, w, h, t, ptr, sp = scrollState) => {
|
|
27
|
+
const p = sp.p;
|
|
28
|
+
const top = keyed(SKY_TOP, p);
|
|
29
|
+
const low = keyed(SKY_LOW, p);
|
|
30
|
+
const hz = h * 0.56;
|
|
31
|
+
const sky = ctx.createLinearGradient(0, 0, 0, hz);
|
|
32
|
+
sky.addColorStop(0, css(top));
|
|
33
|
+
sky.addColorStop(1, css(low));
|
|
34
|
+
ctx.globalCompositeOperation = "source-over";
|
|
35
|
+
ctx.fillStyle = sky;
|
|
36
|
+
ctx.fillRect(0, 0, w, h);
|
|
37
|
+
// estrellas al caer la noche
|
|
38
|
+
const night = smooth(span(p, 0.72, 0.95));
|
|
39
|
+
if (night > 0.02) {
|
|
40
|
+
for (let i = 0; i < 90; i++) {
|
|
41
|
+
const tw = 0.5 + 0.5 * Math.sin(t * 2 + i);
|
|
42
|
+
ctx.fillStyle = `rgba(255,255,255,${night * tw})`;
|
|
43
|
+
ctx.fillRect(rnd(i) * w, rnd(i + 99) * hz * 0.9, 2, 2);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// sol → luna, en arco
|
|
47
|
+
const arcT = clamp(p * 1.05);
|
|
48
|
+
const sx = w * (0.12 + 0.76 * arcT) + (ptr.x - 0.5) * w * 0.05;
|
|
49
|
+
const sy = hz - Math.sin(arcT * Math.PI) * h * 0.42;
|
|
50
|
+
const moon = smooth(span(p, 0.74, 0.9));
|
|
51
|
+
const sunCol = mixc([255, 226, 140], [235, 240, 255], moon);
|
|
52
|
+
const glow = ctx.createRadialGradient(sx, sy, 0, sx, sy, h * 0.35);
|
|
53
|
+
glow.addColorStop(0, css(sunCol, 0.9));
|
|
54
|
+
glow.addColorStop(0.25, css(sunCol, 0.35));
|
|
55
|
+
glow.addColorStop(1, css(sunCol, 0));
|
|
56
|
+
ctx.globalCompositeOperation = "lighter";
|
|
57
|
+
ctx.fillStyle = glow;
|
|
58
|
+
ctx.fillRect(0, 0, w, hz + h * 0.1);
|
|
59
|
+
ctx.globalCompositeOperation = "source-over";
|
|
60
|
+
ctx.fillStyle = css(sunCol);
|
|
61
|
+
ctx.beginPath();
|
|
62
|
+
ctx.arc(sx, sy, h * (0.06 - moon * 0.015), 0, TAU);
|
|
63
|
+
ctx.fill();
|
|
64
|
+
// olas en capas
|
|
65
|
+
const deep = keyed([[16, 110, 130], [20, 130, 170], [70, 40, 110], [8, 24, 60]], p);
|
|
66
|
+
for (let i = 0; i < 6; i++) {
|
|
67
|
+
const y0 = hz + i * h * 0.075;
|
|
68
|
+
const amp = h * (0.012 + i * 0.008);
|
|
69
|
+
const col = mixc(mixc(low, deep, 0.5 + i * 0.1), [0, 8, 20], i * 0.12);
|
|
70
|
+
ctx.fillStyle = css(col);
|
|
71
|
+
ctx.beginPath();
|
|
72
|
+
ctx.moveTo(0, h);
|
|
73
|
+
for (let x = 0; x <= w; x += 6) {
|
|
74
|
+
const bump = Math.exp(-(((x / w - ptr.x) * 5) ** 2)) * h * 0.03 * Math.sin(t * 3 + i);
|
|
75
|
+
ctx.lineTo(x, y0 + Math.sin(x * 0.02 * (1 + i * 0.1) + t * (0.7 + i * 0.2) + i) * amp - bump);
|
|
76
|
+
}
|
|
77
|
+
ctx.lineTo(w, h);
|
|
78
|
+
ctx.fill();
|
|
79
|
+
}
|
|
80
|
+
// brillo del sol sobre el agua
|
|
81
|
+
ctx.globalCompositeOperation = "lighter";
|
|
82
|
+
for (let i = 0; i < 18; i++) {
|
|
83
|
+
const yy = hz + 6 + i * (h * 0.02);
|
|
84
|
+
const half = (8 + i * 6) * (0.6 + 0.4 * Math.sin(t * 2 + i));
|
|
85
|
+
ctx.fillStyle = css(sunCol, 0.35 * (1 - i / 18));
|
|
86
|
+
ctx.fillRect(sx - half, yy, half * 2, 2);
|
|
87
|
+
}
|
|
88
|
+
ctx.globalCompositeOperation = "source-over";
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
/** Tostador: granos → vapor → latte art, con fundidos entre las tres escenas. */
|
|
92
|
+
const brew = () => (ctx, w, h, t, ptr, sp = scrollState) => {
|
|
93
|
+
const p = sp.p;
|
|
94
|
+
const bg = ctx.createRadialGradient(w / 2, h / 2, 0, w / 2, h / 2, Math.max(w, h) * 0.7);
|
|
95
|
+
bg.addColorStop(0, "#3a2314");
|
|
96
|
+
bg.addColorStop(1, "#120a06");
|
|
97
|
+
ctx.globalCompositeOperation = "source-over";
|
|
98
|
+
ctx.globalAlpha = 1;
|
|
99
|
+
ctx.fillStyle = bg;
|
|
100
|
+
ctx.fillRect(0, 0, w, h);
|
|
101
|
+
// 1) granos en rejilla, que giran
|
|
102
|
+
const beans = 1 - smooth(span(p, 0.25, 0.45));
|
|
103
|
+
if (beans > 0.01) {
|
|
104
|
+
ctx.globalAlpha = beans;
|
|
105
|
+
const cw = w / 12;
|
|
106
|
+
for (let j = -1; j < 8; j++) {
|
|
107
|
+
for (let i = -1; i < 13; i++) {
|
|
108
|
+
const x = (i + (j % 2 ? 0.5 : 0)) * cw;
|
|
109
|
+
const y = j * cw * 0.85;
|
|
110
|
+
const rot = Math.sin(t * 0.4 + i * 1.3 + j * 0.7) * 0.4 + i + j;
|
|
111
|
+
const near = Math.exp(-((((x / w - ptr.x) ** 2 + ((y / h - ptr.y) * 0.6) ** 2)) * 14));
|
|
112
|
+
ctx.save();
|
|
113
|
+
ctx.translate(x, y);
|
|
114
|
+
ctx.rotate(rot);
|
|
115
|
+
ctx.fillStyle = `rgb(${70 + near * 130},${40 + near * 70},${20 + near * 30})`;
|
|
116
|
+
ctx.beginPath();
|
|
117
|
+
ctx.ellipse(0, 0, cw * 0.42, cw * 0.3, 0, 0, TAU);
|
|
118
|
+
ctx.fill();
|
|
119
|
+
ctx.strokeStyle = `rgba(20,10,5,0.9)`;
|
|
120
|
+
ctx.lineWidth = cw * 0.05;
|
|
121
|
+
ctx.beginPath();
|
|
122
|
+
ctx.moveTo(-cw * 0.38, 0);
|
|
123
|
+
ctx.quadraticCurveTo(0, cw * 0.14, cw * 0.38, 0);
|
|
124
|
+
ctx.stroke();
|
|
125
|
+
ctx.restore();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// 2) vapor que sube sobre una taza vista de perfil
|
|
130
|
+
const steam = smooth(span(p, 0.3, 0.45)) * (1 - smooth(span(p, 0.62, 0.78)));
|
|
131
|
+
if (steam > 0.01) {
|
|
132
|
+
ctx.globalAlpha = steam;
|
|
133
|
+
ctx.fillStyle = "#f2e2c8";
|
|
134
|
+
ctx.beginPath();
|
|
135
|
+
ctx.roundRect(w * 0.36, h * 0.62, w * 0.28, h * 0.26, [8, 8, h * 0.13, h * 0.13]);
|
|
136
|
+
ctx.fill();
|
|
137
|
+
ctx.lineWidth = w * 0.02;
|
|
138
|
+
ctx.strokeStyle = "#f2e2c8";
|
|
139
|
+
ctx.beginPath();
|
|
140
|
+
ctx.arc(w * 0.66, h * 0.73, h * 0.08, -Math.PI / 2, Math.PI / 2);
|
|
141
|
+
ctx.stroke();
|
|
142
|
+
ctx.lineCap = "round";
|
|
143
|
+
for (let k = 0; k < 5; k++) {
|
|
144
|
+
ctx.strokeStyle = `rgba(255,240,220,${0.35 - k * 0.04})`;
|
|
145
|
+
ctx.lineWidth = w * (0.018 + k * 0.004);
|
|
146
|
+
ctx.beginPath();
|
|
147
|
+
for (let s = 0; s <= 30; s++) {
|
|
148
|
+
const u = s / 30;
|
|
149
|
+
const x = w * (0.42 + k * 0.04) + Math.sin(u * 7 - t * 1.4 + k) * w * 0.03 * (0.4 + u) + (ptr.x - 0.5) * w * 0.05 * u;
|
|
150
|
+
const y = h * 0.6 - u * h * 0.5;
|
|
151
|
+
if (s === 0)
|
|
152
|
+
ctx.moveTo(x, y);
|
|
153
|
+
else
|
|
154
|
+
ctx.lineTo(x, y);
|
|
155
|
+
}
|
|
156
|
+
ctx.stroke();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// 3) latte art: anillos que se deforman en una hoja
|
|
160
|
+
const art = smooth(span(p, 0.6, 0.8));
|
|
161
|
+
if (art > 0.01) {
|
|
162
|
+
ctx.globalAlpha = art;
|
|
163
|
+
const cx = w / 2 + (ptr.x - 0.5) * w * 0.04;
|
|
164
|
+
const cy = h / 2;
|
|
165
|
+
const R = h * 0.42;
|
|
166
|
+
ctx.fillStyle = "#5a3520";
|
|
167
|
+
ctx.beginPath();
|
|
168
|
+
ctx.arc(cx, cy, R, 0, TAU);
|
|
169
|
+
ctx.fill();
|
|
170
|
+
for (let i = 9; i >= 1; i--) {
|
|
171
|
+
const r = (R * i) / 9;
|
|
172
|
+
ctx.fillStyle = i % 2 ? "#f3e3c6" : "#8a5a37";
|
|
173
|
+
ctx.beginPath();
|
|
174
|
+
for (let a = 0; a <= 64; a++) {
|
|
175
|
+
const th = (a / 64) * TAU;
|
|
176
|
+
const wob = 1 + 0.16 * Math.sin(th * 6 + t * 0.6 + i * 0.5) * (1 - i / 10) - 0.22 * Math.max(0, Math.cos(th - Math.PI / 2)) * (i / 9);
|
|
177
|
+
const x = cx + Math.cos(th) * r * wob;
|
|
178
|
+
const y = cy + Math.sin(th) * r * wob * 0.92;
|
|
179
|
+
if (a === 0)
|
|
180
|
+
ctx.moveTo(x, y);
|
|
181
|
+
else
|
|
182
|
+
ctx.lineTo(x, y);
|
|
183
|
+
}
|
|
184
|
+
ctx.fill();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
ctx.globalAlpha = 1;
|
|
188
|
+
};
|
|
189
|
+
/** Botánica: enredaderas que crecen con el scroll y acaban floreciendo. */
|
|
190
|
+
const bloom = () => {
|
|
191
|
+
return (ctx, w, h, t, ptr, sp = scrollState) => {
|
|
192
|
+
const p = sp.p;
|
|
193
|
+
const bg = ctx.createLinearGradient(0, 0, 0, h);
|
|
194
|
+
bg.addColorStop(0, "#0a1f14");
|
|
195
|
+
bg.addColorStop(1, "#04100a");
|
|
196
|
+
ctx.globalCompositeOperation = "source-over";
|
|
197
|
+
ctx.fillStyle = bg;
|
|
198
|
+
ctx.fillRect(0, 0, w, h);
|
|
199
|
+
const growth = 0.4 + p * 0.95;
|
|
200
|
+
const flowers = smooth(span(p, 0.55, 0.85));
|
|
201
|
+
const sway = (ptr.x - 0.5) * 0.35;
|
|
202
|
+
ctx.lineCap = "round";
|
|
203
|
+
const grow = (tw, g) => {
|
|
204
|
+
if (g <= 0 || tw.depth > 6)
|
|
205
|
+
return;
|
|
206
|
+
const len = tw.len * Math.min(1, g);
|
|
207
|
+
const bend = Math.sin(t * 0.7 + tw.id) * 0.06 + sway * 0.15 * (tw.depth + 1) * 0.3;
|
|
208
|
+
const a2 = tw.a + bend;
|
|
209
|
+
const ex = tw.x + Math.cos(a2) * len;
|
|
210
|
+
const ey = tw.y + Math.sin(a2) * len;
|
|
211
|
+
const mx = tw.x + Math.cos(tw.a - 0.25) * len * 0.5;
|
|
212
|
+
const my = tw.y + Math.sin(tw.a - 0.25) * len * 0.5;
|
|
213
|
+
ctx.strokeStyle = `hsl(${120 + tw.depth * 8},${60 + tw.depth * 4}%,${34 + tw.depth * 6}%)`;
|
|
214
|
+
ctx.lineWidth = Math.max(2, (9 - tw.depth) * (w / 560));
|
|
215
|
+
ctx.beginPath();
|
|
216
|
+
ctx.moveTo(tw.x, tw.y);
|
|
217
|
+
ctx.quadraticCurveTo(mx, my, ex, ey);
|
|
218
|
+
ctx.stroke();
|
|
219
|
+
if (g < 1)
|
|
220
|
+
return;
|
|
221
|
+
// hoja
|
|
222
|
+
if (tw.depth > 1) {
|
|
223
|
+
ctx.fillStyle = `hsl(${105 + rnd(tw.id) * 40},65%,${34 + rnd(tw.id + 3) * 16}%)`;
|
|
224
|
+
ctx.beginPath();
|
|
225
|
+
ctx.ellipse(ex, ey, len * 0.28, len * 0.12, a2 + 1.2, 0, TAU);
|
|
226
|
+
ctx.fill();
|
|
227
|
+
}
|
|
228
|
+
const g2 = g - 1;
|
|
229
|
+
for (let k = 0; k < 2; k++) {
|
|
230
|
+
const da = (k ? 1 : -1) * (0.45 + rnd(tw.id + k) * 0.4);
|
|
231
|
+
grow({ x: ex, y: ey, a: a2 + da, len: tw.len * 0.74, depth: tw.depth + 1, id: tw.id * 2 + k + 1 }, g2);
|
|
232
|
+
}
|
|
233
|
+
// flor en las puntas
|
|
234
|
+
if (tw.depth >= 5 && flowers > 0.02) {
|
|
235
|
+
for (let pet = 0; pet < 6; pet++) {
|
|
236
|
+
const pa = (pet / 6) * TAU + t * 0.3;
|
|
237
|
+
ctx.fillStyle = `hsla(${330 + rnd(tw.id) * 40},85%,${62 + flowers * 10}%,${flowers})`;
|
|
238
|
+
ctx.beginPath();
|
|
239
|
+
ctx.ellipse(ex + Math.cos(pa) * 6 * flowers, ey + Math.sin(pa) * 6 * flowers, 5 * flowers, 3 * flowers, pa, 0, TAU);
|
|
240
|
+
ctx.fill();
|
|
241
|
+
}
|
|
242
|
+
ctx.fillStyle = `rgba(255,220,120,${flowers})`;
|
|
243
|
+
ctx.beginPath();
|
|
244
|
+
ctx.arc(ex, ey, 2.4 * flowers, 0, TAU);
|
|
245
|
+
ctx.fill();
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
const roots = [0.28, 0.5, 0.72];
|
|
249
|
+
roots.forEach((rx, i) => grow({ x: w * rx, y: h + 4, a: -Math.PI / 2 + (i - 1) * 0.12, len: h * 0.24, depth: 0, id: i + 1 }, growth * 5.2));
|
|
250
|
+
};
|
|
251
|
+
};
|
|
252
|
+
/** Arcade: bloques que caen → invasores → "INSERT COIN", en píxel grueso. */
|
|
253
|
+
const arcade = () => {
|
|
254
|
+
const INV = ["00111100", "01111110", "11011011", "11111111", "00100100", "01011010"];
|
|
255
|
+
return (ctx, w, h, t, ptr, sp = scrollState) => {
|
|
256
|
+
const p = sp.p;
|
|
257
|
+
const px = Math.max(4, Math.round(w / 80));
|
|
258
|
+
const cols = Math.floor(w / px);
|
|
259
|
+
const rows = Math.floor(h / px);
|
|
260
|
+
const PAL = ["#0f380f", "#306230", "#8bac0f", "#9bbc0f"];
|
|
261
|
+
ctx.globalCompositeOperation = "source-over";
|
|
262
|
+
ctx.fillStyle = PAL[0];
|
|
263
|
+
ctx.fillRect(0, 0, w, h);
|
|
264
|
+
const cell = (x, y, c) => {
|
|
265
|
+
ctx.fillStyle = PAL[c];
|
|
266
|
+
ctx.fillRect(x * px, y * px, px - 1, px - 1);
|
|
267
|
+
};
|
|
268
|
+
// rejilla tenue
|
|
269
|
+
for (let y = 0; y < rows; y += 4)
|
|
270
|
+
for (let x = 0; x < cols; x += 4)
|
|
271
|
+
cell(x, y, 1);
|
|
272
|
+
// 1) bloques que caen
|
|
273
|
+
const blocks = 1 - smooth(span(p, 0.3, 0.42));
|
|
274
|
+
if (blocks > 0.02) {
|
|
275
|
+
for (let i = 0; i < 14; i++) {
|
|
276
|
+
const col = Math.floor(rnd(i) * (cols - 4));
|
|
277
|
+
const fall = (t * (3 + rnd(i + 9) * 4) + rnd(i + 3) * rows) % (rows + 6);
|
|
278
|
+
const shape = [[0, 0], [1, 0], [2, 0], [1, 1]];
|
|
279
|
+
const rot = Math.floor(rnd(i + 5) * 2);
|
|
280
|
+
for (const [dx, dy] of shape)
|
|
281
|
+
cell(col + (rot ? dy : dx), Math.floor(fall - 4 + (rot ? dx : dy)), 2 + (i % 2));
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// 2) invasores marchando y nave que sigue al puntero
|
|
285
|
+
const inv = smooth(span(p, 0.28, 0.42)) * (1 - smooth(span(p, 0.68, 0.8)));
|
|
286
|
+
if (inv > 0.02) {
|
|
287
|
+
const step = Math.floor(t * 2);
|
|
288
|
+
const ox = 6 + ((step % 10) - (step % 10 > 5 ? 2 * (step % 10 - 5) : 0)) * 2;
|
|
289
|
+
for (let r = 0; r < 4; r++) {
|
|
290
|
+
for (let c = 0; c < 6; c++) {
|
|
291
|
+
const sx = ox + c * 11;
|
|
292
|
+
const sy = 5 + r * 8 + Math.floor(inv * 0);
|
|
293
|
+
INV.forEach((row, yy) => [...row].forEach((v, xx) => v === "1" && cell(sx + xx, sy + yy, 2 + (r % 2))));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const shipX = Math.floor(ptr.x * (cols - 9));
|
|
297
|
+
for (let xx = 0; xx < 9; xx++)
|
|
298
|
+
cell(shipX + xx, rows - 4, 3);
|
|
299
|
+
for (let xx = 3; xx < 6; xx++)
|
|
300
|
+
cell(shipX + xx, rows - 5, 3);
|
|
301
|
+
cell(shipX + 4, rows - 6, 3);
|
|
302
|
+
const bulletY = rows - 8 - ((t * 20) % (rows - 10));
|
|
303
|
+
cell(shipX + 4, Math.floor(bulletY), 3);
|
|
304
|
+
}
|
|
305
|
+
// 3) INSERT COIN
|
|
306
|
+
const coin = smooth(span(p, 0.68, 0.82));
|
|
307
|
+
if (coin > 0.02) {
|
|
308
|
+
ctx.globalAlpha = coin;
|
|
309
|
+
ctx.fillStyle = PAL[3];
|
|
310
|
+
ctx.font = `bold ${px * 7}px monospace`;
|
|
311
|
+
ctx.textAlign = "center";
|
|
312
|
+
ctx.fillText("INSERT", w / 2, h * 0.42);
|
|
313
|
+
ctx.fillText("COIN", w / 2, h * 0.42 + px * 9);
|
|
314
|
+
if (Math.floor(t * 2) % 2 === 0) {
|
|
315
|
+
ctx.font = `bold ${px * 3}px monospace`;
|
|
316
|
+
ctx.fillStyle = PAL[2];
|
|
317
|
+
ctx.fillText("PRESS START", w / 2, h * 0.86);
|
|
318
|
+
}
|
|
319
|
+
ctx.globalAlpha = 1;
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
};
|
|
323
|
+
/**
|
|
324
|
+
* Ola de surf en corte lateral: series de olas asimétricas (espalda suave, cara empinada) que avanzan
|
|
325
|
+
* hacia la derecha, espuma en la cresta, gotas de rocío y un surfista bajando por la cara de la ola
|
|
326
|
+
* delantera. Con `progress` el oleaje crece (mar plana → serie grande) y el cielo va del amanecer a la noche.
|
|
327
|
+
*/
|
|
328
|
+
const wave = () => {
|
|
329
|
+
const SKY_TOP = [[43, 27, 77], [47, 143, 216], [58, 28, 113], [5, 10, 37]];
|
|
330
|
+
const SKY_LOW = [[255, 154, 107], [191, 232, 255], [255, 106, 77], [20, 41, 90]];
|
|
331
|
+
const SEA_TOP = [[90, 200, 190], [80, 210, 220], [120, 90, 160], [30, 80, 120]];
|
|
332
|
+
const SEA_DEEP = [[6, 40, 70], [8, 60, 100], [20, 20, 60], [2, 10, 30]];
|
|
333
|
+
/** Perfil de una ola: espalda que sube despacio y cara que cae en seco tras la cresta (u en 0..1). */
|
|
334
|
+
const prof = (u) => {
|
|
335
|
+
u -= Math.floor(u);
|
|
336
|
+
return u < 0.8 ? Math.pow(u / 0.8, 2.2) : 1 - smooth((u - 0.8) / 0.2);
|
|
337
|
+
};
|
|
338
|
+
return (ctx, w, h, t, ptr, sp = scrollState) => {
|
|
339
|
+
const p = sp.p;
|
|
340
|
+
const size = 0.65 + 0.6 * smooth(span(p, 0.05, 0.75));
|
|
341
|
+
const top = keyed(SKY_TOP, p);
|
|
342
|
+
const low = keyed(SKY_LOW, p);
|
|
343
|
+
const hz = h * 0.42;
|
|
344
|
+
ctx.globalCompositeOperation = "source-over";
|
|
345
|
+
ctx.globalAlpha = 1;
|
|
346
|
+
const sky = ctx.createLinearGradient(0, 0, 0, hz + h * 0.1);
|
|
347
|
+
sky.addColorStop(0, css(top));
|
|
348
|
+
sky.addColorStop(1, css(low));
|
|
349
|
+
ctx.fillStyle = sky;
|
|
350
|
+
ctx.fillRect(0, 0, w, h);
|
|
351
|
+
// sol o luna
|
|
352
|
+
const night = smooth(span(p, 0.72, 0.92));
|
|
353
|
+
const arcT = clamp(p * 1.05);
|
|
354
|
+
const sx = w * (0.15 + 0.7 * arcT);
|
|
355
|
+
const sy = hz - Math.sin(arcT * Math.PI) * h * 0.3;
|
|
356
|
+
const sunCol = mixc([255, 226, 140], [235, 240, 255], night);
|
|
357
|
+
const glow = ctx.createRadialGradient(sx, sy, 0, sx, sy, h * 0.3);
|
|
358
|
+
glow.addColorStop(0, css(sunCol, 0.8));
|
|
359
|
+
glow.addColorStop(1, css(sunCol, 0));
|
|
360
|
+
ctx.fillStyle = glow;
|
|
361
|
+
ctx.fillRect(0, 0, w, hz + h * 0.1);
|
|
362
|
+
ctx.fillStyle = css(sunCol);
|
|
363
|
+
ctx.beginPath();
|
|
364
|
+
ctx.arc(sx, sy, h * 0.05, 0, TAU);
|
|
365
|
+
ctx.fill();
|
|
366
|
+
const seaTop = keyed(SEA_TOP, p);
|
|
367
|
+
const seaDeep = keyed(SEA_DEEP, p);
|
|
368
|
+
const LAYERS = 5;
|
|
369
|
+
const surface = (i, x) => {
|
|
370
|
+
const k = i / (LAYERS - 1);
|
|
371
|
+
const lambda = w * (0.42 + k * 0.5);
|
|
372
|
+
const amp = h * (0.03 + k * k * 0.2) * size;
|
|
373
|
+
const base = hz + h * (0.06 + k * 0.42);
|
|
374
|
+
const speed = 0.06 + k * 0.05;
|
|
375
|
+
return base - amp * prof(x / lambda - t * speed + i * 0.37) + Math.sin(x * 0.05 + t * 1.4 + i) * h * 0.004;
|
|
376
|
+
};
|
|
377
|
+
for (let i = 0; i < LAYERS; i++) {
|
|
378
|
+
const k = i / (LAYERS - 1);
|
|
379
|
+
const col = mixc(seaDeep, seaTop, 0.25 + k * 0.6);
|
|
380
|
+
const yTop = hz + h * 0.02;
|
|
381
|
+
const g = ctx.createLinearGradient(0, yTop, 0, h);
|
|
382
|
+
g.addColorStop(0, css(mixc(col, seaTop, 0.5)));
|
|
383
|
+
g.addColorStop(1, css(mixc(col, seaDeep, 0.6)));
|
|
384
|
+
ctx.fillStyle = g;
|
|
385
|
+
ctx.beginPath();
|
|
386
|
+
ctx.moveTo(0, h);
|
|
387
|
+
for (let x = 0; x <= w + 4; x += 4)
|
|
388
|
+
ctx.lineTo(x, surface(i, x));
|
|
389
|
+
ctx.lineTo(w, h);
|
|
390
|
+
ctx.fill();
|
|
391
|
+
// espuma: banda clara sobre la cresta y donde la cara cae en seco
|
|
392
|
+
const foam = k > 0.2;
|
|
393
|
+
if (foam) {
|
|
394
|
+
ctx.fillStyle = css([240, 250, 255], 0.55 + k * 0.4);
|
|
395
|
+
for (let x = 0; x <= w; x += 4) {
|
|
396
|
+
const y = surface(i, x);
|
|
397
|
+
const slope = y - surface(i, x + 6);
|
|
398
|
+
if (slope > h * 0.006 * (1.4 - k))
|
|
399
|
+
ctx.fillRect(x, y - 1, 5, h * 0.012 + slope * 0.6);
|
|
400
|
+
}
|
|
401
|
+
// rocío que sale de las crestas
|
|
402
|
+
ctx.fillStyle = css([255, 255, 255], 0.8);
|
|
403
|
+
for (let n = 0; n < 26; n++) {
|
|
404
|
+
const x = rnd(n * 7 + i * 131) * w;
|
|
405
|
+
const life = (t * 0.7 + rnd(n + i * 17)) % 1;
|
|
406
|
+
const y = surface(i, x) - life * h * 0.09 * size + life * life * h * 0.06;
|
|
407
|
+
const slope = y - surface(i, x + 6);
|
|
408
|
+
if (slope > -h)
|
|
409
|
+
ctx.fillRect(x, y - h * 0.01, 3, 3);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
// surfista en la cara de la ola delantera
|
|
414
|
+
const last = LAYERS - 1;
|
|
415
|
+
const rx = w * (0.4 + (ptr.x - 0.5) * 0.2);
|
|
416
|
+
const ry = surface(last, rx);
|
|
417
|
+
const ang = Math.atan2(surface(last, rx + 12) - surface(last, rx - 12), 24);
|
|
418
|
+
const s = h * 0.05;
|
|
419
|
+
ctx.save();
|
|
420
|
+
ctx.translate(rx, ry - s * 0.15);
|
|
421
|
+
ctx.rotate(ang * 0.8);
|
|
422
|
+
ctx.fillStyle = css([12, 16, 24]);
|
|
423
|
+
ctx.beginPath(); // tabla
|
|
424
|
+
ctx.ellipse(0, 0, s * 1.5, s * 0.16, 0, 0, TAU);
|
|
425
|
+
ctx.fill();
|
|
426
|
+
ctx.fillRect(-s * 0.2, -s * 1.15, s * 0.42, s * 0.75); // torso
|
|
427
|
+
ctx.fillRect(-s * 0.55, -s * 0.4, s * 0.32, s * 0.4); // pierna trasera
|
|
428
|
+
ctx.fillRect(s * 0.2, -s * 0.4, s * 0.32, s * 0.4); // pierna delantera
|
|
429
|
+
ctx.beginPath(); // cabeza
|
|
430
|
+
ctx.arc(0, -s * 1.4, s * 0.2, 0, TAU);
|
|
431
|
+
ctx.fill();
|
|
432
|
+
ctx.fillRect(-s * 0.9, -s * 1.05, s * 0.7, s * 0.14); // brazos abiertos
|
|
433
|
+
ctx.fillRect(s * 0.2, -s * 1.05, s * 0.7, s * 0.14);
|
|
434
|
+
ctx.restore();
|
|
435
|
+
};
|
|
436
|
+
};
|
|
437
|
+
export const themePainters = { tide, wave, brew, bloom, arcade };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Textmodifier } from "textmode.js";
|
|
2
|
+
export type RGB = [number, number, number];
|
|
3
|
+
/** h, s, v en 0..1 → [r, g, b] en 0..255 */
|
|
4
|
+
export declare function hsv(h: number, s: number, v: number): RGB;
|
|
5
|
+
export declare const clamp01: (v: number) => number;
|
|
6
|
+
export declare const mix: (a: number, b: number, k: number) => number;
|
|
7
|
+
export declare const smoothstep: (a: number, b: number, v: number) => number;
|
|
8
|
+
/** Hash determinista 0..1 para una posición de celda. */
|
|
9
|
+
export declare function hash2(x: number, y: number): number;
|
|
10
|
+
/** Relación alto/ancho de una celda (≈ 2). Sirve para corregir distancias. */
|
|
11
|
+
export declare const aspect: (t: Textmodifier) => number;
|
|
12
|
+
/** Rango de coordenadas de celda (origen en el centro de la rejilla). */
|
|
13
|
+
export declare function gridOrigin(t: Textmodifier): {
|
|
14
|
+
cols: number;
|
|
15
|
+
rows: number;
|
|
16
|
+
x0: number;
|
|
17
|
+
y0: number;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Recorre todas las celdas de la rejilla. El callback puede devolver `false`
|
|
21
|
+
* para no dibujar la celda. Cada celda se dibuja con `point()` usando el estado
|
|
22
|
+
* (char, colores) que el callback haya fijado.
|
|
23
|
+
*/
|
|
24
|
+
export declare function eachCell(t: Textmodifier, fn: (x: number, y: number, i: number, j: number) => boolean | void): void;
|
|
25
|
+
/** Dibuja una sola celda en coordenadas de rejilla. */
|
|
26
|
+
export declare function plot(t: Textmodifier, x: number, y: number): void;
|
|
27
|
+
/**
|
|
28
|
+
* Puntero suavizado. Usa el ratón o el primer toque; si no hay ninguno sobre el
|
|
29
|
+
* lienzo, vaga por la pantalla en piloto automático para que el fondo siga vivo.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createPointer(t: Textmodifier): {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
vx: number;
|
|
35
|
+
vy: number;
|
|
36
|
+
/** true si hay un puntero real sobre el lienzo */
|
|
37
|
+
hovering: boolean;
|
|
38
|
+
pressed: boolean;
|
|
39
|
+
/** celda exacta bajo el puntero (sin suavizar) o null */
|
|
40
|
+
raw: {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
} | null;
|
|
44
|
+
update(): void;
|
|
45
|
+
};
|
|
46
|
+
export type Pointer = ReturnType<typeof createPointer>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/** h, s, v en 0..1 → [r, g, b] en 0..255 */
|
|
2
|
+
export function hsv(h, s, v) {
|
|
3
|
+
h = ((h % 1) + 1) % 1;
|
|
4
|
+
const i = Math.floor(h * 6);
|
|
5
|
+
const f = h * 6 - i;
|
|
6
|
+
const p = v * (1 - s);
|
|
7
|
+
const q = v * (1 - f * s);
|
|
8
|
+
const u = v * (1 - (1 - f) * s);
|
|
9
|
+
const [r, g, b] = [
|
|
10
|
+
[v, u, p],
|
|
11
|
+
[q, v, p],
|
|
12
|
+
[p, v, u],
|
|
13
|
+
[p, q, v],
|
|
14
|
+
[u, p, v],
|
|
15
|
+
[v, p, q],
|
|
16
|
+
][i % 6];
|
|
17
|
+
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
|
|
18
|
+
}
|
|
19
|
+
export const clamp01 = (v) => (v < 0 ? 0 : v > 1 ? 1 : v);
|
|
20
|
+
export const mix = (a, b, k) => a + (b - a) * k;
|
|
21
|
+
export const smoothstep = (a, b, v) => {
|
|
22
|
+
const k = clamp01((v - a) / (b - a));
|
|
23
|
+
return k * k * (3 - 2 * k);
|
|
24
|
+
};
|
|
25
|
+
/** Hash determinista 0..1 para una posición de celda. */
|
|
26
|
+
export function hash2(x, y) {
|
|
27
|
+
const n = Math.sin(x * 127.1 + y * 311.7) * 43758.5453;
|
|
28
|
+
return n - Math.floor(n);
|
|
29
|
+
}
|
|
30
|
+
/** Relación alto/ancho de una celda (≈ 2). Sirve para corregir distancias. */
|
|
31
|
+
export const aspect = (t) => t.grid.cellHeight / t.grid.cellWidth;
|
|
32
|
+
/** Rango de coordenadas de celda (origen en el centro de la rejilla). */
|
|
33
|
+
export function gridOrigin(t) {
|
|
34
|
+
const cols = t.grid.cols;
|
|
35
|
+
const rows = t.grid.rows;
|
|
36
|
+
return {
|
|
37
|
+
cols,
|
|
38
|
+
rows,
|
|
39
|
+
x0: -Math.floor((cols - 1) / 2),
|
|
40
|
+
y0: -Math.floor(rows / 2),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Recorre todas las celdas de la rejilla. El callback puede devolver `false`
|
|
45
|
+
* para no dibujar la celda. Cada celda se dibuja con `point()` usando el estado
|
|
46
|
+
* (char, colores) que el callback haya fijado.
|
|
47
|
+
*/
|
|
48
|
+
export function eachCell(t, fn) {
|
|
49
|
+
const { cols, rows, x0, y0 } = gridOrigin(t);
|
|
50
|
+
for (let j = 0; j < rows; j++) {
|
|
51
|
+
for (let i = 0; i < cols; i++) {
|
|
52
|
+
const x = x0 + i;
|
|
53
|
+
const y = y0 + j;
|
|
54
|
+
t.push();
|
|
55
|
+
t.translate(x, y);
|
|
56
|
+
if (fn(x, y, i, j) !== false)
|
|
57
|
+
t.point();
|
|
58
|
+
t.pop();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Dibuja una sola celda en coordenadas de rejilla. */
|
|
63
|
+
export function plot(t, x, y) {
|
|
64
|
+
t.push();
|
|
65
|
+
t.translate(x, y);
|
|
66
|
+
t.point();
|
|
67
|
+
t.pop();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Puntero suavizado. Usa el ratón o el primer toque; si no hay ninguno sobre el
|
|
71
|
+
* lienzo, vaga por la pantalla en piloto automático para que el fondo siga vivo.
|
|
72
|
+
*/
|
|
73
|
+
export function createPointer(t) {
|
|
74
|
+
const p = {
|
|
75
|
+
x: 0,
|
|
76
|
+
y: 0,
|
|
77
|
+
vx: 0,
|
|
78
|
+
vy: 0,
|
|
79
|
+
/** true si hay un puntero real sobre el lienzo */
|
|
80
|
+
hovering: false,
|
|
81
|
+
pressed: false,
|
|
82
|
+
/** celda exacta bajo el puntero (sin suavizar) o null */
|
|
83
|
+
raw: null,
|
|
84
|
+
update() {
|
|
85
|
+
let tx = NaN;
|
|
86
|
+
let ty = NaN;
|
|
87
|
+
const touch = t.touches?.[0];
|
|
88
|
+
if (touch && Number.isFinite(touch.x)) {
|
|
89
|
+
tx = touch.x;
|
|
90
|
+
ty = touch.y;
|
|
91
|
+
}
|
|
92
|
+
else if (Number.isFinite(t.mouse.x)) {
|
|
93
|
+
tx = t.mouse.x;
|
|
94
|
+
ty = t.mouse.y;
|
|
95
|
+
}
|
|
96
|
+
p.hovering = Number.isFinite(tx);
|
|
97
|
+
p.pressed = t.mouseIsPressed && p.hovering;
|
|
98
|
+
p.raw = p.hovering ? { x: tx, y: ty } : null;
|
|
99
|
+
if (!p.hovering) {
|
|
100
|
+
const s = t.secs;
|
|
101
|
+
tx = Math.sin(s * 0.37) * t.grid.cols * 0.32;
|
|
102
|
+
ty = Math.sin(s * 0.53 + 1.3) * t.grid.rows * 0.3;
|
|
103
|
+
}
|
|
104
|
+
const k = p.hovering ? 0.35 : 0.04;
|
|
105
|
+
const nx = p.x + (tx - p.x) * k;
|
|
106
|
+
const ny = p.y + (ty - p.y) * k;
|
|
107
|
+
p.vx = nx - p.x;
|
|
108
|
+
p.vy = ny - p.y;
|
|
109
|
+
p.x = nx;
|
|
110
|
+
p.y = ny;
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
return p;
|
|
114
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Estado de scroll compartido entre GSAP (que lo mide) y los sketches (que lo leen
|
|
3
|
+
* en cada fotograma). `p` es el progreso suavizado 0..1 dentro de la altura de 300vh;
|
|
4
|
+
* `v` es la velocidad normalizada (-1..1) y decae sola al parar.
|
|
5
|
+
*/
|
|
6
|
+
export declare const scrollState: {
|
|
7
|
+
/** progreso suavizado por GSAP */
|
|
8
|
+
p: number;
|
|
9
|
+
/** progreso directo, sin suavizar */
|
|
10
|
+
raw: number;
|
|
11
|
+
/** velocidad de scroll normalizada -1..1 */
|
|
12
|
+
v: number;
|
|
13
|
+
/** texto que un demo puede publicar para mostrarlo en la interfaz */
|
|
14
|
+
label: string;
|
|
15
|
+
};
|
|
16
|
+
export type ScrollState = typeof scrollState;
|
|
17
|
+
export declare function resetScroll(): void;
|
|
18
|
+
export declare const clamp: (v: number, a?: number, b?: number) => number;
|
|
19
|
+
/** Progreso local: 0 antes de `a`, 1 después de `b`. */
|
|
20
|
+
export declare const span: (p: number, a: number, b: number) => number;
|
|
21
|
+
export declare const smooth: (k: number) => number;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Estado de scroll compartido entre GSAP (que lo mide) y los sketches (que lo leen
|
|
3
|
+
* en cada fotograma). `p` es el progreso suavizado 0..1 dentro de la altura de 300vh;
|
|
4
|
+
* `v` es la velocidad normalizada (-1..1) y decae sola al parar.
|
|
5
|
+
*/
|
|
6
|
+
export const scrollState = {
|
|
7
|
+
/** progreso suavizado por GSAP */
|
|
8
|
+
p: 0,
|
|
9
|
+
/** progreso directo, sin suavizar */
|
|
10
|
+
raw: 0,
|
|
11
|
+
/** velocidad de scroll normalizada -1..1 */
|
|
12
|
+
v: 0,
|
|
13
|
+
/** texto que un demo puede publicar para mostrarlo en la interfaz */
|
|
14
|
+
label: "",
|
|
15
|
+
};
|
|
16
|
+
export function resetScroll() {
|
|
17
|
+
scrollState.p = 0;
|
|
18
|
+
scrollState.raw = 0;
|
|
19
|
+
scrollState.v = 0;
|
|
20
|
+
scrollState.label = "";
|
|
21
|
+
}
|
|
22
|
+
export const clamp = (v, a = 0, b = 1) => (v < a ? a : v > b ? b : v);
|
|
23
|
+
/** Progreso local: 0 antes de `a`, 1 después de `b`. */
|
|
24
|
+
export const span = (p, a, b) => clamp((p - a) / (b - a));
|
|
25
|
+
export const smooth = (k) => k * k * (3 - 2 * k);
|