@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,255 @@
|
|
|
1
|
+
// 1966 漫威银河时代(Jack Kirby + 四色胶印)——纯代码。
|
|
2
|
+
// 管线:同一份几何画两遍 →
|
|
3
|
+
// 色版层:纸+平涂+本戴网点(C 15°、M 75°、Y 0°),整层偏移 (4,3) = 套色不准
|
|
4
|
+
// 墨线层:白填+黑描边+黑块阴影(spotted blacks)+羽化排线(feathering)+ Kirby krackle 黑点
|
|
5
|
+
// 合成:色版 → 墨线 multiply(墨线层白色=透明)。墨线层逐部件「白填→阴影→描边」,后画的部件盖掉前面的线,天然只剩看得见的线。
|
|
6
|
+
// 母题:窗外宇宙能量流里的 krackle 黑点群 12fps 流动、端杯速度线、SLURRP! 拟声大字弹出、右侧分格猫眼特写瞳孔追杯、聚焦线闪。
|
|
7
|
+
SCENES['27_kirby'] = (() => {
|
|
8
|
+
const W = 1920, H = 1080, TAU = Math.PI * 2, { clamp, lerp } = U, P = PAINT;
|
|
9
|
+
const INK = '#141212', PAPER = '#f4ecd6';
|
|
10
|
+
const C = { cyan: '#2a9fd8', blue: '#1f3f9e', mag: '#e0287a', red: '#e3262d', yel: '#ffd62a', org: '#f6921e', orgD: '#d2541c',
|
|
11
|
+
skin: '#fbd9b8', wall: '#cfe7f2', floor: '#2b3f9c', wood: '#f2a524', space: '#121a52', energy: '#ffe14a', energy2: '#ff8a2a', planet: '#b04ac8' };
|
|
12
|
+
const LIGHT = [-0.86, -0.5]; // 光从窗(左上)来,阴影在右下边缘
|
|
13
|
+
|
|
14
|
+
// ---- 墨线层工具:阴影月牙 + 羽化排线 ----
|
|
15
|
+
const big = (() => { const p = new Path2D(); p.rect(-50, -50, W + 100, H + 100); return p; })();
|
|
16
|
+
const outsideShift = (path, d) => { const m = new Path2D(); m.addPath(big); m.addPath(path, new DOMMatrix().translate(LIGHT[0] * d, LIGHT[1] * d)); return m; };
|
|
17
|
+
function shade(g, path, d1 = 14, d2 = 30, gap = 9) {
|
|
18
|
+
// 羽化带:path ∩ ¬shift(d2),画沿光方向的平行短线
|
|
19
|
+
g.save(); g.clip(path); g.clip(outsideShift(path, d2), 'evenodd');
|
|
20
|
+
g.strokeStyle = INK; g.lineWidth = 2.6; g.lineCap = 'butt'; g.beginPath();
|
|
21
|
+
const a = Math.atan2(LIGHT[1], LIGHT[0]), nx = -Math.sin(a), ny = Math.cos(a);
|
|
22
|
+
for (let k = -260; k < 260; k++) { const ox = 960 + nx * k * gap, oy = 540 + ny * k * gap; g.moveTo(ox - LIGHT[0] * 1400, oy - LIGHT[1] * 1400); g.lineTo(ox + LIGHT[0] * 1400, oy + LIGHT[1] * 1400); }
|
|
23
|
+
g.stroke(); g.restore();
|
|
24
|
+
// 黑块:path ∩ ¬shift(d1)
|
|
25
|
+
g.save(); g.clip(path); g.clip(outsideShift(path, d1), 'evenodd'); g.fillStyle = INK; g.fillRect(0, 0, W, H); g.restore();
|
|
26
|
+
}
|
|
27
|
+
// 一个部件:色版层填色;墨线层白填→阴影→描边
|
|
28
|
+
const mkPart = (g, mode) => (path, col, o = {}) => {
|
|
29
|
+
if (!path) return;
|
|
30
|
+
if (mode === 'color') { g.fillStyle = col; g.fill(path); return; }
|
|
31
|
+
g.fillStyle = '#fff'; g.fill(path);
|
|
32
|
+
if (o.shade) shade(g, path, o.shade[0], o.shade[1]);
|
|
33
|
+
if (o.lw !== 0) { g.strokeStyle = INK; g.lineWidth = o.lw || 6; g.lineJoin = 'round'; g.stroke(path); }
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// ---- Kirby krackle:一簇大小不一的黑点,按 12fps 换形 ----
|
|
37
|
+
function krackle(g, cx, cy, R, seed, t, n = 22) {
|
|
38
|
+
const st = Math.floor(t * 12), r = U.rng(seed * 131 + st * 7);
|
|
39
|
+
g.fillStyle = INK;
|
|
40
|
+
for (let i = 0; i < n; i++) {
|
|
41
|
+
const a = r() * TAU, d = Math.pow(r(), 0.7) * R, rr = R * (0.05 + 0.2 * (1 - d / R) * r()) * (0.8 + 0.3 * Math.sin(t * 18 + i));
|
|
42
|
+
g.beginPath(); g.arc(cx + Math.cos(a) * d, cy + Math.sin(a) * d * 0.8, Math.max(2, rr), 0, TAU); g.fill();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ---- 窗外:宇宙+能量流 ----
|
|
47
|
+
const WIN = [372, 142, 416, 396];
|
|
48
|
+
const stream = (s, w) => { // 能量流中线:从左下到右上的 S 形
|
|
49
|
+
const x = lerp(330, 840, s), y = lerp(560, 120, s) + Math.sin(s * 5.2 + 0.4) * 46; return [x, y + w];
|
|
50
|
+
};
|
|
51
|
+
function windowArt(g, mode, t) {
|
|
52
|
+
const [x, y, w, h] = WIN;
|
|
53
|
+
g.save(); g.beginPath(); g.rect(x, y, w, h); g.clip();
|
|
54
|
+
if (mode === 'color') {
|
|
55
|
+
g.fillStyle = C.space; g.fillRect(x, y, w, h);
|
|
56
|
+
// 星云网点(M 75°)
|
|
57
|
+
g.fillStyle = P.dotPattern(g, 'neb', 10, 3.2, C.mag, null, 75); g.beginPath(); g.ellipse(700, 230, 160, 90, -0.4, 0, TAU); g.fill();
|
|
58
|
+
// 行星
|
|
59
|
+
g.lineWidth = 9; g.strokeStyle = C.yel; g.beginPath(); g.ellipse(470, 230, 112, 20, -0.25, Math.PI, TAU); g.stroke();
|
|
60
|
+
g.fillStyle = C.planet; g.beginPath(); g.arc(470, 230, 64, 0, TAU); g.fill();
|
|
61
|
+
g.beginPath(); g.ellipse(470, 230, 112, 20, -0.25, 0, Math.PI); g.stroke();
|
|
62
|
+
// 能量流带
|
|
63
|
+
const band = new Path2D(); for (let i = 0; i <= 40; i++) band.lineTo(...stream(i / 40, -58)); for (let i = 40; i >= 0; i--) band.lineTo(...stream(i / 40, 58)); band.closePath();
|
|
64
|
+
g.fillStyle = C.energy2; g.fill(band);
|
|
65
|
+
const core = new Path2D(); for (let i = 0; i <= 40; i++) core.lineTo(...stream(i / 40, -34)); for (let i = 40; i >= 0; i--) core.lineTo(...stream(i / 40, 34)); core.closePath();
|
|
66
|
+
g.fillStyle = C.energy; g.fill(core);
|
|
67
|
+
// 星
|
|
68
|
+
g.fillStyle = '#fff'; [[400, 170, 7], [760, 420, 6], [560, 170, 5], [740, 170, 8], [420, 470, 5]].forEach(([sx, sy, r], k) => { const tw = 0.7 + 0.4 * Math.sin(t * 9 + k * 2); g.fill(KIT.star(sx, sy, r * 2.6 * tw, r * 0.5, 4)); });
|
|
69
|
+
} else {
|
|
70
|
+
g.fillStyle = '#fff'; g.fillRect(x, y, w, h);
|
|
71
|
+
// 行星阴影
|
|
72
|
+
g.strokeStyle = INK; g.lineWidth = 3; g.beginPath(); g.ellipse(470, 230, 112, 20, -0.25, Math.PI, TAU); g.stroke(); const pl = new Path2D(); pl.arc(470, 230, 64, 0, TAU); g.fillStyle = '#fff'; g.fill(pl); shade(g, pl, 18, 34, 8); g.strokeStyle = INK; g.lineWidth = 5; g.stroke(pl); g.lineWidth = 3; g.beginPath(); g.ellipse(470, 230, 112, 20, -0.25, 0, Math.PI); g.stroke();
|
|
73
|
+
// krackle:沿能量流移动(300px/s),每簇 12fps 换形
|
|
74
|
+
for (let k = 0; k < 6; k++) { const s = ((k / 6 + t * 0.42) % 1); const [cx, cy] = stream(s, Math.sin(k * 2.3) * 12); krackle(g, cx, cy, 44 + (k % 3) * 12, k + 1, t, 26); }
|
|
75
|
+
// 能量流边缘的锯齿墨线
|
|
76
|
+
g.strokeStyle = INK; g.lineWidth = 4;
|
|
77
|
+
[-58, 58].forEach(o => { g.beginPath(); for (let i = 0; i <= 40; i++) { const [px, py] = stream(i / 40, o + (i % 2 ? 6 : -6)); i ? g.lineTo(px, py) : g.moveTo(px, py); } g.stroke(); });
|
|
78
|
+
}
|
|
79
|
+
g.restore();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ---- 室内底版 ----
|
|
83
|
+
function room(g, mode, t) {
|
|
84
|
+
const part = mkPart(g, mode);
|
|
85
|
+
if (mode === 'color') {
|
|
86
|
+
g.fillStyle = PAPER; g.fillRect(0, 0, W, H);
|
|
87
|
+
g.fillStyle = C.wall; g.fillRect(0, 0, 1540, 700);
|
|
88
|
+
g.fillStyle = P.dotPattern(g, 'wallc', 9, 2.7, C.cyan, null, 15); g.fillRect(0, 0, 1540, 700);
|
|
89
|
+
g.fillStyle = C.floor; g.fillRect(0, 700, 1540, 380);
|
|
90
|
+
g.fillStyle = P.dotPattern(g, 'floorm', 9, 3.2, '#7a2a9a', null, 75); g.fillRect(0, 700, 1540, 380);
|
|
91
|
+
} else {
|
|
92
|
+
g.fillStyle = '#fff'; g.fillRect(0, 0, W, H);
|
|
93
|
+
// 地板缝(Kirby 的方块透视)+地平线
|
|
94
|
+
g.strokeStyle = INK; g.lineWidth = 5; g.beginPath(); g.moveTo(0, 700); g.lineTo(1540, 700); g.stroke();
|
|
95
|
+
g.lineWidth = 3; for (let i = -10; i <= 12; i++) { g.beginPath(); g.moveTo(760 + i * 70, 700); g.lineTo(760 + i * 230, 1080); g.stroke(); }
|
|
96
|
+
[760, 830, 930, 1060].forEach(y => { g.beginPath(); g.moveTo(0, y); g.lineTo(1540, y); g.stroke(); });
|
|
97
|
+
}
|
|
98
|
+
// 窗框(红)+窗台
|
|
99
|
+
const frame = new Path2D(); frame.rect(344, 114, 472, 452); frame.rect(372, 142, 416, 396);
|
|
100
|
+
if (mode === 'color') { g.fillStyle = C.red; g.fill(frame, 'evenodd'); } else { g.fillStyle = '#fff'; g.fill(frame, 'evenodd'); g.strokeStyle = INK; g.lineWidth = 6; g.stroke(frame); }
|
|
101
|
+
windowArt(g, mode, t);
|
|
102
|
+
const mull = new Path2D(); mull.rect(570, 142, 20, 396); part(mull, C.red, { lw: 5 });
|
|
103
|
+
const sill = new Path2D(); sill.rect(322, 556, 516, 30); part(sill, C.red, { lw: 6, shade: [8, 16] });
|
|
104
|
+
// 郁金香椅(1956 Saarinen,60 年代的家具)
|
|
105
|
+
const shell = RIG.smooth([[1376, 470], [1420, 452], [1468, 480], [1484, 600], [1470, 712], [1330, 726], [1300, 700], [1420, 680], [1430, 560], [1400, 500]], true, 0.5);
|
|
106
|
+
part(shell, '#fff', { lw: 6, shade: [10, 22] });
|
|
107
|
+
const stem = new Path2D(); stem.moveTo(1380, 724); stem.lineTo(1400, 724); stem.lineTo(1412, 880); stem.lineTo(1368, 880); stem.closePath(); part(stem, '#fff', { lw: 5, shade: [6, 12] });
|
|
108
|
+
const base = new Path2D(); base.ellipse(1390, 892, 74, 16, 0, 0, TAU); part(base, '#fff', { lw: 5, shade: [5, 10] });
|
|
109
|
+
// 桌
|
|
110
|
+
const top = new Path2D(); top.rect(806, 616, 404, 30); part(top, C.wood, { lw: 6, shade: [10, 18] });
|
|
111
|
+
const apron = new Path2D(); apron.rect(826, 646, 364, 44); part(apron, C.orgD, { lw: 5, shade: [10, 22] });
|
|
112
|
+
[[842, 690], [1158, 690]].forEach(([x, y]) => { const l = new Path2D(); l.rect(x, y, 26, 214); part(l, C.orgD, { lw: 5, shade: [8, 14] }); });
|
|
113
|
+
// 茶壶(桌上,红白)
|
|
114
|
+
const pot = RIG.smooth([[868, 616], [856, 586], [872, 556], [912, 548], [948, 558], [962, 590], [952, 616]], true, 0.5);
|
|
115
|
+
const spout = U.poly([[862, 590], [828, 560], [836, 552], [870, 574]]);
|
|
116
|
+
part(spout, C.red, { lw: 4 }); part(pot, C.red, { lw: 5, shade: [8, 16] });
|
|
117
|
+
const lid = new Path2D(); lid.ellipse(910, 548, 26, 9, 0, 0, TAU); part(lid, '#fff', { lw: 4 });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ---- 角色 ----
|
|
121
|
+
function chars(g, mode, t, ch, G, K) {
|
|
122
|
+
const part = mkPart(g, mode);
|
|
123
|
+
const SH = { skirt: [16, 34], torso: [14, 30], farSleeve: [10, 22], upperArm: [10, 22], foreArm: [8, 18], hairDown: [12, 26], hairBack: [12, 24], bangs: [6, 14], body: [16, 34], head: [12, 26] };
|
|
124
|
+
const LW = { skirt: 6, torso: 6, hand: 4, farHand: 4, neck: 4, face: 4.5, cuff: 4, farCuff: 4, shoe: 5, leg: 5 };
|
|
125
|
+
const skinFill = mode === 'color' ? P.dotPattern(g, 'skin', 7, 1.5, '#ef8a8a', C.skin, 75) : null;
|
|
126
|
+
// 猫
|
|
127
|
+
RIG.drawCat(g, K, { line: INK, lw: 6, orange: C.org, white: mode === 'color' ? PAPER : '#fff', stripe: mode === 'color' ? C.orgD : INK, stripeW: 6, eye: '#8ad24a',
|
|
128
|
+
paint: (c, p, col, name, stroke) => { if (mode === 'color') { c.fillStyle = col; c.fill(p); return; } c.fillStyle = '#fff'; c.fill(p); if (SH[name]) shade(c, p, SH[name][0], SH[name][1]); if (stroke) { c.strokeStyle = INK; c.lineWidth = LW[name] || 6; c.stroke(p); } },
|
|
129
|
+
tail: (c, K) => { c.lineCap = 'round'; if (mode === 'color') { c.strokeStyle = C.org; c.lineWidth = K.tailW; c.stroke(K.tail); return; } c.strokeStyle = INK; c.lineWidth = K.tailW + 12; c.stroke(K.tail); c.strokeStyle = '#fff'; c.lineWidth = K.tailW; c.stroke(K.tail);
|
|
130
|
+
[5, 10, 15].forEach(i => { const p = K.tailPts[i], q = K.tailPts[i + 1], a = Math.atan2(q[1] - p[1], q[0] - p[0]) + Math.PI / 2; c.lineWidth = 6; c.strokeStyle = INK; c.beginPath(); c.moveTo(p[0] + Math.cos(a) * 12, p[1] + Math.sin(a) * 12); c.lineTo(p[0] - Math.cos(a) * 4, p[1] - Math.sin(a) * 4); c.stroke(); }); },
|
|
131
|
+
face: mode === 'color' ? (c) => { } : undefined });
|
|
132
|
+
// 少女:60 年代红色 A 字长裙、白色彼得潘领、黑发箍、外翻发尾
|
|
133
|
+
const fills = { skin: mode === 'color' ? skinFill : '#fff', hair: C.yel, dress: C.red, cuff: mode === 'color' ? PAPER : '#fff', shoe: INK, cupBody: '#fff', cupRim: '#7a3a16', locks: false };
|
|
134
|
+
RIG.drawGirl(g, G, { line: INK, lw: 6, ...fills, lwOf: LW, blink: ch.blink, iris: '#2a7ad8', lip: '#e3262d', featureW: 1.35,
|
|
135
|
+
paint: (c, p, col, name) => { if (mode === 'color') { c.fillStyle = col; c.fill(p); return; } c.fillStyle = '#fff'; c.fill(p); if (SH[name]) shade(c, p, SH[name][0], SH[name][1]); const w = LW[name] ?? 6; c.strokeStyle = INK; c.lineWidth = w; c.stroke(p); },
|
|
136
|
+
features: mode === 'color' ? () => { } : (c, G) => RIG.drawFeatures(c, G, { line: INK, iris: '#2a7ad8', lip: '#e3262d', featureW: 1.35, blink: ch.blink }),
|
|
137
|
+
cup: (c, G) => { const k = G.cup; c.save(); c.translate(k.x, k.y); c.rotate(k.tilt); const w = 50, h = 44;
|
|
138
|
+
const body = U.poly([[-w / 2, 0], [-w * 0.42, h], [w * 0.42, h], [w / 2, 0]]); const hd = new Path2D(); hd.arc(w * 0.58, h * 0.42, 13, -1.3, 1.4);
|
|
139
|
+
if (mode === 'color') { c.fillStyle = '#fff'; c.fill(body); c.fillStyle = C.cyan; c.fillRect(-w / 2, h * 0.35, w, 10); c.strokeStyle = '#fff'; c.lineWidth = 7; c.stroke(hd); c.fillStyle = '#7a3a16'; c.beginPath(); c.ellipse(0, 1, w / 2 - 2, 6, 0, 0, TAU); c.fill(); }
|
|
140
|
+
else { c.fillStyle = '#fff'; c.fill(body); c.strokeStyle = INK; c.lineWidth = 5; c.stroke(body); c.lineWidth = 11; c.stroke(hd); c.strokeStyle = '#fff'; c.lineWidth = 4; c.stroke(hd); c.strokeStyle = INK; c.lineWidth = 4; c.beginPath(); c.ellipse(0, 1, w / 2 - 2, 6, 0, 0, TAU); c.stroke(); }
|
|
141
|
+
c.restore(); },
|
|
142
|
+
hooks: {
|
|
143
|
+
skirt: (c) => { // 外翻发尾(在躯干后面)
|
|
144
|
+
const hp = (x, y) => [x + G.A.headC[0] - 1318, y + G.A.headC[1] - 360];
|
|
145
|
+
const flip = RIG.smooth([[1360, 430], [1404, 452], [1424, 520], [1430, 560], [1462, 556], [1474, 590], [1430, 604], [1396, 590], [1372, 520]].map(p => hp(...p)), true, 0.5);
|
|
146
|
+
if (mode === 'color') { c.fillStyle = C.yel; c.fill(flip); } else { c.fillStyle = '#fff'; c.fill(flip); shade(c, flip, 12, 26); c.strokeStyle = INK; c.lineWidth = 6; c.stroke(flip); }
|
|
147
|
+
if (mode !== 'color') { c.lineWidth = 3; c.strokeStyle = INK; [[0, 0], [14, 8]].forEach(([dx, dy]) => { c.beginPath(); c.moveTo(...hp(1376 + dx, 470 + dy)); c.quadraticCurveTo(...hp(1410 + dx, 520 + dy), ...hp(1440 + dx / 2, 584)); c.stroke(); }); }
|
|
148
|
+
},
|
|
149
|
+
torso: (c) => { // 彼得潘白领+一排纽扣
|
|
150
|
+
const n = G.A.neck; const col = new Path2D(); col.ellipse(n[0] - 10, n[1] + 6, 26, 15, -0.2, 0, TAU); col.ellipse(n[0] + 26, n[1] + 4, 24, 14, 0.3, 0, TAU);
|
|
151
|
+
if (mode === 'color') { c.fillStyle = PAPER; c.fill(col); } else { c.fillStyle = '#fff'; c.fill(col); c.strokeStyle = INK; c.lineWidth = 4.5; c.stroke(col); c.fillStyle = INK; [0, 1, 2].forEach(i => { c.beginPath(); c.arc(G.A.chest[0] + 2, G.A.chest[1] - 20 + i * 34, 4.5, 0, TAU); c.fill(); }); }
|
|
152
|
+
},
|
|
153
|
+
head: (c) => { // 黑发箍+发丝墨线
|
|
154
|
+
if (mode === 'color') return;
|
|
155
|
+
const h = G.A.headC, rot = G.tilt;
|
|
156
|
+
c.save(); c.translate(h[0], h[1]); c.rotate(rot); c.strokeStyle = INK; c.lineWidth = 11; c.lineCap = 'round'; c.beginPath(); c.ellipse(6, 4, 60, 66, -0.25, -2.35, -0.95); c.stroke(); c.restore();
|
|
157
|
+
c.lineWidth = 3; c.strokeStyle = INK; G.hairLines.forEach(l => c.stroke(l));
|
|
158
|
+
G.locks.forEach(l => { c.lineWidth = 3; c.stroke(l); });
|
|
159
|
+
},
|
|
160
|
+
} });
|
|
161
|
+
// 少女与猫的地面黑影
|
|
162
|
+
if (mode !== 'color') { g.fillStyle = INK; g.beginPath(); g.ellipse(560, 908, 150, 14, 0, 0, TAU); g.fill(); g.beginPath(); g.ellipse(1260, 932, 170, 12, 0, 0, TAU); g.fill(); }
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ---- 顶层:速度线、拟声字、旁白框、右侧特写格、格框 ----
|
|
166
|
+
function speedLines(c, G, lt, t) {
|
|
167
|
+
const v = Math.abs(Math.sin(Math.min(1, lt / 0.9) * Math.PI)) * (lt < 0.95 ? 1 : 0); // 端杯速度(正比)
|
|
168
|
+
if (v < 0.08) return;
|
|
169
|
+
const st = Math.floor(t * 12), r = U.rng(500 + st);
|
|
170
|
+
const k = G.cup, dir = [-0.16, 1]; // 杯往上走 → 速度线拖在下面
|
|
171
|
+
c.save(); c.strokeStyle = INK; c.lineCap = 'round';
|
|
172
|
+
for (let i = 0; i < 9; i++) { const ox = k.x - 40 + i * 12 + (r() - 0.5) * 6, oy = k.y + 50 + r() * 26, L = (70 + r() * 110) * v;
|
|
173
|
+
c.lineWidth = 2.5 + r() * 2.5; c.beginPath(); c.moveTo(ox, oy); c.lineTo(ox + dir[0] * L, oy + dir[1] * L); c.stroke(); }
|
|
174
|
+
// 手肘后方两道动作弧
|
|
175
|
+
const E = G.A.elbow; c.lineWidth = 5; [0, 1].forEach(j => { c.beginPath(); c.arc(E[0] + 6, E[1] + 10, 46 + j * 18, 0.1, 1.1); c.stroke(); });
|
|
176
|
+
c.restore();
|
|
177
|
+
}
|
|
178
|
+
function sfx(c, lt, t) {
|
|
179
|
+
const q = clamp((lt - 0.52) / 0.16); if (q <= 0) return;
|
|
180
|
+
const s = U.ease.outBack(q), st = Math.floor(t * 12), jx = (U.hash(st, 3) - 0.5) * 6, jy = (U.hash(st, 9) - 0.5) * 6;
|
|
181
|
+
c.save(); c.translate(990 + jx, 318 + jy); c.rotate(-0.14); c.scale(s * 0.82, s * 0.82);
|
|
182
|
+
// 爆炸放射底
|
|
183
|
+
c.fillStyle = C.yel; c.strokeStyle = INK; c.lineWidth = 7; const burst = []; for (let i = 0; i < 26; i++) { const a = i / 26 * TAU, r = (i % 2 ? 108 : 178) * (0.9 + U.hash(i, 77) * 0.2); burst.push([Math.cos(a) * r * 1.5, Math.sin(a) * r * 0.75]); }
|
|
184
|
+
const bp = U.poly(burst); c.fill(bp); c.stroke(bp);
|
|
185
|
+
c.font = '132px "Bangers-400"'; c.textAlign = 'center'; c.textBaseline = 'middle'; c.letterSpacing = '4px';
|
|
186
|
+
for (let d = 14; d > 0; d -= 2) { c.fillStyle = INK; c.fillText('SLURRP!', d * 0.8, d); } // Kirby 的立体积木字:黑色挤出
|
|
187
|
+
c.lineWidth = 10; c.strokeStyle = INK; c.strokeText('SLURRP!', 0, 0); c.fillStyle = C.red; c.fillText('SLURRP!', 0, 0);
|
|
188
|
+
c.fillStyle = C.yel; c.save(); c.beginPath(); c.rect(-400, -70, 800, 46); c.clip(); c.fillText('SLURRP!', 0, 0); c.restore();
|
|
189
|
+
c.restore();
|
|
190
|
+
}
|
|
191
|
+
function caption(c) {
|
|
192
|
+
c.save(); c.translate(5, 5); c.fillStyle = INK; c.fillRect(40, 38, 420, 62); c.restore();
|
|
193
|
+
c.fillStyle = C.yel; c.fillRect(40, 38, 420, 62); c.strokeStyle = INK; c.lineWidth = 5; c.strokeRect(40, 38, 420, 62);
|
|
194
|
+
c.font = '38px "Bangers-400"'; c.fillStyle = INK; c.textBaseline = 'middle'; c.letterSpacing = '2px'; c.fillText('MEANWHILE, AT TEA TIME...', 56, 70); c.letterSpacing = '0px';
|
|
195
|
+
}
|
|
196
|
+
function catPanel(c, lt, t, ch) {
|
|
197
|
+
const x0 = 1556, y0 = 236, x1 = 1892, y1 = 1050, cx = 1724, cy = 560;
|
|
198
|
+
c.save(); c.beginPath(); c.rect(x0, y0, x1 - x0, y1 - y0); c.clip();
|
|
199
|
+
c.fillStyle = C.mag; c.fillRect(x0, y0, x1 - x0, y1 - y0);
|
|
200
|
+
c.fillStyle = P.dotPattern(c, 'pnl', 10, 3.6, C.yel, null, 0); c.fillRect(x0, y0, x1 - x0, y1 - y0);
|
|
201
|
+
// 聚焦线:12fps 换种子
|
|
202
|
+
const r = U.rng(900 + Math.floor(t * 12)); c.fillStyle = INK;
|
|
203
|
+
for (let i = 0; i < 44; i++) { const a = i / 44 * TAU + r() * 0.08, w = 0.012 + r() * 0.02, r0 = 230 + r() * 60; c.beginPath(); c.moveTo(cx + Math.cos(a - w) * r0, cy + Math.sin(a - w) * r0); c.lineTo(cx + Math.cos(a) * 900, cy + Math.sin(a) * 900); c.lineTo(cx + Math.cos(a + w) * r0, cy + Math.sin(a + w) * r0); c.fill(); }
|
|
204
|
+
// krackle 在头周围
|
|
205
|
+
for (let k = 0; k < 6; k++) { const a = k / 6 * TAU + t * 0.8; krackle(c, cx + Math.cos(a) * 200, cy + Math.sin(a) * 250, 30, 40 + k, t, 12); }
|
|
206
|
+
// 特写猫头
|
|
207
|
+
const head = new Path2D(); head.ellipse(cx, cy, 150, 132, 0, 0, TAU);
|
|
208
|
+
const ears = U.poly([[cx - 140, cy - 40], [cx - 120, cy - 200], [cx - 40, cy - 120], [cx + 40, cy - 120], [cx + 120, cy - 200], [cx + 140, cy - 40]]);
|
|
209
|
+
c.lineJoin = 'round'; c.fillStyle = C.org; c.strokeStyle = INK; c.lineWidth = 8; c.fill(ears); c.stroke(ears); c.fill(head); c.stroke(head);
|
|
210
|
+
c.save(); c.clip(head); c.fillStyle = PAPER; c.beginPath(); c.ellipse(cx, cy + 80, 96, 72, 0, 0, TAU); c.fill();
|
|
211
|
+
c.fillStyle = INK; c.beginPath(); c.ellipse(cx + 168, cy + 30, 48, 150, 0, 0, TAU); c.fill(); // 右侧黑块月牙
|
|
212
|
+
c.strokeStyle = C.orgD; c.lineWidth = 12; [[-40, -128, -30, -80], [0, -132, 0, -84], [40, -128, 30, -80]].forEach(([a, b, d, e]) => { c.beginPath(); c.moveTo(cx + a, cy + b); c.lineTo(cx + d, cy + e); c.stroke(); });
|
|
213
|
+
c.restore(); c.lineWidth = 8; c.strokeStyle = INK; c.stroke(head);
|
|
214
|
+
// 眼:瞳孔追着杯子(杯往上→瞳孔往上,往左看)
|
|
215
|
+
const look = ch.cup;
|
|
216
|
+
[-62, 62].forEach(dx => { const ex = cx + dx, ey = cy - 18; c.fillStyle = '#b6e04a'; c.beginPath(); c.ellipse(ex, ey, 42, 48, 0, 0, TAU); c.fill(); c.lineWidth = 6; c.stroke();
|
|
217
|
+
const px = ex - 14, py = ey + lerp(14, -18, look); c.fillStyle = INK; c.beginPath(); c.ellipse(px, py, 10 + look * 6, 30, 0, 0, TAU); c.fill();
|
|
218
|
+
c.fillStyle = '#fff'; c.beginPath(); c.arc(ex - 22, ey - 22, 8, 0, TAU); c.fill(); });
|
|
219
|
+
c.fillStyle = '#f07aa0'; c.beginPath(); c.moveTo(cx - 16, cy + 46); c.lineTo(cx + 16, cy + 46); c.lineTo(cx, cy + 64); c.closePath(); c.fill(); c.lineWidth = 4; c.stroke();
|
|
220
|
+
c.lineWidth = 5; c.beginPath(); c.moveTo(cx - 36, cy + 84); c.quadraticCurveTo(cx - 14, cy + 102, cx, cy + 72); c.quadraticCurveTo(cx + 14, cy + 102, cx + 36, cy + 84); c.stroke();
|
|
221
|
+
c.lineWidth = 3.5; [[-1, 60], [-1, 76], [1, 60], [1, 76]].forEach(([s, y]) => { c.beginPath(); c.moveTo(cx + s * 70, cy + y); c.lineTo(cx + s * 170, cy + y - 14 + (y - 60)); c.stroke(); });
|
|
222
|
+
// 旁白
|
|
223
|
+
c.fillStyle = '#fff'; c.fillRect(x0 + 14, y1 - 132, 308, 100); c.strokeStyle = INK; c.lineWidth = 5; c.strokeRect(x0 + 14, y1 - 132, 308, 100);
|
|
224
|
+
c.font = '34px "Bangers-400"'; c.fillStyle = INK; c.textAlign = 'center'; c.letterSpacing = '1px'; c.fillText('BUT THE CAT', x0 + 168, y1 - 92); c.fillText('IS WATCHING!', x0 + 168, y1 - 52); c.letterSpacing = '0px'; c.textAlign = 'left';
|
|
225
|
+
c.restore();
|
|
226
|
+
}
|
|
227
|
+
function gutters(c) {
|
|
228
|
+
c.fillStyle = PAPER; c.fillRect(0, 0, W, 24); c.fillRect(0, H - 24, W, 24); c.fillRect(0, 0, 24, H); c.fillRect(W - 24, 0, 24, H);
|
|
229
|
+
c.fillRect(1534, 0, 22, H); c.fillRect(1556, 214, W - 1556, 22);
|
|
230
|
+
c.strokeStyle = INK; c.lineWidth = 7; c.strokeRect(24, 24, 1510, H - 48); c.strokeRect(1556, 236, 340, H - 260);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
draw(c, lt, t) {
|
|
235
|
+
const ch = P.choreo(lt, t);
|
|
236
|
+
const G = RIG.girl({ cup: ch.cup, sip: ch.sip, hair: 'long', breathe: ch.breathe });
|
|
237
|
+
const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe, look: ch.cup });
|
|
238
|
+
const cc = P.scratch('kbColor'), cg = cc.getContext('2d'), ic = P.scratch('kbInk'), ig = ic.getContext('2d');
|
|
239
|
+
cg.reset(); ig.reset();
|
|
240
|
+
room(cg, 'color', t); chars(cg, 'color', t, ch, G, K);
|
|
241
|
+
room(ig, 'ink', t); chars(ig, 'ink', t, ch, G, K);
|
|
242
|
+
c.fillStyle = PAPER; c.fillRect(0, 0, W, H);
|
|
243
|
+
c.drawImage(cc, 4, 3); // 套色不准:色版相对墨版偏 (4,3)
|
|
244
|
+
c.save(); c.globalCompositeOperation = 'multiply'; c.drawImage(ic, 0, 0); c.restore();
|
|
245
|
+
speedLines(c, G, lt, t);
|
|
246
|
+
// 热气 = krackle 小点上升
|
|
247
|
+
for (let k = 0; k < 4; k++) { const q = ((t * 1.6 + k / 4) % 1); krackle(c, G.cup.x + Math.sin(q * 6 + k) * 14, G.cup.y - 24 - q * 120, 20 * (1 - q * 0.5), 60 + k, t, 9); }
|
|
248
|
+
sfx(c, lt, t);
|
|
249
|
+
caption(c);
|
|
250
|
+
catPanel(c, lt, t, ch);
|
|
251
|
+
gutters(c);
|
|
252
|
+
c.save(); c.globalCompositeOperation = 'multiply'; c.drawImage(P.grain('kb', 0.05, [120, 100, 60], 0.25), 0, 0); c.restore();
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
})();
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// 1899 莫奈《睡莲》+《睡莲池上的日本桥》——纯代码。
|
|
2
|
+
// 管线(照 09_postimp 标杆):①平涂底稿(墙=橘园式睡莲壁画、窗=绿色拱桥、地板、桌布)
|
|
3
|
+
// → ②流场笔触分遍重画(水面横笔 / 窗景细笔 / 地板 / 道具),区域色板
|
|
4
|
+
// → ③角色平涂+source-atop 细笔触,只补五官(印象派不勾轮廓)
|
|
5
|
+
// → ④会动的高光:水面光斑颤动(10fps 闪)、睡莲漂浮、柳影横向波动、涟漪扩散、紫藤摆动
|
|
6
|
+
SCENES['28_monet'] = (() => {
|
|
7
|
+
const W = 1920, H = 1080, TAU = Math.PI * 2, { clamp, lerp } = U, P = PAINT;
|
|
8
|
+
const WIN = [372, 142, 416, 396]; // 窗内
|
|
9
|
+
const inWin = (x, y) => x > 360 && x < 800 && y > 130 && y < 550;
|
|
10
|
+
const inFrame = (x, y) => x > 300 && x < 862 && y > 100 && y < 598;
|
|
11
|
+
const isFloor = (x, y) => y > 704;
|
|
12
|
+
|
|
13
|
+
// 壁画里的睡莲叶丛(固定布局,随时间漂浮)
|
|
14
|
+
const r0 = U.rng(4242), PADS = [];
|
|
15
|
+
[[150, 210, 7], [260, 520, 9], [930, 170, 6], [1030, 430, 8], [1640, 330, 7], [1760, 600, 8], [120, 400, 5], [1450, 120, 5], [1250, 600, 6], [700, 640, 7]].forEach(([cx, cy, n], k) => {
|
|
16
|
+
for (let i = 0; i < n + 3; i++) PADS.push({ x: cx + (r0() - 0.5) * 300, y: cy + (r0() - 0.5) * 90, rx: 38 + r0() * 40, ry: 12 + r0() * 9, notch: r0() * TAU, flower: r0() < 0.4, ph: r0() * TAU, k });
|
|
17
|
+
});
|
|
18
|
+
const pad = (p, t) => [p.x + Math.sin(t * 1.3 + p.ph) * 7 + t * 6, p.y + Math.sin(t * 1.9 + p.ph * 1.3) * 2.5];
|
|
19
|
+
|
|
20
|
+
function base(g, t) {
|
|
21
|
+
// ---- 壁画:水面(上浅下深,紫蓝绿) ----
|
|
22
|
+
const wg = g.createLinearGradient(0, 0, 0, 700); wg.addColorStop(0, '#b9c7e6'); wg.addColorStop(0.35, '#8fa6d6'); wg.addColorStop(0.7, '#6c84bf'); wg.addColorStop(1, '#5a72a8');
|
|
23
|
+
g.fillStyle = wg; g.fillRect(0, 0, W, 704);
|
|
24
|
+
// 天光倒影(浅粉黄云)
|
|
25
|
+
[[600, 120, 260, 50], [1300, 260, 300, 60], [200, 600, 200, 40], [1500, 520, 220, 40]].forEach(([x, y, rx, ry], k) => { g.fillStyle = k % 2 ? '#e8d6e8' : '#f2e2c4'; g.beginPath(); g.ellipse(x + Math.sin(t * 0.8 + k) * 10, y, rx, ry, 0, 0, TAU); g.fill(); });
|
|
26
|
+
// 柳影(竖向暗绿紫,横向随时间波动)
|
|
27
|
+
[[70, 0.9], [150, 0.7], [1560, 0.8], [1660, 1], [1840, 0.7]].forEach(([x0, a], k) => {
|
|
28
|
+
g.fillStyle = k % 2 ? '#3f6a58' : '#4f5f8f';
|
|
29
|
+
g.beginPath(); for (let y = 0; y <= 700; y += 20) g.lineTo(x0 + Math.sin(y * 0.03 - t * 3.2 + k) * 14, y);
|
|
30
|
+
for (let y = 700; y >= 0; y -= 20) g.lineTo(x0 + 46 * a + Math.sin(y * 0.03 - t * 3.2 + k + 0.6) * 14, y); g.closePath(); g.fill(); });
|
|
31
|
+
// 睡莲叶 + 花
|
|
32
|
+
PADS.forEach(p => { const [x, y] = pad(p, t); g.fillStyle = p.k % 2 ? '#5f8f4a' : '#7aa85a'; g.beginPath(); g.ellipse(x, y, p.rx, p.ry, 0, p.notch + 0.35, p.notch + TAU - 0.35); g.lineTo(x, y); g.closePath(); g.fill();
|
|
33
|
+
if (p.flower) { g.fillStyle = p.k % 3 ? '#f2a6bf' : '#fbf4f0'; g.beginPath(); g.ellipse(x + 6, y - 7, 17, 10, 0, 0, TAU); g.fill(); g.fillStyle = p.k % 3 ? '#fff2f2' : '#f6d86a'; g.beginPath(); g.ellipse(x + 6, y - 11, 8, 6, 0, 0, TAU); g.fill(); } });
|
|
34
|
+
// ---- 地板:暖木 ----
|
|
35
|
+
const fg = g.createLinearGradient(0, 704, 0, H); fg.addColorStop(0, '#b98a6a'); fg.addColorStop(1, '#d6a874'); g.fillStyle = fg; g.fillRect(0, 700, W, H - 700);
|
|
36
|
+
g.fillStyle = '#7a6a9a'; g.fillRect(0, 696, W, 10);
|
|
37
|
+
// ---- 窗:日本桥 ----
|
|
38
|
+
g.save(); g.beginPath(); g.rect(...WIN); g.clip();
|
|
39
|
+
g.fillStyle = '#3f6a3a'; g.fillRect(...WIN);
|
|
40
|
+
// 上半:浓密树叶+紫藤
|
|
41
|
+
for (let i = 0; i < 60; i++) { const r = U.rng(i * 17 + 3), x = 372 + r() * 416, y = 142 + r() * 150; g.fillStyle = ['#4f8a3a', '#6fa040', '#2f5a30', '#9cbf58', '#3a6a5a'][i % 5]; g.beginPath(); g.ellipse(x, y, 30 + r() * 30, 20 + r() * 20, 0, 0, TAU); g.fill(); }
|
|
42
|
+
// 下半:池水+睡莲(透视:越远越小)
|
|
43
|
+
const pg = g.createLinearGradient(0, 330, 0, 538); pg.addColorStop(0, '#6f8f9a'); pg.addColorStop(1, '#4f6f8a'); g.fillStyle = pg; g.fillRect(372, 330, 416, 210);
|
|
44
|
+
for (let row = 0; row < 7; row++) { const y = 352 + row * row * 4.2 + row * 6, s = 0.4 + row * 0.16; for (let i = 0; i < 6; i++) { const x = 380 + ((i * 83 + row * 47) % 420) + Math.sin(t * 1.4 + i + row) * 5;
|
|
45
|
+
g.fillStyle = (i + row) % 2 ? '#7aa85a' : '#9cc070'; g.beginPath(); g.ellipse(x, y, 28 * s, 7 * s, 0, 0, TAU); g.fill(); if ((i * 3 + row) % 4 === 0) { g.fillStyle = '#f4b0c4'; g.beginPath(); g.arc(x, y - 3 * s, 6 * s, 0, TAU); g.fill(); } } }
|
|
46
|
+
// 桥的倒影(绿色弧,波动)
|
|
47
|
+
g.strokeStyle = '#4f8a6a'; g.lineWidth = 9; g.beginPath(); for (let x = 372; x <= 788; x += 8) { const q = (x - 580) / 208, y = 352 + 46 * (1 - q * q) + Math.sin(x * 0.08 + t * 6) * 3; x === 372 ? g.moveTo(x, y) : g.lineTo(x, y); } g.stroke();
|
|
48
|
+
// 桥:绿色拱+栏杆+竖栏
|
|
49
|
+
g.fillStyle = '#24452a'; g.beginPath(); for (let x = 350; x <= 810; x += 6) g.lineTo(x, arc(x, 62) - 60); g.lineTo(810, 360); g.lineTo(350, 360); g.closePath(); g.fill(); // 桥后的深色树荫衬底
|
|
50
|
+
g.restore();
|
|
51
|
+
}
|
|
52
|
+
const arc = (x, lift) => { const q = (x - 580) / 230; return 330 - lift * (1 - q * q); };
|
|
53
|
+
function bridge(g, t) {
|
|
54
|
+
g.save(); g.beginPath(); g.rect(...WIN); g.clip();
|
|
55
|
+
g.strokeStyle = '#7fcf9a'; g.lineCap = 'round';
|
|
56
|
+
g.strokeStyle = '#3f8a62'; g.lineWidth = 20; g.beginPath(); for (let x = 350; x <= 810; x += 6) { const y = arc(x, 62); x === 350 ? g.moveTo(x, y) : g.lineTo(x, y); } g.stroke(); g.strokeStyle = '#8fdcae'; // 桥面
|
|
57
|
+
g.lineWidth = 8; g.beginPath(); for (let x = 350; x <= 810; x += 6) { const y = arc(x, 62) - 46; x === 350 ? g.moveTo(x, y) : g.lineTo(x, y); } g.stroke(); // 扶手
|
|
58
|
+
g.lineWidth = 6; g.beginPath(); for (let x = 350; x <= 810; x += 6) { const y = arc(x, 62) - 24; x === 350 ? g.moveTo(x, y) : g.lineTo(x, y); } g.stroke();
|
|
59
|
+
g.lineWidth = 5; for (let x = 360; x <= 800; x += 30) { g.beginPath(); g.moveTo(x, arc(x, 62)); g.lineTo(x, arc(x, 62) - 46); g.stroke(); }
|
|
60
|
+
// 紫藤垂花(摆动)
|
|
61
|
+
for (let i = 0; i < 12; i++) { const x = 386 + i * 36, sw = Math.sin(t * 2.4 + i * 0.9) * 8, L = 40 + (i * 37 % 50); for (let j = 0; j < 9; j++) { const q = j / 8, px = x + sw * q * q + Math.sin(j * 2.1 + i) * 4, py = 150 + L * q * 1.2; g.fillStyle = ['#b8a0dc', '#d6c0ee', '#9a86c8', '#e8dcf6'][(i + j) % 4]; g.beginPath(); g.ellipse(px, py, 8 * (1 - q * 0.6), 6 * (1 - q * 0.5), 0.3, 0, TAU); g.fill(); } }
|
|
62
|
+
g.restore();
|
|
63
|
+
}
|
|
64
|
+
function frameArt(g) {
|
|
65
|
+
// 窗框(奶白)+两侧绿色百叶(吉维尼的房子)
|
|
66
|
+
g.fillStyle = '#4f9a6a'; g.fillRect(268, 110, 70, 470); g.fillRect(822, 110, 70, 470);
|
|
67
|
+
g.lineWidth = 22; g.strokeStyle = '#f3ecdc'; g.strokeRect(357, 127, 446, 426);
|
|
68
|
+
g.fillStyle = '#f3ecdc'; g.fillRect(570, 142, 18, 396); g.fillRect(330, 548, 500, 28);
|
|
69
|
+
}
|
|
70
|
+
function props(g, t) {
|
|
71
|
+
// 圆桌+白桌布(下垂褶)
|
|
72
|
+
g.fillStyle = '#f2ecf2'; g.beginPath(); g.moveTo(800, 618); g.lineTo(1214, 618); g.lineTo(1226, 780); for (let x = 1226; x >= 788; x -= 24) g.lineTo(x, 780 + Math.sin(x * 0.12) * 8); g.closePath(); g.fill();
|
|
73
|
+
g.fillStyle = '#d0c8e6'; for (let x = 830; x < 1200; x += 48) { g.beginPath(); g.moveTo(x, 640); g.lineTo(x + 10, 640); g.lineTo(x + 18, 780); g.lineTo(x + 2, 780); g.closePath(); g.fill(); }
|
|
74
|
+
g.fillStyle = '#8a6a4a'; g.fillRect(860, 780, 20, 124); g.fillRect(1140, 780, 20, 124);
|
|
75
|
+
// 蓝白瓷壶+一瓶鸢尾
|
|
76
|
+
g.fillStyle = '#e8eef8'; g.beginPath(); g.ellipse(900, 596, 40, 30, 0, 0, TAU); g.fill(); g.fillStyle = '#3f6ab8'; g.fillRect(864, 590, 72, 8); g.beginPath(); g.ellipse(900, 566, 18, 6, 0, 0, TAU); g.fill();
|
|
77
|
+
g.strokeStyle = '#e8eef8'; g.lineWidth = 8; g.beginPath(); g.moveTo(862, 596); g.lineTo(838, 574); g.stroke();
|
|
78
|
+
g.fillStyle = '#c8d8e0'; g.beginPath(); g.moveTo(1030, 618); g.quadraticCurveTo(1016, 586, 1036, 560); g.lineTo(1060, 560); g.quadraticCurveTo(1080, 586, 1066, 618); g.closePath(); g.fill();
|
|
79
|
+
g.strokeStyle = '#5f8f4a'; g.lineWidth = 5; [[1040, 560, 1010, 480], [1048, 560, 1052, 456], [1056, 560, 1092, 490]].forEach(([a, b, c2, d], k) => { const sw = Math.sin(t * 2 + k) * 4; g.beginPath(); g.moveTo(a, b); g.lineTo(c2 + sw, d); g.stroke(); g.fillStyle = '#6a5ac8'; g.beginPath(); g.ellipse(c2 + sw, d - 8, 12, 16, 0, 0, TAU); g.fill(); });
|
|
80
|
+
// 藤椅(椅背在少女身后)
|
|
81
|
+
g.strokeStyle = '#c9a46a'; g.lineWidth = 12; g.beginPath(); g.moveTo(1440, 900); g.lineTo(1446, 500); g.quadraticCurveTo(1470, 460, 1494, 500); g.lineTo(1488, 900); g.stroke();
|
|
82
|
+
g.lineWidth = 6; for (let y = 530; y < 720; y += 36) { g.beginPath(); g.moveTo(1446, y); g.lineTo(1490, y); g.stroke(); }
|
|
83
|
+
}
|
|
84
|
+
const angle = (x, y, t) => {
|
|
85
|
+
if (inWin(x, y)) return y < 300 ? -1.2 + 0.8 * P.noise(x * 0.02, y * 0.02) : 0.04 * Math.sin(x * 0.03 + t);
|
|
86
|
+
if (isFloor(x, y)) return 0.08 + 0.12 * P.noise(x * 0.01, y * 0.01);
|
|
87
|
+
return 0.06 * Math.sin(y * 0.05 + x * 0.004) + 0.05 * P.noise(x * 0.01, y * 0.01); // 水面:横笔
|
|
88
|
+
};
|
|
89
|
+
const SW = {
|
|
90
|
+
water: P.swatch(['#5b6fb5', '#7d8fd0', '#9db5e0', '#6aa0a8', '#b7a6d8', '#d9c8e8', '#8fb8a8', '#a6c4e8', '#c8b0d8'], 0.45),
|
|
91
|
+
green: P.swatch(['#5f8f4a', '#7fae5a', '#3f6f4a', '#9cc070', '#6f9a7a', '#b4cc78'], 0.4),
|
|
92
|
+
pink: P.swatch(['#f0a0b8', '#f7d0dc', '#fff2f4', '#e88aa0'], 0.3),
|
|
93
|
+
willow: P.swatch(['#3f6a58', '#557a4a', '#2e5048', '#6a5f9a', '#4a6aa0'], 0.4),
|
|
94
|
+
floor: P.swatch(['#c69a6a', '#b07a5a', '#d8b08a', '#9a6a7a', '#8a7aa8', '#e0bc90'], 0.35),
|
|
95
|
+
leaf: P.swatch(['#4f7f3a', '#6f9a40', '#2f5a30', '#9cbf58', '#c0d070', '#3a6a5a', '#b8a0dc'], 0.35),
|
|
96
|
+
};
|
|
97
|
+
const palette = (x, y, col, r) => {
|
|
98
|
+
const [R, G, B] = col;
|
|
99
|
+
if (inWin(x, y)) return (R > 150 && G < 200) || (B > R + 30 && R > 150) ? null : SW.leaf(col, r);
|
|
100
|
+
if (inFrame(x, y)) return null;
|
|
101
|
+
if (isFloor(x, y)) return SW.floor(col, r);
|
|
102
|
+
if (R > 200 && G < 190) return SW.pink(col, r);
|
|
103
|
+
if (G > R + 15 && G > B - 10) return SW.green(col, r);
|
|
104
|
+
if (G > R && B < 140) return SW.willow(col, r);
|
|
105
|
+
return SW.water(col, r);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const girlFill = { skin: '#f6d8c4', hair: '#f0cc6a', dress: '#f6f2ec', cuff: '#f6f2ec', shoe: '#d8cce4', cupBody: '#eef2fa', cupRim: '#a8703a', locks: false };
|
|
109
|
+
return {
|
|
110
|
+
draw(c, lt, t) {
|
|
111
|
+
const ch = P.choreo(lt, t);
|
|
112
|
+
const b = P.scratch('moBase'), bg = b.getContext('2d'); bg.reset(); base(bg, t); frameArt(bg);
|
|
113
|
+
c.fillStyle = '#e8e0ec'; c.fillRect(0, 0, W, H);
|
|
114
|
+
c.drawImage(b, 0, 0);
|
|
115
|
+
// 笔触三遍:水面壁画 / 窗景细笔 / 地板
|
|
116
|
+
P.strokes(c, b, { cell: 9, len: 34, width: 6.5, angle, seed: 21, t, boil: 6, outline: 0, jitterCol: 14, palette, mask: (x, y) => !isFloor(x, y) && !inFrame(x, y) });
|
|
117
|
+
P.strokes(c, b, { cell: 6, len: 13, width: 4.5, angle, seed: 22, t, boil: 6, outline: 0, jitterCol: 16, palette, mask: (x, y) => inWin(x, y) });
|
|
118
|
+
P.strokes(c, b, { cell: 12, len: 34, width: 8, angle, seed: 23, t, boil: 0, outline: 0, jitterCol: 12, palette, mask: isFloor });
|
|
119
|
+
// 窗框和百叶:细一点的竖/横笔(单独一遍,免得被大笔打碎)
|
|
120
|
+
P.strokes(c, b, { cell: 6, len: 12, width: 4, seed: 24, t, boil: 0, outline: 0, jitterCol: 18, mask: (x, y) => inFrame(x, y) && !inWin(x, y), angle: (x, y) => (x < 350 || x > 810 || (x > 566 && x < 592)) ? -Math.PI / 2 : 0 });
|
|
121
|
+
// 桥:单独一层,浅蓝绿细笔(不让树叶色板吞掉)
|
|
122
|
+
const Bp = P.scratch('moBridge'), bpg = Bp.getContext('2d'); bpg.clearRect(0, 0, W, H); bridge(bpg, t);
|
|
123
|
+
bpg.save(); bpg.globalCompositeOperation = 'source-atop';
|
|
124
|
+
P.strokes(bpg, Bp, { cell: 5, len: 10, width: 4, seed: 27, t, boil: 6, outline: 0, alphaMask: true, angle: () => 0.1,
|
|
125
|
+
palette: (x, y, col, r) => col[1] > 150 && col[0] < 180 ? P.mix(P.hex(['#8fdcae', '#a8e8c0', '#6fc49a', '#c0f0d0', '#7fb8d8'][(r() * 5) | 0]), col, 0.3) : null });
|
|
126
|
+
bpg.restore(); c.drawImage(Bp, 0, 0);
|
|
127
|
+
// 水面光斑颤动:横向亮短笔,亮度随时间不规则闪(10fps 换位)
|
|
128
|
+
const st = Math.floor(t * 10); c.save(); c.lineCap = 'round';
|
|
129
|
+
for (let i = 0; i < 160; i++) { const r = U.rng(i * 31 + 7), x = r() * W, y = 20 + r() * 670; if (inFrame(x, y)) continue;
|
|
130
|
+
const a = clamp(Math.sin(t * (5 + r() * 6) + r() * TAU) * Math.sin(t * 2.3 + i), 0, 1); if (a < 0.15) continue;
|
|
131
|
+
const jx = (U.hash(i, st) - 0.5) * 10; c.strokeStyle = ['#fffbe8', '#fdf0d0', '#f8e0ec', '#e8f4ff'][i % 4]; c.globalAlpha = 0.85 * a; c.lineWidth = 4 + r() * 3;
|
|
132
|
+
c.beginPath(); c.moveTo(x + jx, y); c.lineTo(x + jx + 14 + r() * 26, y + (r() - 0.5) * 3); c.stroke(); }
|
|
133
|
+
// 涟漪:两处同心椭圆外扩
|
|
134
|
+
[[300, 640], [1250, 160], [560, 470]].forEach(([x, y], k) => { for (let j = 0; j < 3; j++) { const q = (t * 0.9 + j / 3 + k * 0.3) % 1; c.globalAlpha = 0.6 * (1 - q); c.strokeStyle = '#eef4ff'; c.lineWidth = 3; c.setLineDash([12, 10]); c.beginPath(); c.ellipse(x, y, 20 + q * 90, (20 + q * 90) * 0.22, 0, 0, TAU); c.stroke(); } });
|
|
135
|
+
c.setLineDash([]); c.globalAlpha = 1; c.restore();
|
|
136
|
+
// 道具层:桌布、瓷壶、鸢尾、藤椅
|
|
137
|
+
const Pr = P.scratch('moProps'), pg = Pr.getContext('2d'); pg.clearRect(0, 0, W, H); props(pg, t);
|
|
138
|
+
pg.save(); pg.globalCompositeOperation = 'source-atop';
|
|
139
|
+
P.strokes(pg, Pr, { cell: 6, len: 13, width: 4.5, seed: 25, t, boil: 0, outline: 0, jitterCol: 22, alphaMask: true, angle: (x, y) => (y > 640 && x > 790 && x < 1230) ? -Math.PI / 2 + 0.1 : 0.1,
|
|
140
|
+
palette: (x, y, col, r) => (col[0] > 225 && col[2] > 225) ? P.mix(P.hex(['#fbf8f4', '#e6e0f4', '#d8d4f0', '#f6ecd8'][(r() * 4) | 0]), col, 0.3) : null });
|
|
141
|
+
pg.restore(); c.drawImage(Pr, 0, 0);
|
|
142
|
+
// 角色:平涂 → 笔触 → 五官
|
|
143
|
+
const L = P.scratch('moChars'), lg = L.getContext('2d'); lg.clearRect(0, 0, W, H);
|
|
144
|
+
const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe });
|
|
145
|
+
const G = RIG.girl({ cup: ch.cup, sip: ch.sip, hat: 'straw', hair: 'bun', breathe: ch.breathe });
|
|
146
|
+
RIG.drawCat(lg, K, { lw: 0, orange: '#ec9a48', white: '#faf2e6', stripe: '#c8702a', face: () => { } });
|
|
147
|
+
RIG.drawGirl(lg, G, { lw: 0, line: '#111', ...girlFill, features: () => { },
|
|
148
|
+
hooks: { torso: (c) => { // 1899 白色夏裙:高领+泡泡袖+浅蓝腰带
|
|
149
|
+
const n = G.A.neck; c.fillStyle = '#f6f2ec'; c.fillRect(n[0] - 16, n[1] - 30, 30, 34);
|
|
150
|
+
c.strokeStyle = '#8fa8d8'; c.lineWidth = 16; c.beginPath(); c.moveTo(G.A.waist[0] - 60, G.A.waist[1] - 6); c.lineTo(G.A.waist[0] + 70, G.A.waist[1] - 2); c.stroke(); },
|
|
151
|
+
arm: (c) => { const S = G.A.shoulder; c.fillStyle = '#f6f2ec'; c.beginPath(); c.ellipse(S[0] - 6, S[1] + 22, 42, 52, 0.4, 0, TAU); c.fill(); },
|
|
152
|
+
head: (c) => { // 草帽(RIG 的帽檐)+蓝丝带飘动
|
|
153
|
+
c.fillStyle = '#ecd28a'; c.fill(G.brim); c.fill(G.crown); c.fillStyle = '#5f86c8'; c.fill(G.band);
|
|
154
|
+
const b0 = G.bow, sw = Math.sin(t * 4.5) * 10; c.strokeStyle = '#5f86c8'; c.lineWidth = 10; c.lineCap = 'round'; c.beginPath(); c.moveTo(b0[0], b0[1]); c.quadraticCurveTo(b0[0] + 30, b0[1] + 30 + sw, b0[0] + 46 + sw, b0[1] + 74); c.stroke(); } } });
|
|
155
|
+
lg.save(); lg.globalCompositeOperation = 'source-atop';
|
|
156
|
+
P.strokes(lg, L, { cell: 6, len: 12, width: 4.5, seed: 26, t, boil: 0, outline: 0, jitterCol: 18, alphaMask: true,
|
|
157
|
+
angle: (x, y) => x < 760 ? Math.atan2(y - 780, x - 540) + Math.PI / 2 : -Math.PI / 2 + 0.35 * Math.sin(y * 0.03),
|
|
158
|
+
palette: (x, y, col, r) => { const L_ = col[0] + col[1] + col[2]; if (x > 900 && L_ > 690) return P.mix(P.hex(['#fbf8f2', '#e4e0f6', '#cfd2ee', '#f8ecd0', '#d8e8f2'][(r() * 5) | 0]), col, 0.25);
|
|
159
|
+
if (x > 900 && col[0] > 220 && col[1] > 180 && col[2] < 150) return P.mix(P.hex(['#f2d27a', '#e8c060', '#f8e4a0', '#d8a850'][(r() * 4) | 0]), col, 0.3); return null; } });
|
|
160
|
+
// 受光侧暖、背光侧冷:一层淡紫阴影+暖黄高光(印象派的色彩阴影)
|
|
161
|
+
lg.globalCompositeOperation = 'source-atop';
|
|
162
|
+
const sg = lg.createLinearGradient(1150, 0, 1480, 0); sg.addColorStop(0, 'rgba(255,236,170,0.22)'); sg.addColorStop(1, 'rgba(110,100,190,0.32)'); lg.fillStyle = sg; lg.fillRect(1080, 200, 420, 760);
|
|
163
|
+
const cg = lg.createLinearGradient(400, 0, 700, 0); cg.addColorStop(0, 'rgba(120,110,200,0.25)'); cg.addColorStop(1, 'rgba(255,230,160,0.2)'); lg.fillStyle = cg; lg.fillRect(380, 520, 340, 400);
|
|
164
|
+
lg.restore();
|
|
165
|
+
c.drawImage(L, 0, 0);
|
|
166
|
+
// 五官与猫脸:细紫线(不勾外轮廓)
|
|
167
|
+
RIG.drawFeatures(c, G, { line: '#5a4f8a', iris: '#4a6ab0', lip: '#d8707a', featureW: 0.9, blink: ch.blink, cheek: 'rgba(240,140,140,.35)' });
|
|
168
|
+
c.save(); c.strokeStyle = '#5a4f8a'; c.lineWidth = 2.2; c.lineCap = 'round';
|
|
169
|
+
K.eyes.forEach(e => { if (K.blink) { c.beginPath(); c.arc(e.x, e.y, 7, 0.2, Math.PI - 0.2); c.stroke(); } else { c.fillStyle = '#7ab060'; c.beginPath(); c.arc(e.x, e.y, 8, 0, TAU); c.fill(); c.fillStyle = '#2a2a4a'; c.beginPath(); c.ellipse(e.x + 2, e.y, 2.5, 6, 0, 0, TAU); c.fill(); } });
|
|
170
|
+
c.fillStyle = '#d8746a'; c.beginPath(); c.arc(K.nose[0], K.nose[1], 5, 0, TAU); c.fill();
|
|
171
|
+
K.whiskers.forEach(w => { c.beginPath(); c.moveTo(...w[0]); c.lineTo(...w[1]); c.stroke(); });
|
|
172
|
+
c.restore();
|
|
173
|
+
P.steam(c, t, G.cup.x, G.cup.y - 6, { h: 70, n: 2, color: 'rgba(250,246,255,.8)', width: 5, spread: 14, wobble: 9 });
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
})();
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// 1884 修拉《大碗岛的星期天下午》——纯代码点彩。
|
|
2
|
+
// 管线:①整幅平涂底稿(含角色,每帧重画,几毫秒)
|
|
3
|
+
// → ②底层:底稿模糊提亮当「打底色」(点与点的缝里露出来)
|
|
4
|
+
// → ③点彩渲染器:固定六角网格、点大小一致(pitch 10、r 4.5);每个点按「视觉混色」从 14 色纯色板里挑:
|
|
5
|
+
// 找一对纯色 (i,j) 和比例 w∈{0,¼,½,¾,1} 使 w·Pi+(1-w)·Pj 最接近目标色,再用格子哈希 < w 决定这一点是 i 还是 j
|
|
6
|
+
// + 12% 概率换成补色点(绿里的橙、蓝里的橙、橙里的蓝)+ 明暗交界处的光渗(亮侧加白、暗侧加深蓝)
|
|
7
|
+
// → ④修拉自己画的点彩边框(深蓝+橙)
|
|
8
|
+
// 静穆:点的位置不动,只有底稿变了的地方点才换色。母题:河上帆船漂移、水面点闪(6fps 换哈希)、蝴蝶扑翅、汽船冒烟。
|
|
9
|
+
SCENES['29_seurat'] = (() => {
|
|
10
|
+
const W = 1920, H = 1080, TAU = Math.PI * 2, { clamp, lerp } = U, P = PAINT;
|
|
11
|
+
const PAL = ['#1a2058', '#2a3a9a', '#3f6fd0', '#6aaee6', '#1f6a52', '#2f9a5a', '#a8c83a', '#f2cf3a', '#f08a2a', '#e0432a', '#c0306a', '#7a4aa8', '#f8f4e8', '#e8b48a', '#f6e49a', '#f6c48a', '#cfe08a', '#b0cff0', '#f2b8c8', '#c99a4a', '#9a7ac8'];
|
|
12
|
+
// 补色索引:每个纯色对应的「对比色」
|
|
13
|
+
const COMP = { 0: 8, 1: 8, 2: 8, 3: 9, 4: 9, 5: 10, 6: 11, 7: 11, 8: 2, 9: 5, 10: 5, 11: 7, 12: 17, 13: 17, 14: 20, 15: 17, 16: 18, 17: 15, 18: 16, 19: 2, 20: 14 };
|
|
14
|
+
|
|
15
|
+
// ---- 底稿 ----
|
|
16
|
+
const WIN = [372, 142, 416, 396];
|
|
17
|
+
const inWater = (x, y) => x > 372 && x < 788 && y > 300 && y < 380;
|
|
18
|
+
function base(g, t, ch, G, K) {
|
|
19
|
+
// 墙:阳光奶黄,右侧转冷
|
|
20
|
+
const wg = g.createLinearGradient(0, 0, W, 0); wg.addColorStop(0, '#f0d488'); wg.addColorStop(0.55, '#ecc88e'); wg.addColorStop(1, '#b4aecc'); g.fillStyle = wg; g.fillRect(0, 0, W, 700);
|
|
21
|
+
// 墙上窗投下的光斑
|
|
22
|
+
g.fillStyle = '#f8e8a0'; g.beginPath(); g.moveTo(880, 140); g.lineTo(1120, 180); g.lineTo(1120, 560); g.lineTo(880, 560); g.closePath(); g.fill();
|
|
23
|
+
// 地板:暖橙木,阴影紫
|
|
24
|
+
const fg = g.createLinearGradient(0, 700, 0, H); fg.addColorStop(0, '#c08a58'); fg.addColorStop(1, '#dca86a'); g.fillStyle = fg; g.fillRect(0, 700, W, H - 700);
|
|
25
|
+
g.fillStyle = '#5a4a7a'; g.fillRect(0, 696, W, 10);
|
|
26
|
+
g.fillStyle = 'rgba(70,50,110,.55)'; g.beginPath(); g.ellipse(560, 915, 170, 22, 0, 0, TAU); g.fill(); g.beginPath(); g.ellipse(1010, 910, 220, 18, 0, 0, TAU); g.fill(); g.beginPath(); g.ellipse(1300, 930, 200, 18, 0, 0, TAU); g.fill();
|
|
27
|
+
// ---- 窗:大碗岛 ----
|
|
28
|
+
g.save(); g.beginPath(); g.rect(...WIN); g.clip();
|
|
29
|
+
const sg = g.createLinearGradient(0, 142, 0, 300); sg.addColorStop(0, '#bcd4ec'); sg.addColorStop(1, '#e8eef0'); g.fillStyle = sg; g.fillRect(372, 142, 416, 160);
|
|
30
|
+
g.fillStyle = '#3f7a4a'; g.beginPath(); g.moveTo(372, 300); for (let x = 372; x <= 788; x += 26) g.lineTo(x, 280 - Math.abs(Math.sin(x * 0.04)) * 30); g.lineTo(788, 302); g.closePath(); g.fill(); // 对岸树
|
|
31
|
+
g.fillStyle = '#3d6fc0'; g.fillRect(372, 300, 416, 80); // 塞纳河
|
|
32
|
+
// 帆船(漂移)+汽船烟
|
|
33
|
+
[[0, 470, 1], [1, 690, 0.8]].forEach(([k, x0, s]) => { const x = 372 + ((x0 - 372 + t * 40 * (k ? -0.7 : 1)) % 416 + 416) % 416, y = 336 + k * 12;
|
|
34
|
+
g.fillStyle = '#f8f4e8'; g.beginPath(); g.moveTo(x, y); g.lineTo(x, y - 46 * s); g.lineTo(x + 28 * s, y); g.closePath(); g.fill(); g.fillStyle = '#7a4a3a'; g.fillRect(x - 18 * s, y, 50 * s, 7 * s); });
|
|
35
|
+
const sx = 420 + t * 18; g.fillStyle = '#2a2a3a'; g.fillRect(sx, 316, 46, 10); g.fillRect(sx + 26, 300, 7, 16);
|
|
36
|
+
for (let i = 0; i < 5; i++) { const q = (t * 0.8 + i / 5) % 1; g.fillStyle = `rgba(200,200,210,${0.9 - q * 0.6})`; g.beginPath(); g.arc(sx + 30 - q * 40, 292 - q * 50, 8 + q * 14, 0, TAU); g.fill(); }
|
|
37
|
+
// 草地:前景阳光黄绿,树荫翠绿
|
|
38
|
+
g.fillStyle = '#b8c84a'; g.fillRect(372, 380, 416, 160);
|
|
39
|
+
g.fillStyle = '#2f6a4a'; g.beginPath(); g.moveTo(372, 430); g.quadraticCurveTo(520, 400, 640, 440); g.lineTo(640, 540); g.lineTo(372, 540); g.closePath(); g.fill();
|
|
40
|
+
// 树干(右)+树冠
|
|
41
|
+
g.fillStyle = '#4a3a3a'; g.fillRect(730, 150, 22, 390); g.fillStyle = '#1f5a3a'; g.beginPath(); g.ellipse(740, 150, 120, 60, 0, 0, TAU); g.fill();
|
|
42
|
+
// 撑伞的侧影女士(大碗岛的标志:裙撑+阳伞)+小猴
|
|
43
|
+
const lx = 600, ly = 520;
|
|
44
|
+
g.fillStyle = '#23244a'; g.beginPath(); g.moveTo(lx, ly); g.lineTo(lx + 4, ly - 70); g.quadraticCurveTo(lx + 30, ly - 60, lx + 34, ly - 30); g.quadraticCurveTo(lx + 44, ly - 10, lx + 36, ly); g.closePath(); g.fill(); // 裙+裙撑
|
|
45
|
+
g.beginPath(); g.ellipse(lx + 6, ly - 84, 8, 12, 0, 0, TAU); g.fill(); g.fillRect(lx - 1, ly - 76, 14, 14);
|
|
46
|
+
g.fillStyle = '#c8603a'; g.beginPath(); g.ellipse(lx + 4, ly - 112, 30, 12, 0, Math.PI, TAU); g.fill(); g.strokeStyle = '#23244a'; g.lineWidth = 2; g.beginPath(); g.moveTo(lx + 4, ly - 112); g.lineTo(lx + 8, ly - 70); g.stroke();
|
|
47
|
+
g.fillStyle = '#4a3a3a'; g.beginPath(); g.ellipse(lx - 30, ly - 6, 10, 7, 0, 0, TAU); g.fill(); g.strokeStyle = '#4a3a3a'; g.lineWidth = 3; g.beginPath(); g.moveTo(lx - 40, ly - 6); g.quadraticCurveTo(lx - 54, ly - 20, lx - 44, ly - 28); g.stroke();
|
|
48
|
+
// 蝴蝶(扑翅)
|
|
49
|
+
const bx = 470 + Math.sin(t * 2.2) * 60, by = 420 + Math.sin(t * 3.7) * 30, fl = Math.abs(Math.sin(t * 22));
|
|
50
|
+
g.fillStyle = '#f08a2a'; g.beginPath(); g.ellipse(bx - 8, by, 10 * fl + 2, 7, -0.4, 0, TAU); g.ellipse(bx + 8, by, 10 * fl + 2, 7, 0.4, 0, TAU); g.fill();
|
|
51
|
+
g.restore();
|
|
52
|
+
// 窗框(白)+窗台
|
|
53
|
+
g.lineWidth = 22; g.strokeStyle = '#f2ede0'; g.strokeRect(357, 127, 446, 426); g.fillStyle = '#f2ede0'; g.fillRect(570, 142, 18, 396); g.fillRect(330, 548, 500, 28);
|
|
54
|
+
// 曲木椅(索耐特)
|
|
55
|
+
g.strokeStyle = '#4a2a2a'; g.lineWidth = 12; g.lineCap = 'round'; g.beginPath(); g.moveTo(1450, 900); g.lineTo(1452, 520); g.quadraticCurveTo(1478, 470, 1500, 530); g.lineTo(1486, 900); g.stroke(); g.beginPath(); g.ellipse(1474, 600, 18, 60, 0, 0, TAU); g.stroke();
|
|
56
|
+
// 桌:白桌布
|
|
57
|
+
g.fillStyle = '#f2eee0'; g.fillRect(806, 616, 404, 140); g.fillStyle = '#9aa6d8'; for (let x = 830; x < 1200; x += 60) g.fillRect(x, 640, 14, 116);
|
|
58
|
+
g.fillStyle = '#6a3a2a'; g.fillRect(846, 756, 22, 148); g.fillRect(1150, 756, 22, 148);
|
|
59
|
+
// 茶壶(白瓷)+一束罂粟
|
|
60
|
+
g.fillStyle = '#f6f2ea'; g.beginPath(); g.ellipse(900, 590, 40, 30, 0, 0, TAU); g.fill(); g.fillStyle = '#e0432a'; [[1030, 540], [1052, 520], [1074, 546]].forEach(([x, y]) => { g.beginPath(); g.arc(x, y, 14, 0, TAU); g.fill(); });
|
|
61
|
+
g.strokeStyle = '#2f9a5a'; g.lineWidth = 5; [[1030, 540], [1052, 520], [1074, 546]].forEach(([x, y]) => { g.beginPath(); g.moveTo(x, y + 12); g.lineTo(1052, 616); g.stroke(); });
|
|
62
|
+
// 角色(平涂,进点彩)
|
|
63
|
+
RIG.drawCat(g, K, { lw: 0, orange: '#ec8a34', white: '#f8f2e4', stripe: '#c0501a', stripeW: 9, face: (c, K) => {
|
|
64
|
+
K.eyes.forEach(e => { c.fillStyle = K.blink ? '#c0501a' : '#2f9a5a'; c.beginPath(); c.arc(e.x, e.y, 9, 0, TAU); c.fill(); if (!K.blink) { c.fillStyle = '#1a2058'; c.beginPath(); c.arc(e.x + 2, e.y, 4, 0, TAU); c.fill(); } });
|
|
65
|
+
c.fillStyle = '#e0605a'; c.beginPath(); c.arc(K.nose[0], K.nose[1], 6, 0, TAU); c.fill(); } });
|
|
66
|
+
RIG.drawGirl(g, G, { lw: 0, line: '#111', locks: false, skin: '#f2cca8', hair: '#f0c850', dress: '#2c2c64', sleeve: '#2c2c64', cuff: '#f2eee0', shoe: '#1a2058', cupBody: '#f6f2ea', cupRim: '#9a5a2a',
|
|
67
|
+
features: (c, G) => { c.fillStyle = '#1a2058'; c.beginPath(); c.arc(G.eye.x, G.eye.y, 4.5, 0, TAU); c.fill(); c.fillStyle = '#d8584a'; c.beginPath(); c.arc(G.lips[1][0] - 2, G.lips[1][1] + 2, 4, 0, TAU); c.fill();
|
|
68
|
+
c.fillStyle = 'rgba(232,120,110,.6)'; c.beginPath(); c.arc(G.cheek[0], G.cheek[1], 11, 0, TAU); c.fill(); },
|
|
69
|
+
hooks: {
|
|
70
|
+
back: (c, G) => { // 1880 年代裙撑(腰后鼓起)
|
|
71
|
+
const w = G.A.waist; c.fillStyle = '#2c2c64'; c.beginPath(); c.moveTo(w[0] - 10, w[1] - 10); c.quadraticCurveTo(w[0] + 110, w[1] - 6, w[0] + 98, w[1] + 90); c.quadraticCurveTo(w[0] + 60, w[1] + 140, w[0] - 10, w[1] + 120); c.closePath(); c.fill(); },
|
|
72
|
+
torso: (c, G) => { // 高领白蕾丝+前襟一排橙色纽
|
|
73
|
+
const n = G.A.neck; c.fillStyle = '#f2eee0'; c.fillRect(n[0] - 18, n[1] - 26, 34, 30);
|
|
74
|
+
c.fillStyle = '#f08a2a'; [0, 1, 2, 3].forEach(i => { c.beginPath(); c.arc(G.A.chest[0] + 4, G.A.chest[1] - 30 + i * 28, 5, 0, TAU); c.fill(); }); },
|
|
75
|
+
head: (c, G) => { // 小圆帽+红羽毛(大碗岛女帽)
|
|
76
|
+
const h = G.A.headTop; c.save(); c.translate(h[0], h[1]); c.rotate(G.tilt - 0.15);
|
|
77
|
+
c.fillStyle = '#2a3a9a'; c.beginPath(); c.ellipse(0, 4, 64, 12, 0, 0, TAU); c.fill(); c.beginPath(); c.ellipse(4, -10, 36, 22, 0, Math.PI, TAU); c.fill();
|
|
78
|
+
c.fillStyle = '#e0432a'; c.beginPath(); c.ellipse(34, -20, 26, 8, -0.6, 0, TAU); c.fill(); c.restore(); } } });
|
|
79
|
+
// 椅背上靠着一把收拢的阳伞
|
|
80
|
+
g.strokeStyle = '#4a2a2a'; g.lineWidth = 4; g.beginPath(); g.moveTo(1510, 900); g.lineTo(1556, 560); g.stroke();
|
|
81
|
+
g.fillStyle = '#e0432a'; g.beginPath(); g.moveTo(1550, 610); g.lineTo(1560, 560); g.lineTo(1528, 760); g.lineTo(1518, 752); g.closePath(); g.fill();
|
|
82
|
+
// 室内的蝴蝶(大碗岛画面中央那只),在桌上方扑翅绕飞
|
|
83
|
+
{ const bx = 1000 + Math.sin(t * 2.6) * 120, by = 400 + Math.sin(t * 4.1) * 50 - t * 20, fl = 0.25 + 0.75 * Math.abs(Math.sin(t * 20)), dir = Math.cos(t * 2.6) > 0 ? 1 : -1;
|
|
84
|
+
g.save(); g.translate(bx, by); g.scale(dir, 1); g.fillStyle = '#f08a2a'; g.beginPath(); g.ellipse(-14, -6, 22 * fl, 16, -0.5, 0, TAU); g.ellipse(14, -6, 22 * fl, 16, 0.5, 0, TAU); g.fill();
|
|
85
|
+
g.fillStyle = '#e0432a'; g.beginPath(); g.ellipse(-10, 10, 12 * fl, 9, -0.2, 0, TAU); g.ellipse(10, 10, 12 * fl, 9, 0.2, 0, TAU); g.fill(); g.fillStyle = '#1a2058'; g.fillRect(-2, -14, 4, 28); g.restore(); }
|
|
86
|
+
// 修拉画的边框:深蓝带
|
|
87
|
+
g.fillStyle = '#1e2a78'; g.fillRect(0, 0, W, 28); g.fillRect(0, H - 28, W, 28); g.fillRect(0, 0, 28, H); g.fillRect(W - 28, 0, 28, H);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
draw(c, lt, t) {
|
|
92
|
+
const ch = P.choreo(lt, t);
|
|
93
|
+
const G = RIG.girl({ cup: ch.cup, sip: ch.sip, hair: 'bun', breathe: ch.breathe });
|
|
94
|
+
const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe });
|
|
95
|
+
const b = P.scratch('seBase'), bg = b.getContext('2d', { willReadFrequently: true }); bg.reset(); base(bg, t, ch, G, K);
|
|
96
|
+
// 打底:模糊提亮
|
|
97
|
+
c.save(); c.filter = 'blur(4px) saturate(0.85) brightness(1.06)'; c.drawImage(b, 0, 0); c.restore();
|
|
98
|
+
// 点彩:P.pointillism——等大点、纯色板两两并置做视觉混色;水面点 6fps 重掷=波光;12=白点、0=深蓝点做光渗
|
|
99
|
+
P.pointillism(c, b, { pal: PAL, comp: COMP, t, fps: 6, shimmer: inWater, white: 12, dark: 0 });
|
|
100
|
+
// 边框内沿一道细深线(修拉画框的收边)
|
|
101
|
+
c.strokeStyle = 'rgba(20,24,70,.8)'; c.lineWidth = 3; c.strokeRect(29, 29, W - 58, H - 58);
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
})();
|