@biunow/agent-biuart 0.0.0-stage → 0.1.1
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/bin/server.mjs +575 -0
- package/data/styles.json +533 -0
- package/engine/audit_clip.py +46 -0
- package/engine/clip.html +24 -0
- package/engine/clip.js +104 -0
- package/engine/clip_schema.json +19 -0
- package/engine/clips/t1_3b1b.js +203 -0
- package/engine/clips/t2_keynote_ui.js +347 -0
- package/engine/clips/t3_finance_chart.js +206 -0
- package/engine/clips/y1_kurzgesagt.js +183 -0
- package/engine/clips/y2_vox.js +150 -0
- package/engine/clips/y3_whiteboard.js +209 -0
- package/engine/clips/y4_storytime.js +194 -0
- package/engine/clips/y5_kinetic_type.js +134 -0
- package/engine/compare.py +19 -0
- package/engine/demos/_shared/hero/c1_0.png +0 -0
- package/engine/demos/_shared/hero/c1_1.png +0 -0
- package/engine/demos/_shared/hero/c1_2.png +0 -0
- package/engine/demos/_shared/hero/c3_line.png +0 -0
- package/engine/demos/_shared/hero.js +96 -0
- package/engine/demos/long_scroll/eras.js +23 -0
- package/engine/demos/long_scroll/frames/8bit_act/01.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/02.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/03.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/04.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/05.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/06.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/07.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/08.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/meta.json +69 -0
- package/engine/demos/long_scroll/frames/8bit_walk/01.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/02.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/03.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/04.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/05.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/06.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/07.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/08.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/meta.json +69 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/01.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/02.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/03.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/04.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/05.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/06.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/07.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/08.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/meta.json +69 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/01.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/02.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/03.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/04.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/05.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/06.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/07.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/08.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/meta.json +69 -0
- package/engine/demos/long_scroll/frames/monet_throw/01.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/02.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/03.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/04.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/05.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/06.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/07.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/08.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/meta.json +71 -0
- package/engine/demos/long_scroll/frames/monet_walk/01.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/02.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/03.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/04.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/05.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/06.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/07.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/08.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/meta.json +71 -0
- package/engine/demos/long_scroll/lib/acts.js +87 -0
- package/engine/demos/long_scroll/lib/crossing.js +463 -0
- package/engine/demos/long_scroll/lib/world.js +143 -0
- package/engine/demos/long_scroll/lib/xing2_ding.js +42 -0
- package/engine/demos/long_scroll/lib/xing2_jia.js +114 -0
- package/engine/demos/long_scroll/lib/xing2_yi.js +133 -0
- package/engine/demos/long_scroll/segments/02_egypt.js +234 -0
- package/engine/demos/long_scroll/segments/14_8bit.js +118 -0
- package/engine/demos/long_scroll/segments/28_monet.js +86 -0
- package/engine/demos/t1_3b1b/eras.js +11 -0
- package/engine/demos/t1_3b1b/t1_s1.js +101 -0
- package/engine/demos/t1_3b1b/t1_s2.js +74 -0
- package/engine/demos/t1_3b1b/t1_s3.js +65 -0
- package/engine/demos/t2_keynote_ui/eras.js +10 -0
- package/engine/demos/t2_keynote_ui/t2_s1.js +57 -0
- package/engine/demos/t2_keynote_ui/t2_s2.js +70 -0
- package/engine/demos/t2_keynote_ui/t2_s3.js +61 -0
- package/engine/demos/t3_finance_chart/eras.js +11 -0
- package/engine/demos/t3_finance_chart/t3_s1.js +48 -0
- package/engine/demos/t3_finance_chart/t3_s2.js +42 -0
- package/engine/demos/t3_finance_chart/t3_s3.js +51 -0
- package/engine/demos/y1_kurzgesagt/eras.js +10 -0
- package/engine/demos/y1_kurzgesagt/kurzgesagt.js +347 -0
- package/engine/demos/y2_vox/eras.js +10 -0
- package/engine/demos/y2_vox/vox.js +240 -0
- package/engine/demos/y3_whiteboard/eras.js +10 -0
- package/engine/demos/y3_whiteboard/whiteboard.js +162 -0
- package/engine/demos/y4_storytime/eras.js +14 -0
- package/engine/demos/y4_storytime/s1_desk.js +18 -0
- package/engine/demos/y4_storytime/s2_screen.js +82 -0
- package/engine/demos/y4_storytime/s3_react.js +39 -0
- package/engine/demos/y4_storytime/s4_wide.js +16 -0
- package/engine/demos/y4_storytime/s5_close.js +18 -0
- package/engine/demos/y4_storytime/story.js +25 -0
- package/engine/demos/y4_storytime/vo_env.js +2 -0
- package/engine/demos/y4_storytime/world.js +73 -0
- package/engine/demos/y5_kinetic_type/eras.js +16 -0
- package/engine/demos/y5_kinetic_type/k1_title.js +38 -0
- package/engine/demos/y5_kinetic_type/k2_complete.js +55 -0
- package/engine/demos/y5_kinetic_type/k3_chat.js +47 -0
- package/engine/demos/y5_kinetic_type/k4_agent.js +49 -0
- package/engine/demos/y5_kinetic_type/kinetic.js +52 -0
- package/engine/engine.js +277 -0
- package/engine/eras.js +60 -0
- package/engine/eras_gallery.js +115 -0
- package/engine/eras_style_studies.js +20 -0
- package/engine/examples/assets/K/347/272/277/345/233/276_/346/250/252/345/261/217.png +0 -0
- package/engine/examples/assets//347/273/210/347/253/257_for/345/276/252/347/216/257/346/261/202/345/222/214.png +0 -0
- package/engine/examples/assets//347/273/210/347/253/257_sum/346/261/202/345/222/214.png +0 -0
- package/engine/examples/assets//350/220/245/346/224/266/345/233/276_/347/253/226/345/261/217.png +0 -0
- package/engine/examples/assets//350/264/242/347/273/217/345/233/276/350/241/250_/346/250/252/345/261/217.png +0 -0
- package/engine/examples/t1_3b1b.json +19 -0
- package/engine/examples/t2_fullbleed_pulse.json +15 -0
- package/engine/examples/t2_keynote_ui.json +15 -0
- package/engine/examples/t3_finance_chart.json +29 -0
- package/engine/examples/y1_kurzgesagt.json +16 -0
- package/engine/examples/y2_vox.json +14 -0
- package/engine/examples/y3_whiteboard.json +17 -0
- package/engine/examples/y4_storytime.json +52 -0
- package/engine/examples/y5_kinetic_type.json +14 -0
- package/engine/index.html +42 -0
- package/engine/lib/THREE_LICENSE.txt +21 -0
- package/engine/lib/brush.js +149 -0
- package/engine/lib/camera.js +99 -0
- package/engine/lib/chart.js +152 -0
- package/engine/lib/collage.js +97 -0
- package/engine/lib/diagram.js +264 -0
- package/engine/lib/finish.js +70 -0
- package/engine/lib/fonts/AlfaSlabOne-400.woff +0 -0
- package/engine/lib/fonts/Anton-400.woff +0 -0
- package/engine/lib/fonts/ArchivoBlack-400.woff +0 -0
- package/engine/lib/fonts/Bangers-400.woff +0 -0
- package/engine/lib/fonts/Caveat-600.woff +0 -0
- package/engine/lib/fonts/Cinzel-400.woff +0 -0
- package/engine/lib/fonts/Cinzel-700.woff +0 -0
- package/engine/lib/fonts/CormorantGaramond-500.woff +0 -0
- package/engine/lib/fonts/CormorantGaramond-500i.woff +0 -0
- package/engine/lib/fonts/CormorantGaramond-700.woff +0 -0
- package/engine/lib/fonts/EBGaramond-400.woff +0 -0
- package/engine/lib/fonts/EBGaramond-400i.woff +0 -0
- package/engine/lib/fonts/EBGaramond-600.woff +0 -0
- package/engine/lib/fonts/Exo2-800i.woff +0 -0
- package/engine/lib/fonts/GochiHand-400.woff +0 -0
- package/engine/lib/fonts/IMFellEnglish-400.woff +0 -0
- package/engine/lib/fonts/IMFellEnglish-400i.woff +0 -0
- package/engine/lib/fonts/Inter-var.woff +0 -0
- package/engine/lib/fonts/JosefinSans-700.woff +0 -0
- package/engine/lib/fonts/Kalam-700.woff +0 -0
- package/engine/lib/fonts/LICENSES.md +40 -0
- package/engine/lib/fonts/LXGWWenKai-500.woff +0 -0
- package/engine/lib/fonts/LibreBaskerville-400.woff +0 -0
- package/engine/lib/fonts/LibreBaskerville-700.woff +0 -0
- package/engine/lib/fonts/LilitaOne-400.woff +0 -0
- package/engine/lib/fonts/Marcellus-400.woff +0 -0
- package/engine/lib/fonts/NotoSansSC-500.woff +0 -0
- package/engine/lib/fonts/NotoSansSC-700.woff +0 -0
- package/engine/lib/fonts/NotoSansSC-800.woff +0 -0
- package/engine/lib/fonts/NotoSansSC-900.woff +0 -0
- package/engine/lib/fonts/NotoSansSC-sub.woff +0 -0
- package/engine/lib/fonts/NotoSerifJP-600.woff +0 -0
- package/engine/lib/fonts/NotoSerifSC-sub.woff +0 -0
- package/engine/lib/fonts/OFL.txt +94 -0
- package/engine/lib/fonts/PlayfairDisplay-400i.woff +0 -0
- package/engine/lib/fonts/PlayfairDisplay-700.woff +0 -0
- package/engine/lib/fonts/PlayfairDisplay-700i.woff +0 -0
- package/engine/lib/fonts/Poppins-600.woff +0 -0
- package/engine/lib/fonts/Poppins-700.woff +0 -0
- package/engine/lib/fonts/Poppins-800.woff +0 -0
- package/engine/lib/fonts/PressStart2P-400.woff +0 -0
- package/engine/lib/fonts/Righteous-400.woff +0 -0
- package/engine/lib/fonts/RobotoCondensed-var.woff +0 -0
- package/engine/lib/fonts/Rye-400.woff +0 -0
- package/engine/lib/fonts/UnifrakturMaguntia-400.woff +0 -0
- package/engine/lib/fonts/cmunrm.woff +0 -0
- package/engine/lib/fonts/cmunti.woff +0 -0
- package/engine/lib/fonts/index.json +1 -0
- package/engine/lib/fonts.js +56 -0
- package/engine/lib/kit.js +91 -0
- package/engine/lib/media.js +118 -0
- package/engine/lib/medium.js +258 -0
- package/engine/lib/motion.js +111 -0
- package/engine/lib/paint.js +200 -0
- package/engine/lib/palettes.js +57 -0
- package/engine/lib/post.js +108 -0
- package/engine/lib/render.js +189 -0
- package/engine/lib/rig.js +390 -0
- package/engine/lib/rig_huashu.js +171 -0
- package/engine/lib/three.min.js +7 -0
- package/engine/lib/toon.js +186 -0
- package/engine/lib/typo.js +136 -0
- package/engine/lib/ui.js +141 -0
- package/engine/lib/util.js +92 -0
- package/engine/render.py +157 -0
- package/engine/scenes/01_cave.js +364 -0
- package/engine/scenes/02_egypt.js +524 -0
- package/engine/scenes/03_greek.js +448 -0
- package/engine/scenes/04_roman.js +555 -0
- package/engine/scenes/05_gothic.js +522 -0
- package/engine/scenes/06_renaissance.js +338 -0
- package/engine/scenes/08_impressionism.js +206 -0
- package/engine/scenes/09_postimp.js +183 -0
- package/engine/scenes/10_nouveau.js +334 -0
- package/engine/scenes/11_cubism.js +265 -0
- package/engine/scenes/12_bauhaus.js +214 -0
- package/engine/scenes/13_pop.js +269 -0
- package/engine/scenes/14_8bit.js +238 -0
- package/engine/scenes/15_raytrace.js +300 -0
- package/engine/scenes/16_2026.js +565 -0
- package/engine/scenes/17_ink.js +273 -0
- package/engine/scenes/18_klimt.js +227 -0
- package/engine/scenes/19_munch.js +153 -0
- package/engine/scenes/20_dunhuang.js +215 -0
- package/engine/scenes/21_kusama.js +124 -0
- package/engine/scenes/22_constructivism.js +205 -0
- package/engine/scenes/23_dali.js +221 -0
- package/engine/scenes/24_hopper.js +150 -0
- package/engine/scenes/25_ghibli.js +251 -0
- package/engine/scenes/26_vaporwave.js +191 -0
- package/engine/scenes/27_kirby.js +255 -0
- package/engine/scenes/28_monet.js +176 -0
- package/engine/scenes/29_seurat.js +104 -0
- package/engine/scenes/30_matisse.js +151 -0
- package/engine/scenes/31_haring.js +109 -0
- package/engine/scenes/32_rembrandt.js +217 -0
- package/engine/scenes/33_rubberhose.js +219 -0
- package/engine/scenes/34_shadowpuppet.js +195 -0
- package/engine/scenes/35_shinkai.js +197 -0
- package/engine/scenes/36_picasso_blue.js +123 -0
- package/engine/scenes/38_giotto.js +133 -0
- package/engine/scenes/39_piero.js +53 -0
- package/engine/scenes/40_raphael.js +96 -0
- package/engine/scenes/41_michelangelo.js +112 -0
- package/engine/scenes/42_guardi.js +70 -0
- package/engine/scenes/43_lorenzetti.js +145 -0
- package/engine/scenes/44_cortona.js +86 -0
- package/engine/scenes/45_renoir.js +86 -0
- package/engine/scenes/46_pissarro.js +82 -0
- package/engine/scenes/47_sisley.js +94 -0
- package/engine/scenes/48_gauguin.js +107 -0
- package/engine/scenes/49_chagall.js +60 -0
- package/engine/scenes/50_ma_yuan.js +131 -0
- package/engine/scenes/51_persian_miniature.js +149 -0
- package/engine/scenes/52_south_park.js +105 -0
- package/engine/scenes/53_pixar.js +60 -0
- package/engine/scenes/54_ghibli_lab.js +68 -0
- package/engine/scenes/55_dunhuang.js +161 -0
- package/engine/scenes/index.js +28 -0
- package/engine/spec_assets.py +76 -0
- package/engine/transitions.js +929 -0
- package/engine//346/227/266/344/273/243/344/275/234/350/200/205/350/247/204/350/214/203.md +49 -0
- package/lib/host.mjs +754 -0
- package/lib/render-bridge.mjs +543 -0
- package/lib/render-cli.mjs +98 -0
- package/lib/storyboard-lint.mjs +54 -0
- package/manifest.json +52 -0
- package/package.json +8 -4
- package/runtime/package.json +8 -0
- package/skills/biuart/SKILL.md +154 -0
- package/skills/biuart/references/01-/346/213/206/350/247/243.md +55 -0
- package/skills/biuart/references/02-/346/234/272/345/210/266.md +41 -0
- package/skills/biuart/references/03-/344/270/200/345/270/247/345/205/210/350/241/214/345/233/233/346/235/241/350/267/257/347/272/277.md +39 -0
- package/skills/biuart/references/04-/347/272/257/344/273/243/347/240/201/347/273/230/345/210/266.md +92 -0
- package/skills/biuart/references/05-/350/212/202/345/245/217/344/270/216/351/205/215/344/271/220.md +19 -0
- package/skills/biuart/references/06-/345/217/243/346/222/255/351/251/261/345/212/250/347/232/204/350/211/272/346/234/257/347/237/255/347/211/207.md +31 -0
- package/skills/biuart/references/07-/346/255/243/351/235/242/347/273/217/351/252/214.md +196 -0
- package/skills/biuart/references/08-/351/243/216/346/240/274/344/275/234/350/200/205/350/247/204/350/214/203.md +61 -0
- package/skills/biuart/references/09-/350/247/206/351/242/221/345/212/250/347/224/273/350/257/255/346/263/225.md +382 -0
- package/skills/biuart/references/10-/350/247/222/350/211/262.md +34 -0
- package/skills/biuart/references/11-/351/225/277/345/215/267/347/251/277/350/266/212/347/211/207.md +43 -0
- package/skills/biuart/references/12-/345/217/243/346/222/255/346/225/264/347/211/207/344/270/216/347/273/217/351/252/214/345/233/236/346/265/201.md +25 -0
- package/skills/biuart/references/13-/345/217/243/346/222/255/344/270/216/350/257/255/351/237/263/345/244/215/345/210/273.md +7 -0
- package/skills/biuart/references/15-/350/267/250/345/234/272/346/231/257/351/243/216/346/240/274/346/217/220/347/202/274.md +48 -0
- package/skills/biuart/references/16-/351/243/216/346/240/274/346/274/224/347/244/27295/345/210/206/345/215/217/350/256/256.md +103 -0
- package/skills/biuart/references/about-author.md +99 -0
- package/skills/biuart/references/capabilities.md +91 -0
- package/skills/biuart/references/compatibility.md +34 -0
- package/skills/biuart/references/images.md +97 -0
- package/skills/biuart/references/voice-model-review.md +18 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/t1_3b1b.md +122 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/t2_keynote_ui.md +117 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/t3_finance_chart.md +116 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y1_kurzgesagt.md +193 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y2_vox.md +195 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y3_whiteboard.md +190 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y4_storytime.md +166 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y5_kinetic_type.md +158 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y6_presenter_explainer.md +24 -0
- package/skills/biuart/references//345/217/243/346/222/255API.md +125 -0
- package/skills/biuart/references//350/211/262/346/235/277.md +38 -0
- package/skills/biuart/references//350/247/222/350/211/262/347/262/276/347/273/206/345/272/246.md +61 -0
- package/skills/biuart/references//351/225/234/345/244/264/350/241/250/346/250/241/346/235/277.md +80 -0
- package/skills/biuart/references//351/225/234/345/244/264/350/257/255/350/250/200.md +73 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/01_cave.md +46 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/02_egypt.md +64 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/03_greek.md +50 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/04_roman.md +99 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/05_gothic.md +32 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/06_renaissance.md +41 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/08_impressionism.md +34 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/09_postimp.md +17 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/10_nouveau.md +31 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/11_cubism.md +30 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/12_bauhaus.md +30 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/13_pop.md +29 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/14_8bit.md +27 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/15_raytrace.md +32 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/16_2026.md +55 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/17_ink.md +50 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/18_klimt.md +37 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/19_munch.md +42 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/20_dunhuang.md +49 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/21_kusama.md +32 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/22_constructivism.md +35 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/23_dali.md +29 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/24_hopper.md +30 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/25_ghibli.md +55 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/26_vaporwave.md +27 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/27_kirby.md +30 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/28_monet.md +39 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/29_seurat.md +31 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/30_matisse.md +26 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/31_haring.md +22 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/32_rembrandt.md +29 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/33_rubberhose.md +31 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/34_shadowpuppet.md +40 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/35_shinkai.md +34 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/36_picasso_blue.md +25 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/37_rick_morty.md +48 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/38_giotto.md +67 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/39_piero.md +59 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/40_raphael.md +65 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/41_michelangelo.md +65 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/42_guardi.md +65 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/43_lorenzetti.md +69 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/44_cortona.md +66 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/45_renoir.md +66 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/46_pissarro.md +66 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/47_sisley.md +67 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/48_gauguin.md +66 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/49_chagall.md +59 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/50_ma_yuan.md +68 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/51_persian_miniature.md +66 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/52_south_park.md +65 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/53_pixar.md +61 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/55_dunhuang.md +41 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/INDEX.md +89 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/_/350/275/254/345/234/272_/350/277/201/347/247/273/346/265/213/350/257/225.md +53 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/_/351/207/215/345/201/232/350/277/233/345/272/246.md +37 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/_/351/237/263/350/275/250_/350/211/272/346/234/257/345/217/262/351/200/237/351/200/232.md +135 -0
- package/studio/app.js +380 -0
- package/studio/assert-panel-permit.mjs +67 -0
- package/studio/composer.css +465 -0
- package/studio/composer.js +480 -0
- package/studio/index.html +97 -0
- package/studio/optimize-prompt.mjs +84 -0
- package/studio/optimize-prompt.test.mjs +47 -0
- package/studio/recipes.mjs +87 -0
- package/studio/select-menu.js +450 -0
- package/studio/styles.css +71 -0
- package/studio/to-english-prompt.mjs +149 -0
- package/studio/to-english-prompt.test.mjs +72 -0
- package/README.md +0 -3
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// 蒙克《呐喊》式表现主义(1893)——纯代码。
|
|
2
|
+
// 管线:①流线渲染器(每个种子点沿「随时间扭动的方向场」积分 9–14 步画成长弯笔,颜色按区域色带)铺满墙/天/峡湾/地板
|
|
3
|
+
// → ②窗里:桥、栏杆、捂脸尖叫的小人、两个远处的人影 → ③桌、椅(摇晃的腿)
|
|
4
|
+
// → ④角色:平涂+流线肌理(source-atop)+深色粗轮廓 → ⑤整幅按行做正弦位移:整个世界在流动
|
|
5
|
+
SCENES['19_munch'] = (() => {
|
|
6
|
+
const W = 1920, H = 1080, { clamp, lerp, rng } = U, P = PAINT;
|
|
7
|
+
const { ss } = U;
|
|
8
|
+
const WIN = [370, 140, 790, 540];
|
|
9
|
+
const inWin = (x, y) => x > WIN[0] && x < WIN[2] && y > WIN[1] && y < WIN[3];
|
|
10
|
+
const VP = [1000, 640];
|
|
11
|
+
const PAL = {
|
|
12
|
+
sky: ['#e2541c', '#f08a24', '#f4c03a', '#c8301e', '#e8743a', '#f6a63a', '#b82a1e'],
|
|
13
|
+
fjord: ['#1d3a6a', '#2c5a8a', '#3a4a7a', '#14243e', '#4a6aa0'],
|
|
14
|
+
hill: ['#2a4a2a', '#1e3a3a', '#43603a', '#5a6a2a'],
|
|
15
|
+
wall: ['#8a2e1c', '#a8441e', '#6a2418', '#b8562a', '#7a3020', '#c86a2a', '#3a2a4a'],
|
|
16
|
+
floor: ['#8a5a2a', '#a8703a', '#6a3a1e', '#c88a42', '#4a2a1a'],
|
|
17
|
+
};
|
|
18
|
+
const hx = Object.fromEntries(Object.entries(PAL).map(([k, v]) => [k, v.map(P.hex)]));
|
|
19
|
+
// 区域:窗内上部天空(色带随波浪起伏),中部峡湾,左下山坡;窗外是墙/地板
|
|
20
|
+
function region(x, y, t) {
|
|
21
|
+
if (inWin(x, y)) {
|
|
22
|
+
const shore = 330 + 60 * Math.sin((x - 370) * 0.012 + 0.5) + 10 * Math.sin(t * 2 + x * 0.02);
|
|
23
|
+
if (y < shore) return 'sky';
|
|
24
|
+
if (x < 470 + (y - shore) * 0.6) return 'hill';
|
|
25
|
+
return 'fjord';
|
|
26
|
+
}
|
|
27
|
+
return y > 700 ? 'floor' : 'wall';
|
|
28
|
+
}
|
|
29
|
+
function angle(x, y, t, reg) {
|
|
30
|
+
switch (reg) {
|
|
31
|
+
case 'sky': return 0.55 * Math.sin(x * 0.011 + y * 0.018 - t * 2.4) + 0.15 * Math.sin(y * 0.05 + t * 3);
|
|
32
|
+
case 'fjord': { const a = Math.atan2(y - 470, x - 620); return a + Math.PI / 2 + 0.3 * Math.sin(t * 2 + a * 2); }
|
|
33
|
+
case 'hill': return -0.9 + 0.4 * Math.sin(y * 0.03 + t * 2);
|
|
34
|
+
case 'floor': return Math.atan2(y - VP[1], x - VP[0]) + 0.18 * Math.sin(x * 0.01 + t * 2.6);
|
|
35
|
+
default: return -Math.PI / 2 + 0.5 * Math.sin(y * 0.011 + x * 0.005 + t * 2.4) + 0.2 * Math.sin(x * 0.03 - t * 3);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const SEEDS = P.flowSeeds(24, 5);
|
|
39
|
+
// 区域底色:先铺平涂,长笔之间的缝露出的是同色系底,不是黑
|
|
40
|
+
function baseFill(g, t) {
|
|
41
|
+
const wg = g.createLinearGradient(0, 0, 0, 700); wg.addColorStop(0, '#a8401e'); wg.addColorStop(1, '#5a1a14'); g.fillStyle = wg; g.fillRect(0, 0, W, 700);
|
|
42
|
+
const fg = g.createLinearGradient(0, 700, 0, H); fg.addColorStop(0, '#7a4a22'); fg.addColorStop(1, '#4a2614'); g.fillStyle = fg; g.fillRect(0, 700, W, H - 700);
|
|
43
|
+
const sg = g.createLinearGradient(0, 140, 0, 400); sg.addColorStop(0, '#f08a24'); sg.addColorStop(0.5, '#d8401c'); sg.addColorStop(1, '#2c4a7a'); g.fillStyle = sg; g.fillRect(370, 140, 420, 400);
|
|
44
|
+
}
|
|
45
|
+
// 流线:沿方向场积分画长弯笔(不描边、半透明叠色,蒙克是薄涂长笔,不是梵高的厚涂短笔)
|
|
46
|
+
// 天空色由「波浪化的 y」决定色带——呐喊的血色天空就是横向色带在扭
|
|
47
|
+
const RS = { sky: [32, 7, 10, 0.78], fjord: [26, 6, 9, 0.78], hill: [12, 6, 8, 0.8], wall: [42, 7, 13, 0.5], floor: [32, 8, 12, 0.55] }; // 长、宽、薄:一根笔走 200–300px
|
|
48
|
+
// 长弯流线笔:P.flowLines(种子沿方向场积分,越区即停);这里只给区域、方向场、配色
|
|
49
|
+
const flowColor = (reg, sx, sy, r1, t) => {
|
|
50
|
+
if (reg === 'sky') { const yy = sy + 22 * Math.sin(sx * 0.014 - t * 2.2) + 8 * Math.sin(sx * 0.05 + t * 3); const band = Math.floor((yy - 140) / 28 + r1 * 0.6); return hx.sky[((band % 7) + 7) % 7]; }
|
|
51
|
+
const pal = hx[reg]; const col = pal[(r1 * pal.length) | 0]; return reg === 'wall' ? P.mix(col, [30, 12, 10], clamp((sy - 300) / 900) * 0.4) : col;
|
|
52
|
+
};
|
|
53
|
+
const flowLines = (c, t) => P.flowLines(c, SEEDS, { t, region, angle, color: flowColor, params: RS });
|
|
54
|
+
|
|
55
|
+
function bridge(c, t) {
|
|
56
|
+
c.save(); c.beginPath(); c.rect(370, 140, 420, 400); c.clip();
|
|
57
|
+
// 桥面+栏杆(从右下斜向左上,栏杆随世界一起扭)
|
|
58
|
+
c.fillStyle = '#8a5a3a'; c.beginPath(); c.moveTo(790, 470); c.lineTo(370, 410); c.lineTo(370, 540); c.lineTo(790, 540); c.closePath(); c.fill();
|
|
59
|
+
c.lineCap = 'round'; for (let k = 0; k < 14; k++) { c.strokeStyle = ['#a8703a', '#6a3a22', '#c88a52', '#7a4a2a'][k % 4]; c.lineWidth = 6; c.beginPath(); for (let i = 0; i <= 12; i++) { const q = i / 12, x = lerp(800, 360, q), y = lerp(478 + k * 5, 418 + k * 9, q) + Math.sin(q * 5 + t * 3 + k) * 3; i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); }
|
|
60
|
+
c.strokeStyle = '#3a1e14'; c.lineCap = 'round';
|
|
61
|
+
for (let k = 0; k < 3; k++) { c.lineWidth = 6 - k; c.beginPath(); for (let i = 0; i <= 20; i++) { const q = i / 20, x = lerp(800, 360, q), y = lerp(452 + k * 18, 372 + k * 12, q) + Math.sin(q * 6 + t * 3) * 4; i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); }
|
|
62
|
+
for (let i = 0; i < 9; i++) { const q = i / 8, x = lerp(790, 380, q); c.lineWidth = 4; c.beginPath(); c.moveTo(x, lerp(452, 372, q)); c.lineTo(x + 2, lerp(500, 410, q)); c.stroke(); }
|
|
63
|
+
// 远处两个戴帽人影
|
|
64
|
+
[[480, 395], [515, 400]].forEach(([x, y], k) => { c.fillStyle = '#1a1420'; c.beginPath(); c.ellipse(x, y, 7, 22, 0.05 * Math.sin(t * 2 + k), 0, Math.PI * 2); c.fill(); c.fillRect(x - 9, y - 26, 18, 4); c.fillRect(x - 5, y - 32, 10, 7); });
|
|
65
|
+
// 尖叫者:骷髅般的头,双手捂脸,身体是一道扭动的黑袍
|
|
66
|
+
const fx = 690, fy = 470, sway = Math.sin(t * 3.2) * 0.12;
|
|
67
|
+
c.save(); c.translate(fx, fy + 70); c.rotate(sway);
|
|
68
|
+
c.fillStyle = '#2a1e2e'; c.beginPath(); c.moveTo(-30, 0); c.bezierCurveTo(-40, -40, -14, -50, -20, -86); c.lineTo(20, -86); c.bezierCurveTo(20, -50, 44, -30, 34, 0); c.closePath(); c.fill();
|
|
69
|
+
c.translate(0, -104); c.rotate(-sway * 0.6);
|
|
70
|
+
c.fillStyle = '#d8c890'; c.beginPath(); c.ellipse(0, 0, 21, 28, 0, 0, Math.PI * 2); c.fill();
|
|
71
|
+
c.strokeStyle = '#2a1e2e'; c.lineWidth = 2.5; c.stroke();
|
|
72
|
+
c.fillStyle = '#2a1e2e'; c.beginPath(); c.ellipse(-8, -6, 5, 7, 0, 0, Math.PI * 2); c.ellipse(8, -6, 5, 7, 0, 0, Math.PI * 2); c.fill();
|
|
73
|
+
const mo = 7 + Math.sin(t * 9) * 2; c.beginPath(); c.ellipse(0, 13, 5, mo, 0, 0, Math.PI * 2); c.fill();
|
|
74
|
+
c.fillStyle = '#c8b480'; [[-1], [1]].forEach(([s]) => { c.beginPath(); c.ellipse(s * 22, 4, 7, 15, -s * 0.25, 0, Math.PI * 2); c.fill(); c.stroke(); });
|
|
75
|
+
c.strokeStyle = '#2a1e2e'; c.lineWidth = 9; [[-1], [1]].forEach(([s]) => { c.beginPath(); c.moveTo(s * 22, 14); c.quadraticCurveTo(s * 30, 50, s * 20, 72); c.stroke(); });
|
|
76
|
+
c.restore();
|
|
77
|
+
c.restore();
|
|
78
|
+
}
|
|
79
|
+
function furniture(c, t) {
|
|
80
|
+
const wob = (x0, y0, x1, y1, w, col, ph) => { c.strokeStyle = col; c.lineWidth = w; c.lineCap = 'round'; c.beginPath(); for (let i = 0; i <= 12; i++) { const q = i / 12; const x = lerp(x0, x1, q) + Math.sin(q * 5 + t * 3.5 + ph) * 7 * Math.sin(q * Math.PI), y = lerp(y0, y1, q); i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); };
|
|
81
|
+
// 椅子(在少女后)
|
|
82
|
+
wob(1440, 470, 1446, 910, 16, '#2a1a14', 0); wob(1486, 480, 1490, 910, 16, '#2a1a14', 1);
|
|
83
|
+
// 桌
|
|
84
|
+
c.fillStyle = '#5a2e1c'; c.beginPath(); c.moveTo(808, 618); c.quadraticCurveTo(1010, 606 + Math.sin(t * 3) * 6, 1212, 620); c.lineTo(1206, 652); c.quadraticCurveTo(1010, 642, 814, 652); c.closePath(); c.fill();
|
|
85
|
+
c.strokeStyle = '#1a0e14'; c.lineWidth = 5; c.stroke();
|
|
86
|
+
wob(846, 650, 840, 905, 22, '#3a1e14', 2); wob(1176, 650, 1182, 905, 22, '#3a1e14', 3);
|
|
87
|
+
wob(846, 650, 840, 905, 6, '#8a4a2a', 2.3); wob(1176, 650, 1182, 905, 6, '#8a4a2a', 3.3);
|
|
88
|
+
// 窗框:深色、歪的
|
|
89
|
+
c.strokeStyle = '#1a1020'; c.lineWidth = 16; c.lineJoin = 'round'; c.beginPath(); c.moveTo(358, 128); c.quadraticCurveTo(580, 116 + Math.sin(t * 2.5) * 8, 804, 132); c.quadraticCurveTo(810, 340, 800, 552); c.quadraticCurveTo(580, 562, 360, 550); c.quadraticCurveTo(352, 340, 358, 128); c.stroke();
|
|
90
|
+
c.lineWidth = 9; c.beginPath(); c.moveTo(580, 128); c.quadraticCurveTo(586 + Math.sin(t * 3) * 6, 340, 578, 552); c.stroke();
|
|
91
|
+
}
|
|
92
|
+
// 角色
|
|
93
|
+
const girlPal = { skin: '#e8d4a0', hair: '#e2b84a', hairLine: '#a8782a', dress: '#1e2238', line: '#120c18', lw: 5, cheek: 'rgba(200,90,70,.18)', lip: '#a8282a', iris: '#1a1420', cuff: '#e8e0c8', cupBody: '#e8e0c8', cupRim: '#6a2a1a', shoe: '#120c18', browC: '#6a4a2a' };
|
|
94
|
+
const catPal = { orange: '#e07a2a', white: '#efe6c8', stripe: '#8a3a14', line: '#120c18', lw: 5, eye: '#c8d040', noseC: '#c84a3a' };
|
|
95
|
+
function chars(c, t, lt, ch) {
|
|
96
|
+
const L = P.scratch('mu_chars'), g = L.getContext('2d'); g.reset();
|
|
97
|
+
const K = RIG.cat({ tail: ch.tail * 1.4, blink: 0, breathe: ch.breathe });
|
|
98
|
+
const G = RIG.girl({ cup: ch.cup, sip: ch.sip, breathe: ch.breathe, hair: 'long' });
|
|
99
|
+
RIG.drawCat(g, K, { ...catPal, lw: 0 });
|
|
100
|
+
RIG.drawGirl(g, G, { ...girlPal, lw: 0, mode: 'fill', apron: null });
|
|
101
|
+
// 绿影:蒙克脸上的黄绿阴影
|
|
102
|
+
g.save(); g.clip(G.face); g.fillStyle = 'rgba(120,150,70,.4)'; g.filter = 'blur(8px)'; g.beginPath(); g.ellipse(G.A.headC[0] + 20, G.A.headC[1] + 10, 40, 50, 0, 0, Math.PI * 2); g.fill(); g.filter = 'none'; g.restore();
|
|
103
|
+
// 流线肌理(角色身上也是长弯笔)
|
|
104
|
+
g.save(); g.globalCompositeOperation = 'source-atop'; g.globalAlpha = 0.55; g.lineCap = 'round';
|
|
105
|
+
const r = rng(17 + P.boilSeed(t, 10));
|
|
106
|
+
for (let i = 0; i < 900; i++) { const x = 380 + r() * 1100, y = 260 + r() * 680;
|
|
107
|
+
const a = x < 760 ? Math.atan2(y - 780, x - 540) + Math.PI / 2 : -Math.PI / 2 + 0.6 * Math.sin(y * 0.02 + t * 3);
|
|
108
|
+
g.strokeStyle = x < 760 ? (r() < 0.5 ? '#f0a040' : '#b8501a') : (y < 450 ? (r() < 0.5 ? '#f4d070' : '#b88a2a') : (r() < 0.5 ? '#34406a' : '#0e1020')); g.lineWidth = 5;
|
|
109
|
+
g.beginPath(); g.moveTo(x, y); g.quadraticCurveTo(x + Math.cos(a) * 14 + Math.sin(t * 4 + i) * 6, y + Math.sin(a) * 14, x + Math.cos(a) * 28, y + Math.sin(a) * 28); g.stroke(); }
|
|
110
|
+
g.restore();
|
|
111
|
+
c.drawImage(L, 0, 0);
|
|
112
|
+
// 白领(端庄的 1890 年代挪威少女裙)
|
|
113
|
+
c.fillStyle = '#ece4cc'; c.beginPath(); c.ellipse(G.A.neck[0] + 4, G.A.neck[1] + 8, 30, 12, -0.15, 0, Math.PI * 2); c.fill();
|
|
114
|
+
RIG.drawCat(c, K, { ...catPal, mode: 'line' });
|
|
115
|
+
RIG.drawGirl(c, G, { ...girlPal, mode: 'line', apron: null });
|
|
116
|
+
KIT.nearArm(c, G, { fill: girlPal.dress, line: girlPal.line, lw: 5, skin: girlPal.skin, cuff: girlPal.cuff, cupBody: girlPal.cupBody, cupRim: girlPal.cupRim,
|
|
117
|
+
pattern: g3 => { g3.lineCap = 'round'; g3.lineWidth = 5; for (let i = 0; i < 26; i++) { const x = 1150 + (i * 37) % 280, y = 330 + (i * 53) % 300; g3.strokeStyle = i % 2 ? '#34406a' : '#0e1020'; g3.beginPath(); g3.moveTo(x, y); g3.quadraticCurveTo(x + 6 + Math.sin(t * 4 + i) * 6, y + 14, x + 2, y + 30); g3.stroke(); } } });
|
|
118
|
+
// 长金发:一绺绺波浪线,向下传的行波
|
|
119
|
+
c.save(); c.lineCap = 'round';
|
|
120
|
+
for (let k = 0; k < 9; k++) { const x0 = G.A.nape[0] - 30 + k * 7, y0 = G.A.nape[1] - 60 + k * 4; c.strokeStyle = k % 2 ? '#f0c858' : '#b8862a'; c.lineWidth = 7;
|
|
121
|
+
c.beginPath(); for (let i = 0; i <= 16; i++) { const q = i / 16; const x = x0 + 18 * q + Math.sin(q * 9 - t * 7 + k) * (4 + 12 * q), y = y0 + q * 230; i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); }
|
|
122
|
+
c.restore();
|
|
123
|
+
// 猫:张嘴(被画外的尖叫传染)
|
|
124
|
+
c.fillStyle = '#3a1418'; c.beginPath(); c.ellipse(K.nose[0] - 4, K.nose[1] + 16, 6, 7 + Math.sin(t * 9) * 3, 0, 0, Math.PI * 2); c.fill();
|
|
125
|
+
return G;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
draw(c, lt, t) {
|
|
130
|
+
const ch = KIT.choreo(lt, t);
|
|
131
|
+
const S = P.scratch('mu_frame'), g = S.getContext('2d'); g.reset();
|
|
132
|
+
baseFill(g, t);
|
|
133
|
+
flowLines(g, t);
|
|
134
|
+
bridge(g, t);
|
|
135
|
+
furniture(g, t);
|
|
136
|
+
const G = chars(g, t, lt, ch);
|
|
137
|
+
// 热气:三道粗波浪线(像天空色带)
|
|
138
|
+
g.save(); g.lineCap = 'round'; [0, 1].forEach(k => { g.strokeStyle = ['#f6e0a0', '#f4c03a'][k]; g.lineWidth = 3.5; g.beginPath(); for (let i = 0; i <= 20; i++) { const q = i / 20, x = G.cup.x - 10 + k * 10 - q * 60 * ch.cup + Math.sin(q * 7 - t * 8 + k) * 12 * q, y = G.cup.y - 10 - q * 90; i ? g.lineTo(x, y) : g.moveTo(x, y); } g.stroke(); }); g.restore();
|
|
139
|
+
// 整个世界在流动:行位移+轻微列位移
|
|
140
|
+
const T2 = P.scratch('mu_warp'), g2 = T2.getContext('2d'); g2.reset();
|
|
141
|
+
g2.fillStyle = '#3a1410'; g2.fillRect(0, 0, W, H);
|
|
142
|
+
KIT.warpRows(g2, S, y => 9 * Math.sin(y * 0.012 + t * 4.2) + 4 * Math.sin(y * 0.031 - t * 6), 4, 14);
|
|
143
|
+
c.fillStyle = '#3a1410'; c.fillRect(0, 0, W, H);
|
|
144
|
+
KIT.warpCols(c, T2, x => 6 * Math.sin(x * 0.009 - t * 3.6), 4, 8);
|
|
145
|
+
},
|
|
146
|
+
label(c, lt, t) {
|
|
147
|
+
const L = P.scratch('mu_label'), g = L.getContext('2d'); g.reset();
|
|
148
|
+
KIT.label(g, { title: '表现主义', sub: 'EDVARD MUNCH · 1893', tFont: '84px "LXGWWenKai-500"', sFont: '34px "IMFellEnglish-400"', tCol: '#f4c03a', sCol: '#f08a24', y: 132, sy: 186, spacing: 4,
|
|
149
|
+
before: g2 => { g2.save(); g2.font = '84px "LXGWWenKai-500"'; g2.letterSpacing = '4px'; g2.textAlign = 'right'; g2.lineWidth = 10; g2.strokeStyle = '#1a0a10'; g2.lineJoin = 'round'; g2.strokeText('表现主义', 1866, 132); g2.font = '34px "IMFellEnglish-400"'; g2.letterSpacing = '0px'; g2.lineWidth = 7; g2.strokeText('EDVARD MUNCH · 1893', 1866, 186); g2.restore(); } });
|
|
150
|
+
KIT.warpRows(c, L, y => (y > 30 && y < 210) ? 5 * Math.sin(y * 0.09 + t * 7) : 0, 3);
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
})();
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
// 敦煌壁画(莫高窟·盛唐)——纯代码。
|
|
2
|
+
// 管线:①土红地仗(斑驳、烟熏,缓存)→ ②顶部垂幔纹+卷草边 → ③窗=石青天:飞天带飘带飞过、祥云流动
|
|
3
|
+
// → ④莲花砖地、低案、油灯 → ⑤猫:赭红平涂+铁线描 → ⑥少女:唐代供养人(高髻、花钿、间色裙、披帛飘动)
|
|
4
|
+
// → ⑦天花(散落的花瓣)飘下 → ⑧罩层:颜料不匀、龟裂、剥落露出白灰地仗、整体褪色;剥落的颜料片偶尔掉下
|
|
5
|
+
SCENES['20_dunhuang'] = (() => {
|
|
6
|
+
const W = 1920, H = 1080, { clamp, lerp, rng } = U, P = PAINT;
|
|
7
|
+
const { ss } = U;
|
|
8
|
+
const C = { red: '#a5452f', redDk: '#7a2e1e', ochre: '#e08a3a', green: '#3f8f6e', green2: '#6ab08e', blue: '#2f5f8f', blue2: '#5a86b0', white: '#ecdfc4', ink: '#2a1a14', line: '#5a2414', skin: '#f2dcc4', gold: '#d8b05a', plaster: '#d8c6a2' };
|
|
9
|
+
const LW = 2.6;
|
|
10
|
+
const F = (c, p, fill, lw = LW, line = C.line) => { if (fill) { c.fillStyle = fill; c.fill(p); } if (lw) { c.strokeStyle = line; c.lineWidth = lw; c.stroke(p); } };
|
|
11
|
+
|
|
12
|
+
// ---------- 地仗:土红,斑驳 ----------
|
|
13
|
+
const ground = () => P.cached('dh_ground', W, H, (g) => {
|
|
14
|
+
g.drawImage(P.texture('dh_red', C.red, { scale: 0.003, amt: 30, grain: 16, dark: '#7a3020', seed: 3 }), 0, 0);
|
|
15
|
+
// 团花:稀疏的淡色圆形花饰(褪色)
|
|
16
|
+
const r = rng(8);
|
|
17
|
+
for (let i = 0; i < 16; i++) { const x = 100 + r() * 1700, y = 260 + r() * 400; if (x > 330 && x < 840 && y < 600) continue; if (x > 1080 && x < 1520) continue;
|
|
18
|
+
g.save(); g.translate(x, y); g.globalAlpha = 0.5;
|
|
19
|
+
for (let k = 0; k < 8; k++) { g.rotate(Math.PI / 4); g.fillStyle = k % 2 ? C.green2 : C.white; g.beginPath(); g.ellipse(0, -16, 7, 14, 0, 0, Math.PI * 2); g.fill(); }
|
|
20
|
+
g.fillStyle = C.blue2; g.beginPath(); g.arc(0, 0, 8, 0, Math.PI * 2); g.fill(); g.restore(); }
|
|
21
|
+
// 顶部:卷草边+垂幔纹(三角垂帐,石青石绿土红交替)
|
|
22
|
+
g.fillStyle = C.redDk; g.fillRect(0, 0, W, 34);
|
|
23
|
+
g.strokeStyle = C.green2; g.lineWidth = 4; g.beginPath(); for (let x = 0; x <= W; x += 4) { const y = 17 + Math.sin(x * 0.05) * 9; x ? g.lineTo(x, y) : g.moveTo(x, y); } g.stroke();
|
|
24
|
+
for (let x = 0; x < W; x += 80) { g.strokeStyle = C.white; g.lineWidth = 2.5; g.beginPath(); g.arc(x + 40, 17 + Math.sin((x + 40) * 0.05) * 9, 8, 0, Math.PI * 1.5); g.stroke(); }
|
|
25
|
+
const cols = [C.blue, C.green, C.white, C.red];
|
|
26
|
+
for (let x = 0, k = 0; x < W; x += 64, k++) { g.fillStyle = cols[k % 4]; g.beginPath(); g.moveTo(x, 34); g.lineTo(x + 64, 34); g.lineTo(x + 32, 84); g.closePath(); g.fill(); g.strokeStyle = C.line; g.lineWidth = 2; g.stroke();
|
|
27
|
+
g.fillStyle = C.gold; g.beginPath(); g.arc(x + 32, 88, 5, 0, Math.PI * 2); g.fill(); }
|
|
28
|
+
g.fillStyle = C.redDk; g.fillRect(0, 34, W, 5);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// ---------- 窗:石青天、祥云、飞天 ----------
|
|
32
|
+
function cloud(c, x, y, s, t, ph) {
|
|
33
|
+
c.save(); c.translate(x, y); c.scale(s, s);
|
|
34
|
+
c.fillStyle = C.white; c.strokeStyle = C.line; c.lineWidth = 2.2 / s;
|
|
35
|
+
[[0, 0, 18], [-22, 6, 13], [20, 6, 14], [-8, -14, 12], [10, -12, 11]].forEach(([dx, dy, r]) => { c.beginPath(); c.arc(dx, dy, r, 0, Math.PI * 2); c.fill(); c.stroke(); });
|
|
36
|
+
c.fillStyle = C.white; c.beginPath(); c.ellipse(0, 4, 32, 12, 0, 0, Math.PI * 2); c.fill();
|
|
37
|
+
[[0, 0, 10], [-22, 6, 7], [20, 6, 8]].forEach(([dx, dy, r]) => { c.strokeStyle = C.green; c.beginPath(); c.arc(dx, dy, r, 0.3, Math.PI * 1.7); c.stroke(); });
|
|
38
|
+
// 云尾:一条渐细的尾巴
|
|
39
|
+
c.fillStyle = C.white; c.fill(KIT.ribbon(KIT.densify([[30, 8], [60, 14 + Math.sin(ph) * 3], [95, 6 + Math.sin(ph + 1) * 5], [120, 12]], 5), q => 16 * (1 - q) + 1)); c.restore();
|
|
40
|
+
}
|
|
41
|
+
function apsara(c, t, lt) {
|
|
42
|
+
// 飞天:横卧的身体朝左飞,头在左,两条长飘带拖在身后成 S 形(行波)
|
|
43
|
+
const x = 760 - lt * 300, y = 300 + Math.sin(lt * 3.2) * 22;
|
|
44
|
+
c.save(); c.translate(x, y); c.scale(1.35, 1.35); c.rotate(-0.12 + Math.sin(lt * 3.2) * 0.05);
|
|
45
|
+
const rib = (ox, oy, len, col, ph, amp) => { const pts = []; for (let i = 0; i <= 18; i++) { const q = i / 18; pts.push([ox + q * len, oy + Math.sin(q * 5.5 - t * 7 + ph) * amp * (0.3 + q) - q * 30]); } const p = KIT.ribbon(pts, q => 15 * (1 - q * 0.7)); F(c, p, col, 2); };
|
|
46
|
+
rib(40, -6, 300, C.green, 0, 26); rib(30, 10, 270, C.red, 1.6, 30);
|
|
47
|
+
// 绕过头顶的大弧飘带(飞天最有辨识度的形):从肩起,弧过头顶,再甩到身后
|
|
48
|
+
{ const pts = []; for (let i = 0; i <= 30; i++) { const q = i / 30; let x, y; if (q < 0.45) { const a = Math.PI * 0.9 - q / 0.45 * Math.PI * 1.15; x = -40 + Math.cos(a) * 70; y = -30 - Math.abs(Math.sin(a)) * 80; } else { const u = (q - 0.45) / 0.55; x = -40 + Math.cos(Math.PI * 0.9 - Math.PI * 1.15) * 70 + u * 260; y = -30 - Math.abs(Math.sin(-Math.PI * 0.25)) * 80 + Math.sin(u * 5 - t * 7) * 26 * u - u * 20; } pts.push([x, y]); }
|
|
49
|
+
F(c, KIT.ribbon(pts, q => 13 * (1 - q * 0.6)), C.blue2, 2); }
|
|
50
|
+
// 长裙(向后飘)
|
|
51
|
+
F(c, RIG.smooth([[0, -16], [70, -18], [150, -10 + Math.sin(t * 6) * 6], [190, 4 + Math.sin(t * 6 + 1) * 8], [140, 20], [60, 18], [0, 14]]), C.blue, 2.2);
|
|
52
|
+
for (let k = 0; k < 3; k++) { c.strokeStyle = C.white; c.lineWidth = 2; c.beginPath(); c.moveTo(30 + k * 40, -12); c.quadraticCurveTo(60 + k * 40, 0, 40 + k * 46, 16); c.stroke(); }
|
|
53
|
+
// 上身、手臂托花盘
|
|
54
|
+
F(c, RIG.smooth([[-40, -18], [0, -22], [8, 0], [0, 16], [-36, 12]]), C.skin, 2.2);
|
|
55
|
+
F(c, RIG.smooth([[-30, -14], [-70, -34], [-86, -30], [-78, -20], [-40, -4]]), C.skin, 2);
|
|
56
|
+
c.fillStyle = C.gold; c.beginPath(); c.ellipse(-90, -36, 18, 6, -0.2, 0, Math.PI * 2); c.fill(); c.strokeStyle = C.line; c.lineWidth = 2; c.stroke();
|
|
57
|
+
[[-98, -44, C.red], [-88, -46, C.white], [-80, -42, C.red]].forEach(([fx, fy, fc]) => { c.fillStyle = fc; c.beginPath(); c.arc(fx, fy, 5, 0, Math.PI * 2); c.fill(); });
|
|
58
|
+
// 头:头光(圆光)+发髻
|
|
59
|
+
c.fillStyle = 'rgba(106,176,142,.28)'; c.beginPath(); c.arc(-56, -24, 30, 0, Math.PI * 2); c.fill(); c.strokeStyle = C.line; c.lineWidth = 1.4; c.stroke();
|
|
60
|
+
c.strokeStyle = C.white; c.lineWidth = 2.5; c.beginPath(); c.arc(-56, -24, 24, 0, Math.PI * 2); c.stroke();
|
|
61
|
+
F(c, (() => { const p = new Path2D(); p.ellipse(-56, -22, 15, 17, 0, 0, Math.PI * 2); return p; })(), C.skin, 2);
|
|
62
|
+
c.fillStyle = '#1e1410'; c.beginPath(); c.ellipse(-52, -38, 13, 9, 0.3, 0, Math.PI * 2); c.fill(); c.beginPath(); c.arc(-46, -48, 7, 0, Math.PI * 2); c.fill();
|
|
63
|
+
c.fillStyle = C.line; c.fillRect(-64, -23, 4, 1.6); c.fillStyle = '#c0392b'; c.beginPath(); c.arc(-66, -14, 1.8, 0, Math.PI * 2); c.fill();
|
|
64
|
+
c.restore();
|
|
65
|
+
}
|
|
66
|
+
function windowPanel(c, t, lt) {
|
|
67
|
+
c.save(); c.beginPath(); c.rect(370, 140, 420, 400); c.clip();
|
|
68
|
+
const sg = c.createLinearGradient(0, 140, 0, 540); sg.addColorStop(0, '#2a5a86'); sg.addColorStop(1, '#4f86a8'); c.fillStyle = sg; c.fillRect(370, 140, 420, 400);
|
|
69
|
+
[[460, 200, 0.9, 0], [700, 450, 1.1, 1], [520, 490, 0.8, 2], [760, 190, 0.7, 3]].forEach(([x, y, s, k]) => cloud(c, ((x - lt * (40 + k * 12) - 370 + 1000) % 520) + 330, y + Math.sin(t * 1.5 + k) * 4, s, t, t * 3 + k));
|
|
70
|
+
apsara(c, t, lt);
|
|
71
|
+
c.restore();
|
|
72
|
+
// 窗框:卷草纹边框(石绿+白卷须)
|
|
73
|
+
c.fillStyle = C.green; c.fillRect(348, 118, 464, 22); c.fillRect(348, 540, 464, 24); c.fillRect(348, 118, 22, 446); c.fillRect(790, 118, 22, 446);
|
|
74
|
+
c.strokeStyle = C.white; c.lineWidth = 2.5;
|
|
75
|
+
const vine = (x0, y0, x1, y1) => { const L = Math.hypot(x1 - x0, y1 - y0), n = Math.floor(L / 34); for (let i = 0; i < n; i++) { const q = (i + 0.5) / n, x = lerp(x0, x1, q), y = lerp(y0, y1, q); c.beginPath(); c.arc(x, y, 7, i % 2 ? 0 : Math.PI, (i % 2 ? 0 : Math.PI) + Math.PI * 1.4); c.stroke(); } };
|
|
76
|
+
vine(359, 129, 801, 129); vine(359, 552, 801, 552); vine(359, 140, 359, 540); vine(801, 140, 801, 540);
|
|
77
|
+
c.strokeStyle = C.line; c.lineWidth = 3; c.strokeRect(348, 118, 464, 446); c.strokeRect(370, 140, 420, 400);
|
|
78
|
+
}
|
|
79
|
+
// ---------- 地:莲花砖 ----------
|
|
80
|
+
const floor = () => P.cached('dh_floor', W, H, (g) => {
|
|
81
|
+
g.fillStyle = '#b8654a'; g.fillRect(0, 704, W, H - 704);
|
|
82
|
+
g.fillStyle = C.redDk; g.fillRect(0, 700, W, 8);
|
|
83
|
+
for (let x = 0; x < W; x += 150) for (let y = 714; y < H; y += 150) {
|
|
84
|
+
g.strokeStyle = 'rgba(70,30,20,.6)'; g.lineWidth = 2; g.strokeRect(x + 3, y + 3, 144, 144);
|
|
85
|
+
g.save(); g.translate(x + 75, y + 75);
|
|
86
|
+
for (let k = 0; k < 8; k++) { g.rotate(Math.PI / 4); g.fillStyle = k % 2 ? C.green2 : C.white; g.beginPath(); g.ellipse(0, -34, 12, 26, 0, 0, Math.PI * 2); g.fill(); g.strokeStyle = C.line; g.lineWidth = 1.6; g.stroke(); }
|
|
87
|
+
g.fillStyle = C.blue2; g.beginPath(); g.arc(0, 0, 16, 0, Math.PI * 2); g.fill(); g.stroke(); g.fillStyle = C.gold; g.beginPath(); g.arc(0, 0, 6, 0, Math.PI * 2); g.fill();
|
|
88
|
+
g.restore(); }
|
|
89
|
+
});
|
|
90
|
+
// ---------- 案与油灯 ----------
|
|
91
|
+
function table(c, t) {
|
|
92
|
+
F(c, new Path2D('M806 616 L1214 616 L1206 650 L814 650 Z'), C.green);
|
|
93
|
+
F(c, new Path2D('M830 650 L1190 650 L1190 700 L830 700 Z'), C.red);
|
|
94
|
+
c.fillStyle = C.redDk; [[880, 660], [990, 660], [1100, 660]].forEach(([x, y]) => { c.beginPath(); c.moveTo(x, y + 34); c.quadraticCurveTo(x + 6, y + 4, x + 40, y + 6); c.quadraticCurveTo(x + 74, y + 4, x + 80, y + 34); c.closePath(); c.fill(); }); // 壸门
|
|
95
|
+
F(c, new Path2D('M838 700 L868 700 L862 905 L842 905 Z'), C.red); F(c, new Path2D('M1152 700 L1182 700 L1178 905 L1158 905 Z'), C.red);
|
|
96
|
+
// 油灯:灯盏+跳动的火苗
|
|
97
|
+
F(c, new Path2D('M880 604 Q910 618 940 604 L934 616 Q910 626 886 616 Z'), C.gold, 2);
|
|
98
|
+
const fl = 1 + Math.sin(t * 17) * 0.12 + Math.sin(t * 29) * 0.08, sw = Math.sin(t * 11) * 4;
|
|
99
|
+
c.save(); c.globalCompositeOperation = 'lighter'; const gg = c.createRadialGradient(910, 585, 2, 910, 585, 60); gg.addColorStop(0, 'rgba(255,200,90,.45)'); gg.addColorStop(1, 'rgba(255,200,90,0)'); c.fillStyle = gg; c.fillRect(850, 525, 120, 120); c.restore();
|
|
100
|
+
F(c, new Path2D(`M902 604 Q${898 + sw} ${598 - 14 * fl} ${910 + sw} ${574 - 14 * fl} Q${922 + sw * 0.5} ${596 - 10 * fl} 918 604 Z`), '#f2a43a', 1.8);
|
|
101
|
+
c.fillStyle = '#fbe7a0'; c.beginPath(); c.ellipse(910 + sw * 0.4, 596, 4, 7 * fl, 0, 0, Math.PI * 2); c.fill();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 披帛:石绿正面+白色描花,带一点翻面(窄处露出土红里子)
|
|
105
|
+
function shawl(c, pts, w, t) {
|
|
106
|
+
const D = KIT.densify(pts, 3), wq = q => w * (0.75 + 0.25 * Math.sin(q * 9 - t * 5)) * (1 - q * 0.25);
|
|
107
|
+
F(c, KIT.ribbon(D, wq), C.green2, 2.2);
|
|
108
|
+
c.strokeStyle = C.white; c.lineWidth = 2; c.beginPath(); D.forEach((p, i) => i ? c.lineTo(p[0], p[1]) : c.moveTo(p[0], p[1])); c.setLineDash([3, 9]); c.stroke(); c.setLineDash([]);
|
|
109
|
+
D.forEach((p, i) => { if (i % 9 !== 4) return; c.fillStyle = C.red; c.beginPath(); c.arc(p[0], p[1], 3.2, 0, Math.PI * 2); c.fill(); });
|
|
110
|
+
}
|
|
111
|
+
// ---------- 少女:唐代供养人 ----------
|
|
112
|
+
function girl(c, t, ch) {
|
|
113
|
+
const G = RIG.girl({ cup: ch.cup, sip: ch.sip, breathe: ch.breathe, hair: 'bun' });
|
|
114
|
+
const A = G.A;
|
|
115
|
+
// 披帛(背后那段):搭过肩、从椅背后飘出一个大 S,末端向外扬(行波,越往下摆幅越大)
|
|
116
|
+
const pbBack = []; for (let i = 0; i <= 26; i++) { const q = i / 26; pbBack.push([A.back[0] - 10 + q * 230 + Math.sin(q * 4.2 - t * 5.5) * 64 * q, A.nape[1] - 6 + q * 400 - Math.max(0, q - 0.7) * 300 * (0.7 + 0.3 * Math.sin(t * 4))]); }
|
|
117
|
+
shawl(c, pbBack, 32, t);
|
|
118
|
+
F(c, G.farSleeve, C.green); F(c, G.farHand, C.skin); F(c, G.farCuff, C.red, 2);
|
|
119
|
+
// 间色裙:红绿竖条(唐代女装最有辨识度的一条)
|
|
120
|
+
F(c, G.skirt, C.red);
|
|
121
|
+
c.save(); c.clip(G.skirt); for (let x = 1090; x < 1470; x += 44) { c.fillStyle = C.green; c.beginPath(); c.moveTo(x + 6, 600); c.lineTo(x + 26, 600); c.lineTo(x + 12 - (x - 1300) * 0.18, 940); c.lineTo(x - 10 - (x - 1300) * 0.18, 940); c.closePath(); c.fill(); }
|
|
122
|
+
c.restore(); c.strokeStyle = C.line; c.lineWidth = LW; c.stroke(G.skirt);
|
|
123
|
+
G.folds.forEach(f => { c.strokeStyle = C.line; c.lineWidth = 1.6; c.stroke(f); });
|
|
124
|
+
F(c, G.shoe, C.ink);
|
|
125
|
+
// 襦:石青短衫,白边交领;高腰红带
|
|
126
|
+
F(c, G.torso, C.blue);
|
|
127
|
+
c.strokeStyle = C.white; c.lineWidth = 7; c.beginPath(); c.moveTo(A.neck[0] - 14, A.neck[1] + 2); c.quadraticCurveTo(A.chest[0] + 6, A.chest[1] - 20, A.chest[0] + 26, A.chest[1] + 40); c.stroke();
|
|
128
|
+
c.fillStyle = C.red; c.beginPath(); c.moveTo(1290, 590); c.lineTo(1422, 594); c.lineTo(1424, 616); c.lineTo(1294, 614); c.closePath(); c.fill(); c.strokeStyle = C.line; c.lineWidth = 2; c.stroke();
|
|
129
|
+
F(c, G.neck, C.skin);
|
|
130
|
+
// 高髻:金发盘在头顶(把 bun 移上去、加大),簪花、金钗
|
|
131
|
+
F(c, G.hairBack, '#e2b850', 2);
|
|
132
|
+
const top = A.headTop, bunC = [top[0] + 18, top[1] - 22];
|
|
133
|
+
F(c, (() => { const p = new Path2D(); p.ellipse(bunC[0], bunC[1], 34, 30, -0.3, 0, Math.PI * 2); p.moveTo(bunC[0] + 40, bunC[1] - 6); p.ellipse(bunC[0] + 22, bunC[1] - 14, 22, 20, 0.4, 0, Math.PI * 2); return p; })(), '#e2b850', 2);
|
|
134
|
+
c.strokeStyle = '#a8782a'; c.lineWidth = 1.6; for (let k = 0; k < 4; k++) { c.beginPath(); c.arc(bunC[0], bunC[1], 10 + k * 7, -2.4, -0.4); c.stroke(); }
|
|
135
|
+
c.strokeStyle = C.gold; c.lineWidth = 3.5; c.beginPath(); c.moveTo(bunC[0] - 50, bunC[1] + 6); c.lineTo(bunC[0] + 40, bunC[1] - 24); c.stroke();
|
|
136
|
+
[[bunC[0] - 30, bunC[1] - 26, C.red], [bunC[0] + 4, bunC[1] - 40, C.white], [bunC[0] + 44, bunC[1] - 30, C.red]].forEach(([x, y, fc], k) => { const sw = Math.sin(t * 5 + k) * 3; for (let j = 0; j < 5; j++) { c.fillStyle = fc; c.beginPath(); c.arc(x + sw + Math.cos(j * 1.26) * 6, y + Math.sin(j * 1.26) * 6, 4.5, 0, Math.PI * 2); c.fill(); } c.fillStyle = C.gold; c.beginPath(); c.arc(x + sw, y, 3, 0, Math.PI * 2); c.fill(); });
|
|
137
|
+
// 脸:白面、红线(铁线描)、花钿、斜红、点唇
|
|
138
|
+
F(c, G.face, C.skin, 2.2, '#8a3a24');
|
|
139
|
+
F(c, G.bangs, '#e2b850', 2);
|
|
140
|
+
c.fillStyle = '#c0392b'; const fh = G.A.forehead; c.save(); c.translate(fh[0] + 10, fh[1] + 12); for (let j = 0; j < 4; j++) { c.rotate(Math.PI / 2); c.beginPath(); c.ellipse(0, -4, 2, 4, 0, 0, Math.PI * 2); c.fill(); } c.restore();
|
|
141
|
+
c.strokeStyle = 'rgba(200,60,50,.32)'; c.lineWidth = 2; c.beginPath(); c.moveTo(G.eye.x + 16, G.eye.y - 4); c.quadraticCurveTo(G.eye.x + 22, G.eye.y + 10, G.eye.x + 16, G.eye.y + 22); c.stroke();
|
|
142
|
+
c.strokeStyle = C.ink; c.lineWidth = ch.blink ? 2 : 2.4; c.stroke(G.lid); if (!ch.blink) { c.fillStyle = C.ink; c.beginPath(); c.ellipse(G.eye.x - 1, G.eye.y + 1.5, 3, 3.4, 0, 0, Math.PI * 2); c.fill(); }
|
|
143
|
+
c.strokeStyle = '#5a3a1a'; c.lineWidth = 3; c.beginPath(); c.moveTo(G.eye.x - 10, G.eye.y - 14); c.quadraticCurveTo(G.eye.x + 2, G.eye.y - 20, G.eye.x + 14, G.eye.y - 15); c.stroke(); // 蛾眉
|
|
144
|
+
c.fillStyle = '#c0392b'; c.beginPath(); c.arc(G.lips[0][0] + 3, G.lips[0][1] + 3, 3.4, 0, Math.PI * 2); c.fill();
|
|
145
|
+
// 近侧手臂:石青窄袖+红袖口,披帛前段绕臂垂下
|
|
146
|
+
F(c, G.upperArm, C.blue); F(c, G.foreArm, C.blue); F(c, G.cuff, C.red, 2);
|
|
147
|
+
const E = A.elbow; const pbF = []; for (let i = 0; i <= 22; i++) { const q = i / 22; pbF.push([E[0] + 6 - q * 70 + Math.sin(q * 5 - t * 6 + 1) * 46 * q, E[1] + 6 + q * 300 - Math.max(0, q - 0.65) * 200 * (0.7 + 0.3 * Math.sin(t * 4.6 + 1))]); }
|
|
148
|
+
shawl(c, pbF, 28, t);
|
|
149
|
+
F(c, G.hand, C.skin, 2, '#8a3a24');
|
|
150
|
+
// 茶盏:越窑青瓷
|
|
151
|
+
const cp = G.cup; c.save(); c.translate(cp.x, cp.y); c.rotate(cp.tilt);
|
|
152
|
+
F(c, new Path2D(`M${-cp.w / 2} 0 Q0 ${cp.h * 1.3} ${cp.w / 2} 0 Z`), '#8ab89a', 2.2);
|
|
153
|
+
c.restore();
|
|
154
|
+
c.fillStyle = C.skin; c.beginPath(); c.arc(G.thumb[0], G.thumb[1], 6.5, 0, Math.PI * 2); c.fill(); c.strokeStyle = '#8a3a24'; c.lineWidth = 1.6; c.stroke();
|
|
155
|
+
return G;
|
|
156
|
+
}
|
|
157
|
+
function cat(c, t, ch) {
|
|
158
|
+
const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe * 2.5, look: Math.sin(t * 2.6) });
|
|
159
|
+
RIG.drawCat(c, K, { orange: C.ochre, white: C.white, stripe: '#8a3a1e', line: C.line, lw: LW, eye: '#5a9a6a', earInner: '#d88a7a' });
|
|
160
|
+
// 项圈:卷草小铃
|
|
161
|
+
c.strokeStyle = C.green; c.lineWidth = 7; c.beginPath(); c.arc(K.headC[0] - 6, K.headC[1] + 18, 52, 0.7, 2.3); c.stroke();
|
|
162
|
+
const bx = K.headC[0] + 4 + Math.sin(t * 8) * 4, by = K.headC[1] + 72; c.fillStyle = C.gold; c.beginPath(); c.arc(bx, by, 9, 0, Math.PI * 2); c.fill(); c.strokeStyle = C.line; c.lineWidth = 2; c.stroke();
|
|
163
|
+
}
|
|
164
|
+
// ---------- 罩层:颜料不匀、龟裂、剥落、褪色 ----------
|
|
165
|
+
const AGE_MASK = (x, y) => { const n = P.fbm(x * 0.006 + 11, y * 0.006 + 4, 4) + 0.18 * P.noise(x * 0.04, y * 0.04); const keep = (x > 1180 && x < 1360 && y > 260 && y < 470); return !keep && n > 0.33; };
|
|
166
|
+
const aging = () => P.cached('dh_age', W, H, (g) => {
|
|
167
|
+
const img = g.createImageData(W, H), d = img.data, pl = P.hex(C.plaster), r = rng(91);
|
|
168
|
+
for (let y = 0; y < H; y++) for (let x = 0; x < W; x++) {
|
|
169
|
+
const i = (y * W + x) * 4;
|
|
170
|
+
const n = P.fbm(x * 0.0032 + 11, y * 0.0032 + 4, 5) + 0.12 * P.noise(x * 0.03, y * 0.03);
|
|
171
|
+
const keep = (x > 1180 && x < 1360 && y > 260 && y < 470) || (x > 380 && x < 720 && y > 530 && y < 920) || (x > 1070 && x < 1530 && y > 240 && y < 950); // 角色身上不剥落(剥落在人身上会读成「衣服破洞」)
|
|
172
|
+
if (!keep && n > 0.3) { const v = (r() - 0.5) * 22 + (n - 0.3) * 60; d[i] = pl[0] + v; d[i + 1] = pl[1] + v; d[i + 2] = pl[2] + v * 0.8; d[i + 3] = 255; }
|
|
173
|
+
else if (!keep && n > 0.28) { d[i] = 70; d[i + 1] = 45; d[i + 2] = 30; d[i + 3] = 150; } // 剥落边缘的暗线
|
|
174
|
+
else { const m = P.fbm(x * 0.02, y * 0.02 + 7, 3); d[i] = 255; d[i + 1] = 245; d[i + 2] = 225; d[i + 3] = clamp(m * 0.5 + 0.1) * 80; } // 颜料粉化发白
|
|
175
|
+
}
|
|
176
|
+
g.putImageData(img, 0, 0);
|
|
177
|
+
// 龟裂
|
|
178
|
+
g.strokeStyle = 'rgba(50,25,15,.35)'; g.lineWidth = 1;
|
|
179
|
+
for (let k = 0; k < 500; k++) { let x = r() * W, y = r() * H, a = r() * 6.28; g.beginPath(); g.moveTo(x, y); for (let s = 0; s < 5 + r() * 8; s++) { a += (r() - .5) * 1.4; x += Math.cos(a) * (5 + r() * 12); y += Math.sin(a) * (5 + r() * 12); g.lineTo(x, y); } g.stroke(); }
|
|
180
|
+
// 顶部烟熏
|
|
181
|
+
const sg = g.createLinearGradient(0, 0, 0, 380); sg.addColorStop(0, 'rgba(40,20,10,.35)'); sg.addColorStop(1, 'rgba(40,20,10,0)'); g.fillStyle = sg; g.fillRect(0, 0, W, 380);
|
|
182
|
+
});
|
|
183
|
+
const FLAKES = (() => { const r = rng(55), o = []; let n = 0; while (o.length < 10 && n < 2000) { n++; const x = 100 + r() * 1700, y = 120 + r() * 700; { const n = P.fbm(x * 0.0032 + 11, y * 0.0032 + 4, 5); if (n > 0.26 && n < 0.3) o.push([x, y, 0.05 + r() * 0.7, 8 + r() * 10, r()]); } } return o; })();
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
draw(c, lt, t) {
|
|
187
|
+
const ch = KIT.choreo(lt, t);
|
|
188
|
+
c.drawImage(ground(), 0, 0);
|
|
189
|
+
windowPanel(c, t, lt);
|
|
190
|
+
c.drawImage(floor(), 0, 0);
|
|
191
|
+
table(c, t);
|
|
192
|
+
cat(c, t, ch);
|
|
193
|
+
const G = girl(c, t, ch);
|
|
194
|
+
// 热气:几道白色卷云线
|
|
195
|
+
c.save(); c.strokeStyle = C.white; c.lineWidth = 3; c.lineCap = 'round'; [0, 1].forEach(k => { c.beginPath(); for (let i = 0; i <= 18; i++) { const q = i / 18, x = G.cup.x - 4 + k * 12 - q * 50 * ch.cup + Math.sin(q * 6 - t * 6 + k) * 8 * q, y = G.cup.y - 6 - q * 70; i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); const e = [G.cup.x - 4 + k * 12 - 50 * ch.cup + Math.sin(6 - t * 6 + k) * 8, G.cup.y - 76]; c.beginPath(); c.arc(e[0] + 5, e[1], 5, Math.PI, Math.PI * 2.6); c.stroke(); }); c.restore();
|
|
196
|
+
// 天花:花瓣从窟顶散落
|
|
197
|
+
P.particles(30, 13, t, { x0: 0, x1: W, y0: 60, y1: H, speed: 90, drift: 50, life: 3 }).forEach(p => { c.save(); c.translate(p.x, p.y); c.rotate(p.a); c.scale(p.s * 1.6, p.s * 1.6 * (Math.abs(Math.cos(t * 3 + p.i)) * 0.8 + 0.2));
|
|
198
|
+
const fc = [C.white, C.red, C.green2, '#e8a0a0'][p.i % 4]; for (let j = 0; j < 4; j++) { c.rotate(Math.PI / 2); c.fillStyle = fc; c.beginPath(); c.ellipse(0, -7, 4, 8, 0, 0, Math.PI * 2); c.fill(); c.strokeStyle = C.line; c.lineWidth = 1.2; c.stroke(); } c.restore(); });
|
|
199
|
+
// 罩层
|
|
200
|
+
c.save(); c.globalCompositeOperation = 'saturation'; c.fillStyle = 'rgba(128,128,128,.3)'; c.fillRect(0, 0, W, H); c.restore();
|
|
201
|
+
c.save(); c.globalCompositeOperation = 'multiply'; c.globalAlpha = 0.5; c.drawImage(P.texture('dh_dirt', '#e8dcc8', { scale: 0.002, amt: 40, grain: 20, seed: 9 }), 0, 0); c.restore();
|
|
202
|
+
c.drawImage(aging(), 0, 0);
|
|
203
|
+
// 颜料片掉落:剥落边缘处一小片颜料脱开、旋转着落下
|
|
204
|
+
FLAKES.forEach(([x, y, t0, s, ph], k) => { const q = (lt - t0 * 0.9); if (q < 0) return; const fy = y + q * q * 600, fx = x + Math.sin(q * 6 + ph * 6) * 12; if (fy > H) return;
|
|
205
|
+
c.save(); c.translate(fx, fy); c.rotate(q * 6 + ph * 3); c.scale(1, Math.cos(q * 9 + ph)); c.fillStyle = [C.red, C.green, C.blue2][k % 3]; c.beginPath(); c.moveTo(-s, -s * 0.4); c.lineTo(s * 0.6, -s * 0.8); c.lineTo(s, s * 0.5); c.lineTo(-s * 0.3, s * 0.7); c.closePath(); c.fill(); c.restore(); });
|
|
206
|
+
},
|
|
207
|
+
label(c, lt) {
|
|
208
|
+
// 榜题框:浅色长方块、墨书,带剥落
|
|
209
|
+
c.save(); c.fillStyle = '#e6d6b2'; c.fillRect(1500, 52, 380, 150); c.strokeStyle = C.line; c.lineWidth = 4; c.strokeRect(1500, 52, 380, 150); c.strokeRect(1508, 60, 364, 134);
|
|
210
|
+
c.restore();
|
|
211
|
+
KIT.label(c, { title: '敦煌壁画', sub: '莫高窟 · 盛唐', tFont: '78px "LXGWWenKai-500"', sFont: '32px "LXGWWenKai-500"', tCol: '#2a1a14', sCol: C.redDk, x: 1858, y: 134, sy: 180, spacing: 6 });
|
|
212
|
+
c.save(); const r = rng(4); c.fillStyle = 'rgba(216,198,162,.9)'; for (let i = 0; i < 14; i++) { const x = 1500 + r() * 380, y = 52 + r() * 150; c.beginPath(); KIT.blob(c, x, y, 4 + r() * 10, i, 0.3, 8); c.fill(); } c.restore();
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
})();
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// 草间弥生无限波点(《Infinity Mirror Room》1965–、黄南瓜 1994)——纯代码。
|
|
2
|
+
// 管线:①红房间:墙/地铺白点(大小两套网格,地板压扁成透视椭圆)→ ②窗=无限镜屋:黑底彩灯点从深处飞来、镜面倒影
|
|
3
|
+
// → ③桌上黄南瓜(瓣+黑点)→ ④角色:平涂+细黑线;红裙白点、猫的橘色区白点、白色区红点
|
|
4
|
+
// → ⑤会动的:所有点按「从猫出发的行波」一胀一缩(蠕动),裙上的点顺着裙子往下爬,镜屋灯飞来,南瓜呼吸
|
|
5
|
+
// → ⑥结尾 0.3s:自我消融(点越胀越大吞掉一切)
|
|
6
|
+
SCENES['21_kusama'] = (() => {
|
|
7
|
+
const W = 1920, H = 1080, { clamp, lerp, rng } = U, P = PAINT;
|
|
8
|
+
const { ss } = U;
|
|
9
|
+
const C = { red: '#d8181e', redDk: '#a80e14', white: '#fbf6ee', yellow: '#f6d02a', black: '#141210', skin: '#f6dcc8' };
|
|
10
|
+
const O = [540, 760]; // 行波原点(猫)
|
|
11
|
+
const wave = (x, y, t, k = 1) => 0.82 + 0.28 * Math.sin(Math.hypot(x - O[0], y - O[1]) * 0.012 - t * 6 * k);
|
|
12
|
+
const grid = (x0, y0, x1, y1, sp, seed, jit = 0.25) => { const r = rng(seed), o = []; for (let y = y0, j = 0; y < y1; y += sp * 0.87, j++) for (let x = x0 + (j % 2) * sp / 2; x < x1; x += sp) o.push([x + (r() - .5) * sp * jit, y + (r() - .5) * sp * jit, 0.75 + r() * 0.5, r() * 6.28, 0.85 + r() * 0.3]); return o; };
|
|
13
|
+
const WALL_BIG = grid(-40, 20, W + 40, 720, 104, 3), WALL_SM = grid(12, 64, W + 40, 720, 104, 4, 0.5);
|
|
14
|
+
const FLOOR = (() => { const r = rng(6), o = []; for (let row = 0; row < 9; row++) { const q = row / 8, y = 712 + Math.pow(q, 1.4) * 360, sp = 46 + q * 70, rr = 6 + q * 22; for (let x = -40 + (row % 2) * sp / 2; x < W + 40; x += sp) o.push([x + (r() - .5) * 10, y, rr * (0.8 + r() * 0.4), r() * 6.28, 0.9]); } return o; })();
|
|
15
|
+
const dot = (c, x, y, r, rot, asp, col) => { c.fillStyle = col; c.beginPath(); c.ellipse(x, y, r, r * asp, rot, 0, Math.PI * 2); c.fill(); };
|
|
16
|
+
let OBL = 0; // 消融程度(结尾)
|
|
17
|
+
|
|
18
|
+
function room(c, t) {
|
|
19
|
+
c.fillStyle = C.red; c.fillRect(0, 0, W, 708);
|
|
20
|
+
const fg = c.createLinearGradient(0, 700, 0, H); fg.addColorStop(0, '#c0141a'); fg.addColorStop(1, '#e02024'); c.fillStyle = fg; c.fillRect(0, 700, W, H - 700);
|
|
21
|
+
c.fillStyle = C.redDk; c.fillRect(0, 700, W, 6);
|
|
22
|
+
const g = 1 + OBL * 2.2;
|
|
23
|
+
WALL_BIG.forEach(([x, y, s, rot, asp]) => dot(c, x, y, 30 * s * wave(x, y, t) * g, rot, asp, C.white));
|
|
24
|
+
WALL_SM.forEach(([x, y, s, rot, asp]) => dot(c, x, y, 10 * s * wave(x, y, t, 1.3) * g, rot, asp, C.white));
|
|
25
|
+
FLOOR.forEach(([x, y, rr, rot]) => dot(c, x, y, rr * wave(x, y, t) * g, 0, 0.42, C.white));
|
|
26
|
+
}
|
|
27
|
+
// 无限镜屋:3D 点向镜头飞来(z 循环),投影+光晕;地面镜像
|
|
28
|
+
// 灯在一个 3D 格子上(镜面无限反射的感觉来自规则重复),z 循环向镜头飞来
|
|
29
|
+
const LIGHTS = (() => { const r = rng(9), o = []; for (let gx = -6; gx <= 6; gx++) for (let gy = -3; gy <= 3; gy++) for (let gz = 0; gz < 3; gz++) o.push([gx * 0.42 + (r() - .5) * 0.06, gy * 0.38 + (r() - .5) * 0.06, (gz + r() * 0.3) / 3, ['#ff3a4a', '#ffd43a', '#3aa8ff', '#6aff7a', '#ff7ad0', '#ffffff'][(r() * 6) | 0]]); return o; })();
|
|
30
|
+
function mirrorRoom(c, t) {
|
|
31
|
+
c.save(); c.beginPath(); c.rect(370, 140, 420, 400); c.clip();
|
|
32
|
+
c.fillStyle = '#05040a'; c.fillRect(370, 140, 420, 400);
|
|
33
|
+
const cx = 580, cy = 330, F = 150;
|
|
34
|
+
c.strokeStyle = 'rgba(120,120,160,.22)'; c.lineWidth = 1; [[370, 140], [790, 140], [370, 540], [790, 540]].forEach(([x, y]) => { c.beginPath(); c.moveTo(cx, cy); c.lineTo(x, y); c.stroke(); });
|
|
35
|
+
c.globalCompositeOperation = 'lighter';
|
|
36
|
+
const sorted = LIGHTS.map(([x, y, z0, col]) => [x, y, ((z0 - t * 0.3) % 1 + 1) % 1 * 3.6 + 0.4, col]).sort((a, b) => b[2] - a[2]);
|
|
37
|
+
for (const [x, y, z, col] of sorted) {
|
|
38
|
+
const px = cx + x * F / z, py = cy + y * F / z, r = 3.2 / z + 0.6; if (px < 350 || px > 810 || py < 120 || py > 560) continue;
|
|
39
|
+
const fade = clamp((4 - z) / 1.0) * clamp((z - 0.4) / 0.25);
|
|
40
|
+
c.globalAlpha = fade * 0.5; const g = c.createRadialGradient(px, py, 0, px, py, r * 3.2); g.addColorStop(0, col); g.addColorStop(1, col + '00'); c.fillStyle = g; c.beginPath(); c.arc(px, py, r * 3.2, 0, Math.PI * 2); c.fill();
|
|
41
|
+
c.globalAlpha = fade; c.fillStyle = col; c.beginPath(); c.arc(px, py, r, 0, Math.PI * 2); c.fill();
|
|
42
|
+
}
|
|
43
|
+
c.restore();
|
|
44
|
+
// 窗框:黄底黑点
|
|
45
|
+
c.save(); c.fillStyle = C.yellow; c.fillRect(348, 118, 464, 22); c.fillRect(348, 540, 464, 24); c.fillRect(348, 118, 22, 446); c.fillRect(790, 118, 22, 446);
|
|
46
|
+
c.fillStyle = C.black; for (let x = 360; x < 806; x += 22) { dot(c, x, 129, 6 * wave(x, 129, t), 0, 1, C.black); dot(c, x, 552, 6 * wave(x, 552, t), 0, 1, C.black); } for (let y = 150; y < 540; y += 22) { dot(c, 359, y, 6 * wave(359, y, t), 0, 1, C.black); dot(c, 801, y, 6 * wave(801, y, t), 0, 1, C.black); }
|
|
47
|
+
c.strokeStyle = C.black; c.lineWidth = 3; c.strokeRect(348, 118, 464, 446); c.restore();
|
|
48
|
+
}
|
|
49
|
+
// 黄南瓜:5 瓣,瓣间黑线,每瓣按行排黑点(中间大、两边小),整体呼吸
|
|
50
|
+
function pumpkin(c, t) {
|
|
51
|
+
const cx = 960, cy = 540, br = 1 + Math.sin(t * 5) * 0.025;
|
|
52
|
+
c.save(); c.translate(cx, cy + 78); c.scale(br, 2 - br); c.translate(-cx, -(cy + 78));
|
|
53
|
+
const lobes = [[-88, 46, 70], [-44, 58, 74], [0, 62, 80], [44, 58, 74], [88, 46, 70]];
|
|
54
|
+
[0, 4, 1, 3, 2].forEach(k => { const [dx, rx, ry] = lobes[k]; const p = new Path2D(); p.ellipse(cx + dx, cy + 8, rx, ry, 0, 0, Math.PI * 2);
|
|
55
|
+
c.fillStyle = C.yellow; c.fill(p); c.strokeStyle = C.black; c.lineWidth = 4; c.stroke(p);
|
|
56
|
+
c.save(); c.clip(p);
|
|
57
|
+
for (let row = -4; row <= 4; row++) { const y = cy + 8 + row * 17; const wRow = Math.sqrt(Math.max(0, 1 - Math.pow(row * 17 / ry, 2))) * rx; for (let col = -3; col <= 3; col++) { const x = cx + dx + col * wRow * 0.3; const edge = 1 - Math.abs(col) / 4; const r = (3 + 6.5 * edge) * (1 - Math.abs(row) / 6) * wave(x, y, t, 1.5); if (r > 0.8) dot(c, x, y, r, 0, 1, C.black); } }
|
|
58
|
+
c.restore(); });
|
|
59
|
+
c.fillStyle = '#2a3a14'; c.beginPath(); c.moveTo(cx - 10, cy - 50); c.quadraticCurveTo(cx - 14, cy - 86, cx + 6, cy - 92); c.lineTo(cx + 14, cy - 84); c.quadraticCurveTo(cx + 4, cy - 70, cx + 10, cy - 50); c.closePath(); c.fill(); c.strokeStyle = C.black; c.lineWidth = 3; c.stroke();
|
|
60
|
+
c.restore();
|
|
61
|
+
}
|
|
62
|
+
function table(c, t) {
|
|
63
|
+
c.fillStyle = C.yellow; c.strokeStyle = C.black; c.lineWidth = 4;
|
|
64
|
+
[[806, 616, 408, 34], [834, 650, 352, 46], [842, 696, 26, 210], [1152, 696, 26, 210]].forEach(([x, y, w, h]) => { c.fillRect(x, y, w, h); c.strokeRect(x, y, w, h);
|
|
65
|
+
c.save(); c.beginPath(); c.rect(x, y, w, h); c.clip(); for (let yy = y + 8; yy < y + h; yy += 18) for (let xx = x + 8 + ((yy / 18) | 0) % 2 * 9; xx < x + w; xx += 18) dot(c, xx, yy, 4.2 * wave(xx, yy, t), 0, 1, C.black); c.restore(); });
|
|
66
|
+
}
|
|
67
|
+
// 角色:平涂(RIG fill)+各区域点+细黑线
|
|
68
|
+
function chars(c, t, lt, ch) {
|
|
69
|
+
const L = P.scratch('ku_chars'), g = L.getContext('2d'); g.reset();
|
|
70
|
+
const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe });
|
|
71
|
+
const G = RIG.girl({ cup: ch.cup, sip: ch.sip, breathe: ch.breathe, hair: 'bun' });
|
|
72
|
+
const gp = { skin: C.skin, hair: '#f2c84a', hairLine: '#c8962a', dress: C.red, line: C.black, lw: 3, cheek: 'rgba(240,110,110,.4)', lip: '#d8181e', iris: '#2a2a2a', cuff: C.white, apron: null, cupBody: C.yellow, cupRim: '#6a3a14', shoe: C.black };
|
|
73
|
+
RIG.drawCat(g, K, { orange: '#ec8a2a', white: C.white, stripe: '#b85a14', line: C.black, lw: 3, eye: '#2a2a2a' });
|
|
74
|
+
// 猫:橘色区白点、白色区红点
|
|
75
|
+
const catDots = (path, col, sp, r0, dy) => { g.save(); g.clip(path); for (let y = 540 + dy, j = 0; y < 920; y += sp * 0.87, j++) for (let x = 380 + (j % 2) * sp / 2; x < 720; x += sp) dot(g, x, y, r0 * wave(x, y, t) * (1 + OBL * 2), 0, 1, col); g.restore(); };
|
|
76
|
+
catDots(K.body, C.white, 34, 7, (t * 30) % (34 * 0.87 * 2)); catDots(K.head, C.white, 26, 5, 0);
|
|
77
|
+
RIG.drawCat(g, K, { line: C.black, lw: 3, mode: 'line', eye: '#2a2a2a' });
|
|
78
|
+
// 少女
|
|
79
|
+
RIG.drawGirl(g, G, { ...gp, mode: 'fill' });
|
|
80
|
+
// 红裙白点:顺着裙子往下爬(点的 y 随时间平移,爬出下摆再从腰上出来)
|
|
81
|
+
const dress = new Path2D(); [G.skirt, G.torso, G.farSleeve].forEach(p => dress.addPath(p));
|
|
82
|
+
g.save(); g.clip(dress); const off = (t * 46) % (52 * 0.87 * 2); // 周期必须是「两行」的行距(奇偶行错位),取 52 会在回绕时整片跳一帧
|
|
83
|
+
for (let y = 380 - 52, j = 0; y < 960; y += 52 * 0.87, j++) for (let x = 1080 + (j % 2) * 26; x < 1480; x += 52) dot(g, x, y + off, 13 * wave(x, y, t) * (1 + OBL * 2.2), 0.2, 0.92, C.white);
|
|
84
|
+
g.restore();
|
|
85
|
+
const arm = new Path2D(); arm.addPath(G.upperArm); arm.addPath(G.foreArm);
|
|
86
|
+
g.fillStyle = C.red; g.fill(arm);
|
|
87
|
+
g.save(); g.clip(arm); for (let y = 300, j = 0; y < 700; y += 30, j++) for (let x = 1130 + (j % 2) * 15; x < 1420; x += 30) dot(g, x, y + off * 0.6, 8 * wave(x, y, t), 0, 1, C.white); g.restore();
|
|
88
|
+
RIG.drawGirl(g, G, { ...gp, mode: 'line', lw: 4.5 });
|
|
89
|
+
KIT.nearArm(g, G, { fill: C.red, line: C.black, lw: 4, skin: C.skin, cuff: C.white, cupBody: C.yellow, cupRim: '#6a3a14', pattern: gg => { for (let y = 300, j = 0; y < 700; y += 30, j++) for (let x = 1130 + (j % 2) * 15; x < 1420; x += 30) dot(gg, x, y + (off * 0.6) % 60, 8 * wave(x, y, t), 0, 1, C.white); } });
|
|
90
|
+
// 脸颊上画了两个点(草间的身体彩绘)
|
|
91
|
+
dot(g, G.cheek[0] - 4, G.cheek[1] + 2, 5 * wave(G.cheek[0], G.cheek[1], t), 0, 1, C.red);
|
|
92
|
+
// 发箍:红底白点蝴蝶结
|
|
93
|
+
const ht = G.A.headTop; g.fillStyle = C.red; g.beginPath(); g.ellipse(ht[0] + 34, ht[1] + 6, 24, 13, 0.4, 0, Math.PI * 2); g.ellipse(ht[0] + 70, ht[1] + 26, 22, 12, 0.9, 0, Math.PI * 2); g.fill(); g.strokeStyle = C.black; g.lineWidth = 2.5; g.stroke();
|
|
94
|
+
dot(g, ht[0] + 34, ht[1] + 6, 5, 0, 1, C.white); dot(g, ht[0] + 70, ht[1] + 26, 5, 0, 1, C.white);
|
|
95
|
+
c.drawImage(L, 0, 0);
|
|
96
|
+
return G;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
draw(c, lt, t) {
|
|
101
|
+
const ch = KIT.choreo(lt, t);
|
|
102
|
+
OBL = Math.pow(ss(1.0, 1.26, lt), 1.4); // 1.26 后停住 0.15s,给结尾一个落点
|
|
103
|
+
room(c, t);
|
|
104
|
+
mirrorRoom(c, t);
|
|
105
|
+
table(c, t);
|
|
106
|
+
pumpkin(c, t);
|
|
107
|
+
const G = chars(c, t, lt, ch);
|
|
108
|
+
// 热气:一串由小到大的白点往上飘(热气也是点)
|
|
109
|
+
for (let i = 0; i < 7; i++) { const q = ((t * 1.4 + i / 7) % 1); dot(c, G.cup.x - 4 - q * 50 * ch.cup + Math.sin(q * 6 + t * 3) * 10, G.cup.y - 10 - q * 90, 3 + q * 7, 0, 1, `rgba(251,246,238,${1 - q})`); }
|
|
110
|
+
// 自我消融:最后 0.3s,全屏红底白点从猫的位置一圈圈胀开,把人、猫、南瓜都「消」进波点里(停在点阵,不是白屏)
|
|
111
|
+
if (OBL > 0) { const sp = 120; c.save(); c.globalAlpha = clamp(OBL * 1.4); c.fillStyle = C.red; c.fillRect(0, 0, W, H); c.globalAlpha = 1;
|
|
112
|
+
for (let y = 0, j = 0; y < H + sp; y += sp * 0.87, j++) for (let x = (j % 2) * sp / 2; x < W + sp; x += sp) { const d = Math.hypot(x - O[0], y - O[1]); const r = clamp(OBL * 1.5 - d / 2600) * 0.47 * sp * wave(x, y, t); if (r > 0.5) dot(c, x, y, r, 0, 1, C.white); }
|
|
113
|
+
c.restore(); }
|
|
114
|
+
},
|
|
115
|
+
label(c, lt, t) {
|
|
116
|
+
c.save(); c.fillStyle = C.yellow; c.strokeStyle = C.black; c.lineWidth = 5;
|
|
117
|
+
c.beginPath(); c.roundRect(1488, 44, 392, 160, 80); c.fill(); c.stroke();
|
|
118
|
+
c.beginPath(); c.roundRect(1488, 44, 392, 160, 80); c.clip();
|
|
119
|
+
[[1520, 76, 12], [1530, 172, 9], [1850, 80, 10], [1858, 170, 13], [1690, 60, 6], [1612, 192, 7], [1780, 196, 6]].forEach(([x, y, r]) => dot(c, x, y, r * wave(x, y, t, 1.4), 0, 1, C.black));
|
|
120
|
+
c.restore();
|
|
121
|
+
KIT.label(c, { title: '无限波点', sub: 'YAYOI KUSAMA · 1965–', tFont: '74px "LXGWWenKai-500"', sFont: '26px "Poppins-800"', tCol: C.black, sCol: C.red, x: 1838, y: 128, sy: 172, spacing: 6 });
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
})();
|