Spaces:
Running
Running
Download taurus/js/backdrop.js from Ethscriptions/POLS: direct link, hf CLI and curl.
- Browser
- Download file 4.84 kB
-
https://huggingface.co/spaces/Ethscriptions/POLS/resolve/main/taurus/js/backdrop.js
- Command line
-
hf download hf://spaces/Ethscriptions/POLS/taurus/js/backdrop.js
-
curl -L -o backdrop.js https://huggingface.co/spaces/Ethscriptions/POLS/resolve/main/taurus/js/backdrop.js
4.84 kB
| // ♉ 水彩天幕:启动时把每种色调预绘成离屏纹理,播放时漂移 + 交叉溶解(全新编写) | |
| import { h1, col, clamp, lerp, TAU, halo } from './canvas-kit.js'; | |
| export const TONES = { | |
| meadow: { base: ['#0b2a22', '#3a1838'], blobs: [[40, 140, 100], [220, 110, 150], [255, 190, 150], [20, 90, 90], [150, 60, 120]], star: .55, warm: [255, 200, 170] }, | |
| dusk: { base: ['#24160e', '#3a2416'], blobs: [[150, 96, 52], [96, 60, 40], [220, 170, 110], [70, 50, 60]], star: .5, warm: [255, 200, 140] }, | |
| night: { base: ['#060b1c', '#0d1430'], blobs: [[30, 50, 110], [60, 40, 100], [20, 80, 110], [90, 70, 140]], star: 1.2, warm: [170, 190, 255] }, | |
| ink: { base: ['#07100e', '#121a1a'], blobs: [[30, 60, 56], [60, 70, 70], [20, 40, 40], [110, 90, 60]], star: .9, warm: [220, 210, 180] }, | |
| sea: { base: ['#06202a', '#0c2f3c'], blobs: [[30, 110, 130], [60, 150, 160], [200, 170, 140], [20, 60, 90]], star: .6, warm: [200, 240, 255] }, | |
| rose: { base: ['#2a0f1c', '#3a1630'], blobs: [[190, 90, 120], [120, 50, 90], [240, 180, 170], [80, 40, 80]], star: .7, warm: [255, 190, 200] }, | |
| earth: { base: ['#1a1a0e', '#2a1c12'], blobs: [[110, 120, 60], [170, 110, 70], [70, 90, 50], [220, 180, 120]], star: .5, warm: [255, 220, 160] }, | |
| gilt: { base: ['#05140f', '#140a14'], blobs: [[30, 100, 76], [150, 110, 50], [60, 40, 70], [220, 180, 110]], star: .9, warm: [255, 220, 150] }, | |
| sunset: { base: ['#2a1420', '#4a2418'], blobs: [[255, 160, 110], [230, 110, 120], [255, 210, 150], [120, 60, 100]], star: .45, warm: [255, 200, 150] }, | |
| }; | |
| const TEX = 720; | |
| function paintTexture(tone, seed) { | |
| const T = TONES[tone], cv = document.createElement('canvas'); cv.width = cv.height = TEX; const g = cv.getContext('2d'); | |
| const gr = g.createLinearGradient(0, 0, TEX * .3, TEX); gr.addColorStop(0, T.base[0]); gr.addColorStop(1, T.base[1]); g.fillStyle = gr; g.fillRect(0, 0, TEX, TEX); | |
| // 水彩色团:多层低透明度柔边圆,叠出晕染边缘 | |
| for (let i = 0; i < 260; i++) { | |
| const c = T.blobs[i % T.blobs.length]; | |
| const cx = TEX * (h1(i * 3.1 + seed) * 1.2 - .1), cy = TEX * (h1(i * 7.3 + seed) * 1.2 - .1), r = TEX * (.05 + h1(i * 1.7 + seed) * .22); | |
| const g2 = g.createRadialGradient(cx, cy, r * .2, cx, cy, r); | |
| const a = .07 + h1(i + seed * 2) * .09; | |
| g2.addColorStop(0, col(c, a)); g2.addColorStop(.7, col(c, a * .6)); g2.addColorStop(1, col(c, 0)); | |
| g.fillStyle = g2; g.beginPath(); g.ellipse(cx, cy, r, r * (.6 + h1(i + 11) * .6), h1(i + 5) * 3, 0, TAU); g.fill(); | |
| } | |
| // 纸纹颗粒 | |
| const img = g.getImageData(0, 0, TEX, TEX), d = img.data; | |
| for (let i = 0; i < d.length; i += 4) { const n = (h1(i * .25 + seed) - .5) * 10; d[i] += n; d[i + 1] += n; d[i + 2] += n; } | |
| g.putImageData(img, 0, 0); | |
| return cv; | |
| } | |
| const STARS = Array.from({ length: 420 }, (_, i) => [h1(i * 1.3), h1(i * 2.9 + 4), Math.pow(h1(i * 5.1 + 2), 3), h1(i + 99) * TAU]); | |
| const BOKEH = Array.from({ length: 26 }, (_, i) => [h1(i * 4.1 + 1), h1(i * 6.7 + 2), .4 + h1(i * 2.3) * .6, h1(i + 33) * TAU]); | |
| export function createBackdrop() { | |
| const cache = {}; | |
| const tex = tone => cache[tone] || (cache[tone] = paintTexture(tone, Object.keys(TONES).indexOf(tone) * 13.7 + 2)); | |
| function layer(g, tone, t, W, H, a) { | |
| if (a <= 0) return; | |
| const cv = tex(tone), s = Math.max(W, H) * 1.25 / TEX; | |
| const dx = Math.sin(t * .021) * W * .06 - W * .06, dy = Math.cos(t * .017) * H * .05 - H * .06; | |
| g.save(); g.globalAlpha = a; g.drawImage(cv, (W - TEX * s) / 2 + dx, (H - TEX * s) / 2 + dy, TEX * s, TEX * s); g.restore(); | |
| } | |
| // from → to 溶解,k = 0..1 | |
| function draw(g, t, W, H, from, to, k) { | |
| g.fillStyle = '#05070a'; g.fillRect(0, 0, W, H); | |
| layer(g, from, t, W, H, 1); | |
| if (to !== from) layer(g, to, t, W, H, k); | |
| const S = Math.min(W, H) / 1080; | |
| const st = lerp(TONES[from].star, TONES[to].star, k); | |
| // 星空 | |
| g.save(); g.globalCompositeOperation = 'lighter'; | |
| for (const [x, y, m, ph] of STARS) { | |
| const tw = .55 + .45 * Math.sin(t * (1 + m * 2) + ph), r = (.6 + m * 2.2) * S; | |
| g.fillStyle = col([255, 248, 235], clamp(st * tw * (.25 + m * .75))); | |
| g.beginPath(); g.arc(x * W, y * H, r, 0, TAU); g.fill(); | |
| } | |
| g.restore(); | |
| // 散景光斑 | |
| const warm = TONES[to].warm; | |
| for (const [x, y, sz, ph] of BOKEH) { | |
| const px = (x * W + Math.sin(t * .05 + ph) * 60 * S), py = (y * H + Math.cos(t * .04 + ph) * 40 * S); | |
| halo(g, px, py, (40 + sz * 70) * S, warm, .16 * sz * (.6 + .4 * Math.sin(t * .5 + ph))); | |
| } | |
| // 暗角 | |
| const v = g.createRadialGradient(W / 2, H * .5, Math.min(W, H) * .35, W / 2, H * .5, Math.max(W, H) * .78); | |
| v.addColorStop(0, 'rgba(0,0,0,0)'); v.addColorStop(1, 'rgba(0,0,0,.6)'); g.fillStyle = v; g.fillRect(0, 0, W, H); | |
| } | |
| return { draw, warm: () => Object.keys(TONES).forEach(tex) }; | |
| } | |