@biunow/agent-biuart 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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,338 @@
|
|
|
1
|
+
// 1503 文艺复兴盛期油画(达芬奇式)——纯代码。
|
|
2
|
+
// 管线:①静态层(暗褐抹灰墙+窗光晕、拱窗石框与窗洞内的「空气透视」远景:层叠蓝山/湖/蜿蜒银河/柏树、透视棋盘地砖、方桌白布、梯背椅)
|
|
3
|
+
// 整层 blur 1.3px 做 sfumato → ②每帧:扑翼机扇翅飞过窗、飞鸟、河面碎光、光柱里的浮尘
|
|
4
|
+
// → ③角色画进离屏层(渐变体积+天鹅绒高光+写实毛发短线),整层 blur 1.6px 再叠 0.45 清晰层=晕涂柔边
|
|
5
|
+
// → ④罩一层「画面」:龟裂纹+画布经纬+暗角+黄釉(缓存,一次 drawImage)
|
|
6
|
+
SCENES['06_renaissance'] = (() => {
|
|
7
|
+
const W = 1920, H = 1080, { clamp, lerp } = U, P = PAINT;
|
|
8
|
+
const R_ = (s) => U.rng(s);
|
|
9
|
+
const poly = (pts, closed = true) => { const p = new Path2D(); pts.forEach((q, i) => i ? p.lineTo(q[0], q[1]) : p.moveTo(q[0], q[1])); if (closed) p.closePath(); return p; };
|
|
10
|
+
const archPath = (xl, xr, cy, yb) => { const p = new Path2D(), r = (xr - xl) / 2; p.moveTo(xl, yb); p.lineTo(xl, cy); p.arc(xl + r, cy, r, Math.PI, 0); p.lineTo(xr, yb); p.closePath(); return p; };
|
|
11
|
+
const OUTER = archPath(360, 810, 335, 560), INNER = archPath(422, 800, 330, 542);
|
|
12
|
+
|
|
13
|
+
// ---------- 远景(窗洞内) ----------
|
|
14
|
+
const RIVER = [[548, 362], [620, 372], [676, 388], [652, 404], [580, 420], [548, 438], [590, 458], [668, 474], [720, 492], [748, 512], [790, 534]];
|
|
15
|
+
function landscape(g) {
|
|
16
|
+
g.save(); g.clip(INNER);
|
|
17
|
+
const sky = g.createLinearGradient(0, 140, 0, 350); sky.addColorStop(0, '#93b0b6'); sky.addColorStop(0.6, '#c8d4cc'); sky.addColorStop(1, '#e4e2cf');
|
|
18
|
+
g.fillStyle = sky; g.fillRect(400, 130, 420, 230);
|
|
19
|
+
// 云:柔软的横向团块
|
|
20
|
+
const r = R_(7);
|
|
21
|
+
for (let k = 0; k < 14; k++) { const x = 420 + r() * 380, y = 175 + r() * 90, rw = 50 + r() * 70; const cg = g.createRadialGradient(x, y, 0, x, y, rw); cg.addColorStop(0, 'rgba(240,238,225,.55)'); cg.addColorStop(1, 'rgba(240,238,225,0)'); g.fillStyle = cg; g.save(); g.translate(x, y); g.scale(1, 0.32); g.translate(-x, -y); g.beginPath(); g.arc(x, y, rw, 0, 7); g.fill(); g.restore(); }
|
|
22
|
+
// 层叠山(越远越蓝越淡=空气透视)
|
|
23
|
+
const ranges = [[300, '#9fb4b8', 0.010, 26, 3], [318, '#8aa1a6', 0.016, 22, 5], [336, '#7d9294', 0.02, 14, 9]];
|
|
24
|
+
for (const [base, col, f, amp, sd] of ranges) { g.fillStyle = col; g.beginPath(); g.moveTo(410, 360); for (let x = 410; x <= 810; x += 4) g.lineTo(x, base - Math.abs(P.fbm(x * f, sd, 4)) * amp * 3 - P.noise(x * f * 3, sd) * amp * 0.4); g.lineTo(810, 360); g.closePath(); g.fill(); }
|
|
25
|
+
// 湖
|
|
26
|
+
const lake = g.createLinearGradient(0, 340, 0, 366); lake.addColorStop(0, '#dfe3d6'); lake.addColorStop(1, '#c9d2c2'); g.fillStyle = lake; g.fillRect(410, 340, 400, 24);
|
|
27
|
+
// 河谷:近处转暖
|
|
28
|
+
const vg = g.createLinearGradient(0, 360, 0, 545); vg.addColorStop(0, '#93a08a'); vg.addColorStop(0.45, '#7a8462'); vg.addColorStop(1, '#7c7048');
|
|
29
|
+
g.fillStyle = vg; g.beginPath(); g.moveTo(410, 366); for (let x = 410; x <= 810; x += 6) g.lineTo(x, 362 + P.noise(x * 0.02, 3) * 4); g.lineTo(810, 545); g.lineTo(410, 545); g.closePath(); g.fill();
|
|
30
|
+
// 远丘斑块
|
|
31
|
+
for (let k = 0; k < 40; k++) { const x = 420 + r() * 380, y = 380 + r() * 150, s = 0.4 + (y - 370) / 180; g.fillStyle = `rgba(${90 + r() * 30},${100 + r() * 20},${60 + r() * 15},.35)`; g.beginPath(); g.ellipse(x, y, 30 * s, 6 * s, 0, 0, 7); g.fill(); }
|
|
32
|
+
// 河:宽度随距离变大
|
|
33
|
+
g.lineCap = 'round'; g.lineJoin = 'round';
|
|
34
|
+
for (let pass = 0; pass < 2; pass++) for (let i = 0; i < RIVER.length - 1; i++) {
|
|
35
|
+
const a = RIVER[i], b = RIVER[i + 1], w = 3 + i * 1.3;
|
|
36
|
+
g.strokeStyle = pass ? '#eef0e8' : 'rgba(90,100,80,.6)'; g.lineWidth = pass ? w : w + 3;
|
|
37
|
+
g.beginPath(); g.moveTo(...a); g.lineTo(...b); g.stroke();
|
|
38
|
+
}
|
|
39
|
+
// 小路
|
|
40
|
+
g.strokeStyle = 'rgba(200,190,150,.6)'; g.lineWidth = 3; g.beginPath(); g.moveTo(470, 545); g.bezierCurveTo(500, 500, 470, 470, 510, 440); g.stroke();
|
|
41
|
+
// 树:柏树(深色竖椭圆)+圆树
|
|
42
|
+
for (let k = 0; k < 26; k++) { const x = 425 + r() * 370, y = 395 + r() * 140, s = 0.4 + (y - 380) / 150; if (Math.abs(x - 640) < 30 && y < 420) continue;
|
|
43
|
+
g.fillStyle = r() < 0.5 ? '#3f4c30' : '#4c5a36';
|
|
44
|
+
if (r() < 0.55) { g.beginPath(); g.ellipse(x, y - 14 * s, 4 * s, 15 * s, 0, 0, 7); g.fill(); } else { g.beginPath(); g.arc(x, y - 6 * s, 9 * s, 0, 7); g.fill(); } }
|
|
45
|
+
// 石桥
|
|
46
|
+
g.fillStyle = '#b8ad8c'; g.fillRect(566, 452, 56, 7); g.fillStyle = '#6e6650'; for (let k = 0; k < 3; k++) { g.beginPath(); g.arc(578 + k * 16, 460, 5, Math.PI, 0); g.fill(); }
|
|
47
|
+
g.restore();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ---------- 静态层 ----------
|
|
51
|
+
const VP = { yh: 420, cx: 960, f: 1029, h: 5.75 };
|
|
52
|
+
const proj = (X, Z) => [VP.cx + VP.f * X / Z, VP.yh + VP.f * VP.h / Z];
|
|
53
|
+
function room(g) {
|
|
54
|
+
// 墙:抹灰斑驳+窗光晕
|
|
55
|
+
g.drawImage(P.texture('ren_wall', '#43321f', { scale: 0.0024, amt: 30, grain: 12, dark: '#22180e', seed: 21 }), 0, 0);
|
|
56
|
+
const top = g.createLinearGradient(0, 0, 0, 780); top.addColorStop(0, 'rgba(12,7,3,.45)'); top.addColorStop(0.5, 'rgba(12,7,3,0)'); top.addColorStop(1, 'rgba(12,7,3,.15)'); g.fillStyle = top; g.fillRect(0, 0, W, 780);
|
|
57
|
+
const glow = g.createRadialGradient(585, 360, 60, 585, 380, 900); glow.addColorStop(0, 'rgba(190,150,100,.42)'); glow.addColorStop(0.45, 'rgba(150,110,70,.16)'); glow.addColorStop(1, 'rgba(0,0,0,0)');
|
|
58
|
+
g.fillStyle = glow; g.fillRect(0, 0, W, H);
|
|
59
|
+
// 墙裙阴影线
|
|
60
|
+
const bb = g.createLinearGradient(0, 760, 0, 792); bb.addColorStop(0, 'rgba(20,12,6,0)'); bb.addColorStop(0.7, 'rgba(20,12,6,.45)'); bb.addColorStop(1, 'rgba(20,12,6,.7)'); g.fillStyle = bb; g.fillRect(0, 760, W, 32);
|
|
61
|
+
// 地砖:透视棋盘(Z 从墙 16 往前,砖宽 1.4)
|
|
62
|
+
const zs = []; for (let z = 16; z > 5; z -= 1.4) zs.push(z);
|
|
63
|
+
for (let j = 0; j < zs.length; j++) { const z0 = zs[j], z1 = z0 - 1.4;
|
|
64
|
+
for (let i = -14; i < 14; i++) { const X0 = i * 1.4, X1 = X0 + 1.4;
|
|
65
|
+
const p = [proj(X0, z0), proj(X1, z0), proj(X1, z1), proj(X0, z1)];
|
|
66
|
+
if (p[2][0] < -300 || p[3][0] > W + 300) continue;
|
|
67
|
+
g.fillStyle = (i + j) % 2 ? '#a88c64' : '#5c2418'; g.beginPath(); p.forEach((q, k) => k ? g.lineTo(...q) : g.moveTo(...q)); g.closePath(); g.fill();
|
|
68
|
+
g.strokeStyle = 'rgba(30,15,8,.35)'; g.lineWidth = 1.2; g.stroke(); } }
|
|
69
|
+
// 地面受光:窗光落在猫前方
|
|
70
|
+
g.save(); g.globalCompositeOperation = 'soft-light';
|
|
71
|
+
const lp = g.createRadialGradient(700, 950, 30, 700, 950, 520); lp.addColorStop(0, 'rgba(255,230,180,.95)'); lp.addColorStop(1, 'rgba(255,230,180,0)'); g.fillStyle = lp; g.fillRect(0, 780, W, 300);
|
|
72
|
+
g.restore();
|
|
73
|
+
const fd = g.createLinearGradient(0, 790, 0, 1080); fd.addColorStop(0, 'rgba(25,14,8,.55)'); fd.addColorStop(0.35, 'rgba(25,14,8,.2)'); fd.addColorStop(1, 'rgba(25,14,8,.45)'); g.fillStyle = fd; g.fillRect(0, 790, W, 290);
|
|
74
|
+
const fside = g.createRadialGradient(760, 960, 120, 760, 960, 1100); fside.addColorStop(0, 'rgba(18,9,4,0)'); fside.addColorStop(0.5, 'rgba(18,9,4,.3)'); fside.addColorStop(1, 'rgba(18,9,4,.75)'); g.fillStyle = fside; g.fillRect(0, 790, W, 290);
|
|
75
|
+
// 拱窗:石框(左侧受光)+窗洞
|
|
76
|
+
g.save(); g.shadowColor = 'rgba(0,0,0,.5)'; g.shadowBlur = 30; g.shadowOffsetX = 10; g.shadowOffsetY = 14;
|
|
77
|
+
const st = g.createLinearGradient(360, 0, 810, 0); st.addColorStop(0, '#c6b08a'); st.addColorStop(0.5, '#a48c68'); st.addColorStop(1, '#7c6a4e');
|
|
78
|
+
g.fillStyle = st; g.fill(OUTER); g.restore();
|
|
79
|
+
const lightR = g.createLinearGradient(360, 0, 430, 0); lightR.addColorStop(0, 'rgba(255,240,210,.0)'); lightR.addColorStop(1, 'rgba(255,240,210,.35)'); g.fillStyle = lightR; g.fill(OUTER);
|
|
80
|
+
landscape(g);
|
|
81
|
+
g.strokeStyle = 'rgba(60,40,20,.55)'; g.lineWidth = 3; g.stroke(INNER);
|
|
82
|
+
// 窗台
|
|
83
|
+
const sl = g.createLinearGradient(0, 556, 0, 582); sl.addColorStop(0, '#d8c8a4'); sl.addColorStop(1, '#8c7a5a'); g.fillStyle = sl; g.fillRect(335, 556, 490, 24);
|
|
84
|
+
g.fillStyle = 'rgba(0,0,0,.35)'; g.fillRect(345, 580, 480, 10);
|
|
85
|
+
// 桌子影子
|
|
86
|
+
g.fillStyle = 'rgba(15,8,4,.45)'; g.beginPath(); g.ellipse(1000, 905, 230, 22, 0, 0, 7); g.fill();
|
|
87
|
+
// 椅(梯背,在少女后)
|
|
88
|
+
const wood = (x, y, w, h) => { const wg = g.createLinearGradient(x, 0, x + w, 0); wg.addColorStop(0, '#7a4a26'); wg.addColorStop(0.4, '#9a6234'); wg.addColorStop(1, '#4a2c16'); g.fillStyle = wg; g.fillRect(x, y, w, h); };
|
|
89
|
+
wood(1458, 445, 18, 455); g.fillStyle = '#9a6234'; g.beginPath(); g.ellipse(1467, 447, 11, 6, 0, 0, 7); g.fill();
|
|
90
|
+
for (let k = 0; k < 4; k++) wood(1440, 490 + k * 58, 30, 8);
|
|
91
|
+
wood(1430, 560, 14, 340);
|
|
92
|
+
g.fillStyle = 'rgba(15,8,4,.4)'; g.beginPath(); g.ellipse(1400, 905, 130, 14, 0, 0, 7); g.fill();
|
|
93
|
+
// 方桌:桌腿+横撑+白桌布
|
|
94
|
+
wood(838, 750, 16, 155); wood(1160, 750, 16, 155); wood(852, 818, 310, 9); wood(905, 748, 12, 120);
|
|
95
|
+
const cl = poly([[815, 626], [1205, 626], [1210, 758], [812, 760]]);
|
|
96
|
+
const cg = g.createLinearGradient(815, 0, 1210, 0); cg.addColorStop(0, '#e8dcbc'); cg.addColorStop(0.5, '#d6c8a4'); cg.addColorStop(1, '#a8987a'); g.fillStyle = cg; g.fill(cl);
|
|
97
|
+
g.save(); g.clip(cl);
|
|
98
|
+
for (let x = 830; x < 1210; x += 34) { const fg = g.createLinearGradient(x, 0, x + 34, 0); fg.addColorStop(0, 'rgba(255,250,235,.25)'); fg.addColorStop(0.5, 'rgba(80,60,40,.12)'); fg.addColorStop(1, 'rgba(255,250,235,.0)'); g.fillStyle = fg; g.fillRect(x, 640, 34, 120); }
|
|
99
|
+
g.fillStyle = 'rgba(255,250,235,.55)'; g.fillRect(815, 626, 395, 12);
|
|
100
|
+
g.fillStyle = '#7f95a8'; g.fillRect(812, 734, 400, 3); g.fillRect(812, 741, 400, 2);
|
|
101
|
+
g.restore();
|
|
102
|
+
g.strokeStyle = 'rgba(60,45,30,.35)'; g.lineWidth = 2; g.stroke(cl);
|
|
103
|
+
}
|
|
104
|
+
const STATIC = () => P.cached('ren_static', W, H, (g) => {
|
|
105
|
+
const raw = P.canvas(), rg = raw.getContext('2d'); room(rg);
|
|
106
|
+
g.filter = 'blur(1.3px)'; g.drawImage(raw, 0, 0); g.filter = 'none';
|
|
107
|
+
});
|
|
108
|
+
// 罩层:龟裂纹+画布纹+暗角+黄釉
|
|
109
|
+
const OVER = () => P.cached('ren_over', W, H, (g) => {
|
|
110
|
+
const v = g.createRadialGradient(820, 520, 300, 900, 560, 1250); v.addColorStop(0, 'rgba(20,10,4,0)'); v.addColorStop(0.55, 'rgba(20,10,4,.3)'); v.addColorStop(1, 'rgba(10,5,2,.8)'); g.fillStyle = v; g.fillRect(0, 0, W, H);
|
|
111
|
+
g.fillStyle = 'rgba(150,110,40,.07)'; g.fillRect(0, 0, W, H);
|
|
112
|
+
const r = R_(4); g.lineWidth = 1;
|
|
113
|
+
for (let y = 0; y < H; y += 3) { g.strokeStyle = `rgba(0,0,0,${0.03 + r() * 0.04})`; g.beginPath(); g.moveTo(0, y + .5); g.lineTo(W, y + .5); g.stroke(); }
|
|
114
|
+
for (let x = 0; x < W; x += 3) { g.strokeStyle = `rgba(255,240,210,${0.015 + r() * 0.03})`; g.beginPath(); g.moveTo(x + .5, 0); g.lineTo(x + .5, H); g.stroke(); }
|
|
115
|
+
// 油彩笔触肌理:几万笔极淡的短笔(亮暗各半,方向随噪声),让平涂面有「刷过」的感觉
|
|
116
|
+
g.lineCap = 'round';
|
|
117
|
+
for (let k = 0; k < 9000; k++) { const x = r() * W, y = r() * H, a = r() * Math.PI, L = 10 + r() * 16;
|
|
118
|
+
g.strokeStyle = r() < 0.5 ? 'rgba(255,235,200,.022)' : 'rgba(20,10,0,.028)'; g.lineWidth = 5 + r() * 5; g.beginPath(); g.moveTo(x, y); g.lineTo(x + Math.cos(a) * L, y + Math.sin(a) * L); g.stroke(); }
|
|
119
|
+
// 龟裂:暗线+右下错 1px 的亮边(漆皮翘起)
|
|
120
|
+
g.globalAlpha = 0.32; g.drawImage(P.craquelure('ren', 0.55), 0, 0);
|
|
121
|
+
g.globalAlpha = 0.16; g.globalCompositeOperation = 'lighter'; g.filter = 'brightness(0) invert(1)'; g.drawImage(P.craquelure('ren', 0.55), 1, 1); g.filter = 'none';
|
|
122
|
+
g.globalAlpha = 1; g.globalCompositeOperation = 'source-over';
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// ---------- 动态:扑翼机、飞鸟、河光、浮尘 ----------
|
|
126
|
+
function ornithopter(c, lt, t) {
|
|
127
|
+
// 达芬奇扑翼机:木架上趴一个红衣人,两张蝙蝠翼(前缘梁+放射肋+扇贝后缘膜)上下扇,后有扇形尾舵
|
|
128
|
+
const q = clamp(lt / 1.3), x = lerp(440, 790, q), y = 226 + Math.sin(lt * 5) * 14 - q * 24, flap = Math.sin(t * 12);
|
|
129
|
+
c.save(); c.clip(INNER); c.translate(x, y); c.rotate(-0.08 + Math.sin(lt * 5 + 1) * 0.07); c.scale(1.6, 1.6); // 放大 1.6:窗里一眼看得到
|
|
130
|
+
const wing = (sc, dark, ph) => {
|
|
131
|
+
const up = 0.5 + 0.5 * Math.sin(t * 12 + ph); // 0=翼压下 1=翼抬起
|
|
132
|
+
const tip = [-26 * sc, (-118 * up + 24 * (1 - up)) * sc];
|
|
133
|
+
const back = [-128 * sc, (-30 * up + 18) * sc];
|
|
134
|
+
const ribs = 5, ends = [];
|
|
135
|
+
for (let k = 0; k <= ribs; k++) { const u = k / ribs; ends.push([lerp(tip[0], back[0], u) - Math.sin(u * Math.PI) * 26 * sc, lerp(tip[1], back[1], u) - Math.sin(u * Math.PI) * 8 * sc * (up - 0.3)]); }
|
|
136
|
+
c.fillStyle = dark ? 'rgba(150,118,80,.92)' : 'rgba(214,188,140,.95)';
|
|
137
|
+
c.beginPath(); c.moveTo(0, 0); c.lineTo(...ends[0]);
|
|
138
|
+
for (let k = 1; k <= ribs; k++) { const a = ends[k - 1], b = ends[k], m = [(a[0] + b[0]) / 2 + 6 * sc, (a[1] + b[1]) / 2 + 9 * sc]; c.quadraticCurveTo(m[0], m[1], b[0], b[1]); }
|
|
139
|
+
c.lineTo(-30 * sc, 6 * sc); c.closePath(); c.fill();
|
|
140
|
+
c.strokeStyle = '#4e321a'; c.lineWidth = 1.6; c.stroke();
|
|
141
|
+
c.lineWidth = 2.2; ends.forEach((e, k) => { c.beginPath(); c.moveTo(-4, 0); c.lineTo(e[0], e[1]); c.lineWidth = k ? 1.4 : 3; c.stroke(); });
|
|
142
|
+
};
|
|
143
|
+
wing(0.78, true, 0.5); // 远翼:小一号、暗一点、相位略错
|
|
144
|
+
c.strokeStyle = '#4e321a'; c.lineWidth = 3.5; c.beginPath(); c.moveTo(40, 8); c.lineTo(-80, 12); c.stroke();
|
|
145
|
+
c.lineWidth = 2; c.beginPath(); c.moveTo(-10, 10); c.lineTo(-30, 30); c.lineTo(10, 30); c.closePath(); c.stroke();
|
|
146
|
+
// 尾舵
|
|
147
|
+
c.fillStyle = 'rgba(214,188,140,.95)'; c.beginPath(); c.moveTo(-78, 12); c.lineTo(-118, -6 + Math.sin(t * 6) * 4); c.quadraticCurveTo(-112, 12, -116, 32 + Math.sin(t * 6) * 4); c.closePath(); c.fill(); c.strokeStyle = '#4e321a'; c.lineWidth = 1.6; c.stroke();
|
|
148
|
+
// 驾驶者
|
|
149
|
+
c.fillStyle = '#a02a1e'; c.beginPath(); c.ellipse(6, 14, 30, 6.5, 0.03, 0, 7); c.fill();
|
|
150
|
+
c.fillStyle = '#d8b090'; c.beginPath(); c.arc(38, 11, 6, 0, 7); c.fill();
|
|
151
|
+
c.strokeStyle = '#a02a1e'; c.lineWidth = 3.5; c.beginPath(); c.moveTo(-22, 15); c.lineTo(-40, 18 + flap * 4); c.stroke();
|
|
152
|
+
wing(1.0, false, 0);
|
|
153
|
+
c.restore();
|
|
154
|
+
}
|
|
155
|
+
function birds(c, lt, t) {
|
|
156
|
+
c.save(); c.clip(INNER); c.strokeStyle = 'rgba(50,40,30,.85)'; c.lineWidth = 1.8; c.lineCap = 'round';
|
|
157
|
+
const r = R_(9);
|
|
158
|
+
for (let k = 0; k < 14; k++) { const x0 = 520 + r() * 300, y0 = 180 + r() * 120, s = 1.0 + r() * 0.8, sp = 30 + r() * 40, ph = r() * 7;
|
|
159
|
+
const x = x0 - lt * sp, y = y0 + Math.sin(lt * 3 + ph) * 4, f = Math.sin(t * 11 + ph) * 5 * s;
|
|
160
|
+
c.beginPath(); c.moveTo(x - 7 * s, y - f); c.quadraticCurveTo(x - 3 * s, y - 2, x, y + 1); c.quadraticCurveTo(x + 3 * s, y - 2, x + 7 * s, y - f); c.stroke(); }
|
|
161
|
+
c.restore();
|
|
162
|
+
}
|
|
163
|
+
function riverGlints(c, t) {
|
|
164
|
+
// 云影:几块淡暗椭圆从左往右掠过河谷(阳光一明一暗)
|
|
165
|
+
c.save(); c.clip(INNER); for (let k = 0; k < 3; k++) { const x = 360 + ((t * 70 + k * 190) % 560), y = 400 + k * 45; const g = c.createRadialGradient(x, y, 5, x, y, 90); g.addColorStop(0, 'rgba(30,40,40,.28)'); g.addColorStop(1, 'rgba(30,40,40,0)'); c.fillStyle = g; c.save(); c.translate(x, y); c.scale(1, 0.35); c.translate(-x, -y); c.beginPath(); c.arc(x, y, 90, 0, 7); c.fill(); c.restore(); } c.restore();
|
|
166
|
+
c.save(); c.clip(INNER); c.globalCompositeOperation = 'lighter';
|
|
167
|
+
for (let k = 0; k < 24; k++) { const u = ((t * 0.22 + k / 24) % 1) * (RIVER.length - 1), i = Math.floor(u), f = u - i, a = RIVER[i], b = RIVER[i + 1];
|
|
168
|
+
const x = lerp(a[0], b[0], f), y = lerp(a[1], b[1], f), tw = Math.max(0, Math.sin(t * 9 + k * 2.3));
|
|
169
|
+
c.fillStyle = `rgba(255,255,245,${0.6 * tw})`; c.beginPath(); c.ellipse(x, y, 2 + i * 0.5, 1.2, 0, 0, 7); c.fill(); }
|
|
170
|
+
c.restore();
|
|
171
|
+
}
|
|
172
|
+
// 光柱(窗→地面)+浮尘
|
|
173
|
+
const BEAM = poly([[440, 330], [790, 360], [1000, 1080], [400, 1080]]);
|
|
174
|
+
const BEAMC = () => P.cached('ren_beam', W, H, (g) => { // 光柱只算一次:大半径模糊让两条边化开
|
|
175
|
+
const bg = g.createLinearGradient(560, 300, 700, 1080); bg.addColorStop(0, 'rgba(255,225,170,.0)'); bg.addColorStop(0.3, 'rgba(255,225,170,.13)'); bg.addColorStop(1, 'rgba(255,225,170,.07)');
|
|
176
|
+
g.filter = 'blur(28px)'; g.fillStyle = bg; g.fill(BEAM); g.filter = 'none';
|
|
177
|
+
});
|
|
178
|
+
function beamAndDust(c, lt, t) {
|
|
179
|
+
c.save(); c.globalCompositeOperation = 'screen';
|
|
180
|
+
c.globalAlpha = 0.75 + 0.35 * Math.sin(t * 2.3); c.drawImage(BEAMC(), 0, 0); c.globalAlpha = 1; // 云过日:光柱时强时弱
|
|
181
|
+
const ps = P.particles(70, 17, t, { x0: 440, x1: 880, y0: 1000, y1: 430, speed: 60, drift: 18, life: 6 });
|
|
182
|
+
for (const p of ps) { const tw = 0.5 + 0.5 * Math.sin(t * 6 + p.i * 1.7), a = 0.9 * tw * Math.sin(p.q * Math.PI);
|
|
183
|
+
const g = c.createRadialGradient(p.x, p.y, 0, p.x, p.y, 4 * p.s + 1); g.addColorStop(0, `rgba(255,240,200,${a})`); g.addColorStop(1, 'rgba(255,240,200,0)'); c.fillStyle = g; c.fillRect(p.x - 6, p.y - 6, 12, 12); }
|
|
184
|
+
c.restore();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ---------- 角色 ----------
|
|
188
|
+
const SK = '#e6bf9c', SK_D = '#b88866', HAIR = '#c49a4e', HAIR_L = '#e8c87e', HAIR_D = '#6e4a1c';
|
|
189
|
+
const VEL = '#2c4628', VEL_L = '#6e8c56', RED = '#7c1818', RED_L = '#b8302a', RED_D = '#3e0808', GOLD = '#c9a24a';
|
|
190
|
+
// 在 path 里画「沿骨骼的光带」:天鹅绒的高光在褶子顶上,最亮处偏白
|
|
191
|
+
const sheen = (c, path, A, B, col, w) => { c.save(); c.clip(path); c.strokeStyle = col; c.lineWidth = w; c.lineCap = 'round'; c.beginPath(); c.moveTo(...A); c.lineTo(...B); c.stroke(); c.restore(); };
|
|
192
|
+
function girl(c, G, lt, t, ch) {
|
|
193
|
+
const A = G.A;
|
|
194
|
+
c.save(); c.lineJoin = 'round'; c.lineCap = 'round';
|
|
195
|
+
// 长发(背后):中分,波浪垂到腰
|
|
196
|
+
const nape = A.nape, sw = Math.sin(t * 3) * 11;
|
|
197
|
+
const hair = RIG.smooth([[nape[0] - 40, nape[1] - 120], [nape[0] + 40, nape[1] - 110], [nape[0] + 66, nape[1] - 40], [nape[0] + 74, nape[1] + 30], [nape[0] + 92 + sw, nape[1] + 80], [nape[0] + 82 + sw, nape[1] + 130], [nape[0] + 98 + sw, nape[1] + 180], [nape[0] + 84 + sw, nape[1] + 228],
|
|
198
|
+
[nape[0] + 58 + sw, nape[1] + 236], [nape[0] + 36, nape[1] + 170], [nape[0] + 20, nape[1] + 90], [nape[0] - 10, nape[1] + 30], [nape[0] - 40, nape[1] - 20]]);
|
|
199
|
+
const hg = c.createLinearGradient(nape[0], nape[1] - 100, nape[0] + 80, nape[1] + 220); hg.addColorStop(0, HAIR_L); hg.addColorStop(0.5, HAIR); hg.addColorStop(1, HAIR_D);
|
|
200
|
+
c.fillStyle = hg; c.fill(hair);
|
|
201
|
+
c.save(); c.clip(hair); c.lineWidth = 3;
|
|
202
|
+
for (let k = 0; k < 9; k++) { c.strokeStyle = k % 2 ? 'rgba(255,226,150,.7)' : 'rgba(80,50,14,.65)'; c.beginPath(); for (let s = 0; s <= 24; s++) { const q = s / 24, x = nape[0] - 20 + k * 11 + q * 50 + Math.sin(q * 11 + k) * 7, y = nape[1] - 110 + q * 340; s ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); }
|
|
203
|
+
c.restore();
|
|
204
|
+
// 远侧袖与手
|
|
205
|
+
c.fillStyle = VEL; c.fill(G.farSleeve); c.fillStyle = GOLD; c.fill(G.farCuff); c.fillStyle = SK; c.fill(G.farHand);
|
|
206
|
+
// 红天鹅绒长裙:底色+褶上高光+褶间暗
|
|
207
|
+
const sg = c.createLinearGradient(1100, 600, 1450, 940); sg.addColorStop(0, RED_L); sg.addColorStop(0.5, RED); sg.addColorStop(1, RED_D);
|
|
208
|
+
c.fillStyle = sg; c.fill(G.skirt);
|
|
209
|
+
c.save(); c.clip(G.skirt);
|
|
210
|
+
const folds = [[1150, 700, 1120, 925, 22], [1200, 680, 1180, 930, 26], [1250, 660, 1250, 932, 20], [1300, 660, 1310, 930, 18], [1360, 700, 1380, 920, 16]];
|
|
211
|
+
folds.forEach(([x0, y0, x1, y1, w], k) => { c.strokeStyle = 'rgba(200,70,60,.3)'; c.lineWidth = w; c.beginPath(); c.moveTo(x0 - 6, y0 + 60); c.quadraticCurveTo((x0 + x1) / 2 - 10, (y0 + y1) / 2, x1, y1); c.stroke();
|
|
212
|
+
c.strokeStyle = 'rgba(40,0,0,.45)'; c.lineWidth = w * 0.7; c.beginPath(); c.moveTo(x0 + 26, y0 + 10); c.quadraticCurveTo((x0 + x1) / 2 + 18, (y0 + y1) / 2, x1 + 26, y1); c.stroke(); });
|
|
213
|
+
c.strokeStyle = GOLD; c.lineWidth = 5; c.beginPath(); c.moveTo(1100, 922); c.quadraticCurveTo(1230, 940, 1360, 922); c.stroke();
|
|
214
|
+
c.restore();
|
|
215
|
+
c.fillStyle = '#2a1608'; c.fill(G.shoe);
|
|
216
|
+
// 胸衣:红,方领金边
|
|
217
|
+
const tg = c.createLinearGradient(1280, 430, 1420, 620); tg.addColorStop(0, RED_L); tg.addColorStop(1, RED_D); c.fillStyle = tg; c.fill(G.torso);
|
|
218
|
+
sheen(c, G.torso, [1300, 470], [1300, 600], 'rgba(230,100,80,.35)', 18);
|
|
219
|
+
c.fillStyle = SK; c.fill(G.neck);
|
|
220
|
+
c.strokeStyle = GOLD; c.lineWidth = 5; c.beginPath(); c.moveTo(A.neck[0] - 16, A.neck[1] + 4); c.lineTo(A.neck[0] - 6, A.neck[1] + 34); c.lineTo(A.neck[0] + 34, A.neck[1] + 30); c.lineTo(A.neck[0] + 36, A.neck[1] + 2); c.stroke();
|
|
221
|
+
c.fillStyle = '#7a1414'; c.beginPath(); c.arc(A.neck[0] + 4, A.neck[1] + 24, 4, 0, 7); c.fill(); // 小垂饰
|
|
222
|
+
// 头发(头顶)+脸
|
|
223
|
+
c.fillStyle = HAIR; c.fill(G.hairBack);
|
|
224
|
+
const fg = c.createLinearGradient(G.A.forehead[0] - 10, 0, G.A.ear[0] + 10, 0); fg.addColorStop(0, '#f0d0b0'); fg.addColorStop(0.6, SK); fg.addColorStop(1, SK_D);
|
|
225
|
+
c.fillStyle = fg; c.fill(G.face);
|
|
226
|
+
c.save(); c.clip(G.face); c.fillStyle = 'rgba(220,120,100,.28)'; c.beginPath(); c.arc(G.cheek[0] - 2, G.cheek[1], 18, 0, 7); c.fill();
|
|
227
|
+
c.fillStyle = 'rgba(120,70,40,.22)'; c.beginPath(); c.ellipse(G.eye.x + 8, G.eye.y + 2, 14, 9, 0, 0, 7); c.fill(); c.restore(); // 眼窝阴影(晕涂)
|
|
228
|
+
c.fillStyle = SK_D; c.beginPath(); c.ellipse(G.ear[0], G.ear[1], 7, 11, 0.2, 0, 7); c.fill();
|
|
229
|
+
// 中分刘海:两片从发缝向后梳
|
|
230
|
+
c.fillStyle = HAIR; c.fill(G.bangs);
|
|
231
|
+
c.save(); c.clip(G.bangs); c.strokeStyle = 'rgba(250,225,160,.6)'; c.lineWidth = 2.5; G.hairLines.forEach(h => c.stroke(h)); c.restore();
|
|
232
|
+
c.strokeStyle = HAIR_D; c.lineWidth = 2; G.hairLines.forEach(h => c.stroke(h));
|
|
233
|
+
G.locks.forEach(h => { c.strokeStyle = HAIR_D; c.lineWidth = 8; c.stroke(h); c.strokeStyle = HAIR; c.lineWidth = 5; c.stroke(h); });
|
|
234
|
+
// 珍珠额链
|
|
235
|
+
const f0 = A.forehead, e0 = A.ear; for (let k = 0; k <= 12; k++) { const q = k / 12, x = lerp(f0[0] + 6, e0[0] + 4, q), y = lerp(f0[1] + 4, e0[1] - 28, q) - Math.sin(q * Math.PI) * 10; c.fillStyle = '#f4ecd8'; c.beginPath(); c.arc(x, y, 2.6, 0, 7); c.fill(); c.fillStyle = 'rgba(120,100,80,.6)'; c.beginPath(); c.arc(x + .8, y + .8, 1, 0, 7); c.fill(); }
|
|
236
|
+
// 五官:啜饮时闭眼
|
|
237
|
+
const closed = ch.blink || ch.cup > 0.7;
|
|
238
|
+
c.strokeStyle = '#4a2a18'; c.lineWidth = 2.2;
|
|
239
|
+
if (closed) { c.beginPath(); c.arc(G.eye.x + 1, G.eye.y - 3, 7, 0.35, Math.PI - 0.5); c.stroke(); c.lineWidth = 1.4; for (let k = 0; k < 3; k++) { c.beginPath(); c.moveTo(G.eye.x - 4 + k * 4, G.eye.y + 3); c.lineTo(G.eye.x - 6 + k * 4, G.eye.y + 7); c.stroke(); } }
|
|
240
|
+
else { c.fillStyle = '#f2e6d8'; c.beginPath(); c.ellipse(G.eye.x, G.eye.y, 7.5, 4.2, 0, 0, 7); c.fill(); c.fillStyle = '#4a3420'; c.beginPath(); c.arc(G.eye.x - 2.5, G.eye.y + 0.5, 3.2, 0, 7); c.fill(); c.stroke(G.lid); }
|
|
241
|
+
c.strokeStyle = 'rgba(120,80,40,.7)'; c.lineWidth = 1.8; c.stroke(G.brow);
|
|
242
|
+
c.fillStyle = '#a8423a'; c.beginPath(); c.moveTo(...G.lips[0]); c.lineTo(...G.lips[1]); c.lineTo(...G.lips[2]); c.closePath(); c.fill();
|
|
243
|
+
// 近侧手臂:绿天鹅绒袖(沿臂高光)+肩部金线珍珠+金袖口
|
|
244
|
+
for (const [p, a, b] of [[G.upperArm, A.shoulder, A.elbow], [G.foreArm, A.elbow, A.hand]]) {
|
|
245
|
+
const ag = c.createLinearGradient(a[0] - 30, a[1] - 30, a[0] + 30, a[1] + 30); ag.addColorStop(0, VEL_L); ag.addColorStop(1, VEL); c.fillStyle = ag; c.fill(p);
|
|
246
|
+
sheen(c, p, [lerp(a[0], b[0], 0.1) - 4, lerp(a[1], b[1], 0.1) - 6], [lerp(a[0], b[0], 0.85) - 4, lerp(a[1], b[1], 0.85) - 6], 'rgba(170,200,140,.45)', 10);
|
|
247
|
+
}
|
|
248
|
+
for (let k = 0; k < 4; k++) { const q = k / 3; c.fillStyle = '#f4ecd8'; c.beginPath(); c.arc(lerp(A.shoulder[0] - 22, A.shoulder[0] + 24, q), lerp(A.shoulder[1] - 8, A.shoulder[1] + 10, q), 3.4, 0, 7); c.fill(); }
|
|
249
|
+
c.fillStyle = GOLD; c.fill(G.cuff);
|
|
250
|
+
c.fillStyle = SK; c.fill(G.hand);
|
|
251
|
+
goblet(c, G.cup);
|
|
252
|
+
c.fillStyle = SK; c.beginPath(); c.arc(G.thumb[0], G.thumb[1], 6.5, 0, 7); c.fill();
|
|
253
|
+
c.restore();
|
|
254
|
+
}
|
|
255
|
+
function goblet(c, cup) { // 银高脚杯,红酒
|
|
256
|
+
c.save(); c.translate(cup.x, cup.y); c.rotate(cup.tilt || 0);
|
|
257
|
+
const sg = c.createLinearGradient(-24, 0, 24, 0); sg.addColorStop(0, '#6c6c70'); sg.addColorStop(0.3, '#f4f4f0'); sg.addColorStop(0.55, '#b4b4b6'); sg.addColorStop(1, '#4a4a50');
|
|
258
|
+
c.fillStyle = sg; c.beginPath(); c.moveTo(-23, 0); c.bezierCurveTo(-23, 22, -8, 32, 0, 32); c.bezierCurveTo(8, 32, 23, 22, 23, 0); c.closePath(); c.fill();
|
|
259
|
+
c.fillRect(-3, 30, 6, 24); c.beginPath(); c.ellipse(0, 42, 6, 3.5, 0, 0, 7); c.fill();
|
|
260
|
+
c.beginPath(); c.ellipse(0, 56, 17, 4.5, 0, 0, 7); c.fill();
|
|
261
|
+
c.fillStyle = '#5a0c14'; c.beginPath(); c.ellipse(0, 1, 21, 5, 0, 0, 7); c.fill();
|
|
262
|
+
c.fillStyle = 'rgba(255,255,255,.7)'; c.fillRect(-15, 6, 3, 14);
|
|
263
|
+
c.restore();
|
|
264
|
+
}
|
|
265
|
+
function cat(c, K, t, ch) {
|
|
266
|
+
const OR = '#d88a3e', OR_D = '#9a5520', OR_L = '#f2b46a', WH = '#f4ece0';
|
|
267
|
+
c.save(); c.lineCap = 'round'; c.lineJoin = 'round';
|
|
268
|
+
// 地面投影
|
|
269
|
+
c.fillStyle = 'rgba(20,10,4,.45)'; c.beginPath(); c.ellipse(545, 905, 150, 18, 0, 0, 7); c.fill();
|
|
270
|
+
// 尾巴:橘,环纹,白尖
|
|
271
|
+
c.strokeStyle = OR; c.lineWidth = K.tailW; c.stroke(K.tail);
|
|
272
|
+
const tp = K.tailPts; c.strokeStyle = OR_D; c.lineWidth = 5; for (let i = 2; i < tp.length - 2; i += 3) { const a = Math.atan2(tp[i + 1][1] - tp[i][1], tp[i + 1][0] - tp[i][0]) + Math.PI / 2; c.beginPath(); c.moveTo(tp[i][0] - Math.cos(a) * 11, tp[i][1] - Math.sin(a) * 11); c.lineTo(tp[i][0] + Math.cos(a) * 11, tp[i][1] + Math.sin(a) * 11); c.stroke(); }
|
|
273
|
+
const tip = tp[tp.length - 1]; c.fillStyle = WH; c.beginPath(); c.arc(tip[0], tip[1], K.tailW / 2, 0, 7); c.fill();
|
|
274
|
+
// 身体:径向体积
|
|
275
|
+
const body = RIG.smooth(K.bodyPts);
|
|
276
|
+
// 白底(胸腹受光)→ 背上橘色毛被用大半径径向渐变「晕」进来,没有硬边界
|
|
277
|
+
const bg = c.createLinearGradient(430, 0, 660, 0); bg.addColorStop(0, '#e8dccb'); bg.addColorStop(0.55, WH); bg.addColorStop(1, '#a89478');
|
|
278
|
+
c.fillStyle = bg; c.fill(body);
|
|
279
|
+
c.save(); c.clip(body);
|
|
280
|
+
const coat = c.createRadialGradient(455, 760, 30, 470, 770, 170); coat.addColorStop(0, OR); coat.addColorStop(0.55, 'rgba(216,138,62,.92)'); coat.addColorStop(1, 'rgba(216,138,62,0)');
|
|
281
|
+
c.fillStyle = coat; c.fillRect(380, 640, 300, 280);
|
|
282
|
+
const coat2 = c.createRadialGradient(520, 690, 10, 520, 690, 90); coat2.addColorStop(0, OR); coat2.addColorStop(1, 'rgba(216,138,62,0)'); c.fillStyle = coat2; c.fillRect(420, 600, 200, 200);
|
|
283
|
+
// 虎斑:只在橘毛区,软边
|
|
284
|
+
c.strokeStyle = 'rgba(150,75,20,.38)';
|
|
285
|
+
for (let k = 0; k < 7; k++) { const y = 706 + k * 27 + Math.sin(k * 2.7) * 6; c.lineWidth = 5 + (k % 3) * 2; c.beginPath(); c.moveTo(420, y + 14); c.quadraticCurveTo(450 + k * 3, y - 10, 488 + (k % 2) * 20, y + 16 - k); c.stroke(); }
|
|
286
|
+
// 腹侧阴影
|
|
287
|
+
const sh = c.createLinearGradient(0, 760, 0, 905); sh.addColorStop(0, 'rgba(80,40,10,0)'); sh.addColorStop(1, 'rgba(80,40,10,.35)'); c.fillStyle = sh; c.fillRect(400, 760, 280, 150);
|
|
288
|
+
// 毛:短线(受光侧亮、背光侧暗)
|
|
289
|
+
const r = R_(3);
|
|
290
|
+
for (let k = 0; k < 420; k++) { const x = 425 + r() * 240, y = 670 + r() * 230, a = Math.PI / 2 + (x - 540) * 0.004 + (r() - .5) * 0.5, L = 6 + r() * 8;
|
|
291
|
+
c.strokeStyle = r() < 0.5 ? 'rgba(255,220,170,.45)' : 'rgba(110,55,15,.35)'; c.lineWidth = 1.4; c.beginPath(); c.moveTo(x, y); c.lineTo(x + Math.cos(a) * L, y + Math.sin(a) * L); c.stroke(); }
|
|
292
|
+
c.strokeStyle = 'rgba(90,50,15,.22)'; c.lineWidth = 14; c.stroke(body); // 轮廓内侧轻压暗=体积(太重会像玻璃罩)
|
|
293
|
+
c.restore();
|
|
294
|
+
K.legs.forEach(l => { const lg = c.createLinearGradient(570, 0, 650, 0); lg.addColorStop(0, '#fbf6ec'); lg.addColorStop(1, '#c8bcaa'); c.fillStyle = lg; c.fill(l); });
|
|
295
|
+
c.fillStyle = 'rgba(120,90,60,.25)'; K.legs.forEach(l => { c.save(); c.clip(l); c.fillRect(600, 740, 60, 160); c.restore(); });
|
|
296
|
+
for (const x of [585, 635]) { const pg = c.createRadialGradient(x - 4, 886, 2, x, 890, 22); pg.addColorStop(0, '#fffaf0'); pg.addColorStop(1, '#cfc2ae'); c.fillStyle = pg; c.beginPath(); c.ellipse(x, 892, 21, 12, 0, 0, 7); c.fill(); }
|
|
297
|
+
// 头:绕脖子左右歪头蹭(眯眼享受),幅度 0.1rad
|
|
298
|
+
const pv = [K.headC[0] - 10, K.headC[1] + 58]; c.save(); c.translate(...pv); c.rotate(0.16 * Math.sin(t * 3.4) + 0.05 * Math.sin(t * 8.1)); c.translate(-pv[0], -pv[1]);
|
|
299
|
+
const hc = K.headC, hg = c.createRadialGradient(hc[0] - 10, hc[1] - 20, 8, hc[0], hc[1], 80); hg.addColorStop(0, OR_L); hg.addColorStop(0.6, OR); hg.addColorStop(1, OR_D);
|
|
300
|
+
c.fillStyle = hg; c.fill(K.head);
|
|
301
|
+
c.fillStyle = 'rgba(230,150,140,.8)'; K.earInner.forEach(e => { c.beginPath(); e.forEach((p, i) => i ? c.lineTo(...p) : c.moveTo(...p)); c.fill(); });
|
|
302
|
+
c.save(); c.clip(K.head);
|
|
303
|
+
c.strokeStyle = 'rgba(140,70,20,.6)'; c.lineWidth = 6; K.stripes.slice(0, 3).forEach(s => { c.beginPath(); c.moveTo(...s[0]); c.lineTo(...s[1]); c.stroke(); });
|
|
304
|
+
const mg = c.createRadialGradient(K.nose[0] - 20, K.nose[1] + 10, 4, K.nose[0] - 20, K.nose[1] + 10, 46); mg.addColorStop(0, WH); mg.addColorStop(0.7, 'rgba(244,236,224,.9)'); mg.addColorStop(1, 'rgba(244,236,224,0)');
|
|
305
|
+
c.fillStyle = mg; c.beginPath(); c.arc(K.nose[0] - 20, K.nose[1] + 10, 46, 0, 7); c.fill();
|
|
306
|
+
c.strokeStyle = 'rgba(80,35,8,.4)'; c.lineWidth = 18; c.stroke(K.head);
|
|
307
|
+
c.restore();
|
|
308
|
+
// 眯眼微笑
|
|
309
|
+
c.strokeStyle = '#3a2210'; c.lineWidth = 3;
|
|
310
|
+
const sq = 0.5 + 0.5 * Math.sin(t * 4.5); // 眯得更紧/稍松,交替
|
|
311
|
+
K.eyes.forEach(e => { c.beginPath(); c.arc(e.x, e.y + 4 + sq * 2, 9, Math.PI * (1.15 + sq * 0.08), Math.PI * (1.85 - sq * 0.08)); c.stroke(); });
|
|
312
|
+
c.fillStyle = '#d07a70'; c.beginPath(); c.moveTo(K.nose[0] - 5, K.nose[1] - 3); c.lineTo(K.nose[0] + 5, K.nose[1] - 3); c.lineTo(K.nose[0], K.nose[1] + 3); c.closePath(); c.fill();
|
|
313
|
+
c.strokeStyle = '#4a2a18'; c.lineWidth = 2.2; c.beginPath(); c.moveTo(K.nose[0], K.nose[1] + 3); c.lineTo(K.nose[0], K.nose[1] + 8);
|
|
314
|
+
c.moveTo(K.nose[0] - 12, K.nose[1] + 8); c.quadraticCurveTo(K.nose[0] - 6, K.nose[1] + 15, K.nose[0], K.nose[1] + 8); c.quadraticCurveTo(K.nose[0] + 6, K.nose[1] + 15, K.nose[0] + 12, K.nose[1] + 7); c.stroke();
|
|
315
|
+
c.strokeStyle = 'rgba(250,245,235,.85)'; c.lineWidth = 1.3; K.whiskers.forEach(w => { c.beginPath(); c.moveTo(...w[0]); c.lineTo(...w[1]); c.stroke(); });
|
|
316
|
+
c.restore();
|
|
317
|
+
c.restore();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
init() { STATIC(); OVER(); },
|
|
322
|
+
draw(c, lt, t) {
|
|
323
|
+
const ch = P.choreo(lt, t);
|
|
324
|
+
c.drawImage(STATIC(), 0, 0);
|
|
325
|
+
riverGlints(c, t); birds(c, lt, t); ornithopter(c, lt, t);
|
|
326
|
+
// 角色:离屏 → 模糊层+清晰层叠加=晕涂
|
|
327
|
+
const L = P.scratch('renChars'), g = L.getContext('2d'); g.clearRect(320, 180, 1200, 780);
|
|
328
|
+
const K = RIG.cat({ tail: ch.tail * 2.2 + Math.sin(t * 2.6) * 0.35, blink: 0, breathe: ch.breathe * 3, look: Math.sin(t * 2.5) * 1.6 });
|
|
329
|
+
const G = RIG.girl({ cup: ch.cup, sip: ch.sip * 1.6, hair: 'long', breathe: ch.breathe * 2.5 });
|
|
330
|
+
g.fillStyle = 'rgba(15,8,4,.35)'; g.beginPath(); g.ellipse(1240, 935, 170, 16, 0, 0, 7); g.fill();
|
|
331
|
+
cat(g, K, t, ch); girl(g, G, lt, t, ch);
|
|
332
|
+
c.filter = 'blur(1.6px)'; c.drawImage(L, 320, 180, 1200, 780, 320, 180, 1200, 780); c.filter = 'none';
|
|
333
|
+
c.globalAlpha = 0.82; c.drawImage(L, 320, 180, 1200, 780, 320, 180, 1200, 780); c.globalAlpha = 1;
|
|
334
|
+
beamAndDust(c, lt, t);
|
|
335
|
+
c.drawImage(OVER(), 0, 0);
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
})();
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// 1874 印象派(莫奈)——纯代码。
|
|
2
|
+
// 管线:①平涂底稿(粉紫墙+右上斜射阳光、窗外花园:天/树/红顶屋/草地/摇曳的罂粟/撑伞女子、墙上《日出·印象》、圆桌、花瓶、右侧盆栽)
|
|
3
|
+
// → ②P.strokes 短笔触重画(无描边、区域粉彩色板、方向场:墙斜向右上、地面横、植物放射;8fps 沸腾=整幅画在呼吸)
|
|
4
|
+
// → ③角色平涂(白裙蓝腰带、系丝带草帽、金发;橘白猫)→ 同一渲染器细笔触重画 → 只给五官补极淡的色线
|
|
5
|
+
// → ④光斑:地面/桌面的阳光斑块按各自节奏明灭跳动;猫背上的毛尖闪光;花瓣色点
|
|
6
|
+
SCENES['08_impressionism'] = (() => {
|
|
7
|
+
const W = 1920, H = 1080, { clamp, lerp } = U, P = PAINT;
|
|
8
|
+
const poly = (pts) => { const p = new Path2D(); pts.forEach((q, i) => i ? p.lineTo(q[0], q[1]) : p.moveTo(q[0], q[1])); p.closePath(); return p; };
|
|
9
|
+
const WX0 = 372, WX1 = 790, WY0 = 132, WY1 = 548; // 窗洞
|
|
10
|
+
const POPPIES = (() => { const r = U.rng(8), o = []; for (let i = 0; i < 70; i++) { const x = 380 + r() * 200 + (r() < 0.25 ? r() * 200 : 0), y = 420 + r() * 125; o.push([x, y, 5 + r() * 6 + (y - 420) * 0.05, r() * 7]); } return o; })();
|
|
11
|
+
|
|
12
|
+
function base(g, t) {
|
|
13
|
+
// 墙:粉紫,右上方暖黄阳光
|
|
14
|
+
const wg = g.createLinearGradient(0, 0, W, 300); wg.addColorStop(0, '#d8bede'); wg.addColorStop(0.5, '#f0c8cc'); wg.addColorStop(1, '#f2c4d4');
|
|
15
|
+
g.fillStyle = wg; g.fillRect(0, 0, W, 790);
|
|
16
|
+
const sun = g.createRadialGradient(1400, -100, 50, 1400, -100, 900); sun.addColorStop(0, 'rgba(255,240,170,.85)'); sun.addColorStop(1, 'rgba(255,240,170,0)'); g.fillStyle = sun; g.fillRect(700, 0, 1220, 790);
|
|
17
|
+
// 地板
|
|
18
|
+
const fg = g.createLinearGradient(0, 780, 0, H); fg.addColorStop(0, '#d9a8b0'); fg.addColorStop(1, '#d88a80'); g.fillStyle = fg; g.fillRect(0, 780, W, 300);
|
|
19
|
+
g.fillStyle = '#b8a8dc'; g.fillRect(0, 768, W, 18);
|
|
20
|
+
// 窗外花园
|
|
21
|
+
g.save(); g.beginPath(); g.rect(WX0, WY0, WX1 - WX0, WY1 - WY0); g.clip();
|
|
22
|
+
const sk = g.createLinearGradient(0, WY0, 0, 300); sk.addColorStop(0, '#7aa6e2'); sk.addColorStop(1, '#bcd4f0'); g.fillStyle = sk; g.fillRect(WX0, WY0, 420, 180);
|
|
23
|
+
g.fillStyle = '#f6f6ff'; for (const [x, y, rx] of [[450, 175, 50], [520, 165, 40], [640, 190, 60], [720, 160, 40]]) { g.beginPath(); g.ellipse(WX0 - 60 + ((t * 45 + x - WX0 + 60) % 540), y, rx, rx * 0.35, 0, 0, 7); g.fill(); }
|
|
24
|
+
g.fillStyle = '#3e8a46'; g.beginPath(); g.moveTo(WX0, 360); for (let x = WX0; x <= WX1; x += 10) g.lineTo(x, 330 - Math.abs(P.noise(x * 0.02, 1)) * 60 - (x > 600 && x < 720 ? 60 * Math.sin((x - 600) / 120 * Math.PI) : 0)); g.lineTo(WX1, 360); g.closePath(); g.fill();
|
|
25
|
+
g.fillStyle = '#f0e6d0'; g.fillRect(455, 305, 50, 40); g.fillStyle = '#d0402a'; g.beginPath(); g.moveTo(448, 308); g.lineTo(480, 285); g.lineTo(512, 308); g.closePath(); g.fill();
|
|
26
|
+
const mg = g.createLinearGradient(0, 350, 0, WY1); mg.addColorStop(0, '#9ccc5a'); mg.addColorStop(1, '#6aa840'); g.fillStyle = mg; g.fillRect(WX0, 350, 420, 200);
|
|
27
|
+
// 罂粟:随风摇(茎底不动、花头横摆)
|
|
28
|
+
for (const [x, y, r, ph] of POPPIES) { const sw = Math.sin(t * 3.6 + ph + x * 0.02) * (10 + r * 0.8), hx = x + sw, hy = y - r * 0.6;
|
|
29
|
+
g.strokeStyle = '#3e7a30'; g.lineWidth = 2; g.beginPath(); g.moveTo(x, y + r * 1.4); g.quadraticCurveTo(x + sw * 0.3, y, hx, hy); g.stroke();
|
|
30
|
+
g.fillStyle = '#e8281e'; g.beginPath(); g.ellipse(hx, hy, r, r * 0.75, sw * 0.03, 0, 7); g.fill(); }
|
|
31
|
+
// 撑阳伞的女子+孩子(《罂粟田》)
|
|
32
|
+
const wx = 545, wy = 470; g.fillStyle = '#2a3a6a'; g.beginPath(); g.moveTo(wx - 12, wy + 50); g.lineTo(wx + 14, wy + 50); g.lineTo(wx + 5, wy); g.lineTo(wx - 5, wy); g.closePath(); g.fill();
|
|
33
|
+
g.fillStyle = '#f6f0e8'; g.beginPath(); g.arc(wx, wy - 6, 6, 0, 7); g.fill();
|
|
34
|
+
g.fillStyle = '#4aa0c8'; g.beginPath(); g.ellipse(wx - 4, wy - 22, 26, 12, -0.3, Math.PI, 0); g.fill(); g.strokeStyle = '#2a3a6a'; g.lineWidth = 2; g.beginPath(); g.moveTo(wx - 4, wy - 22); g.lineTo(wx + 2, wy + 8); g.stroke();
|
|
35
|
+
g.fillStyle = '#f6f0e8'; g.fillRect(505, 492, 10, 22); g.fillStyle = '#e8b040'; g.beginPath(); g.arc(510, 488, 6, 0, 7); g.fill();
|
|
36
|
+
// 花丛
|
|
37
|
+
g.fillStyle = '#5a9a40'; g.beginPath(); g.ellipse(735, 505, 44, 34, 0, 0, 7); g.fill();
|
|
38
|
+
for (let k = 0; k < 14; k++) { g.fillStyle = k % 2 ? '#f4a0c0' : '#fff0f4'; g.beginPath(); g.arc(710 + (k * 37 % 56), 485 + (k * 23 % 40), 6, 0, 7); g.fill(); }
|
|
39
|
+
g.fillStyle = '#c87a40'; g.fillRect(722, 530, 26, 20);
|
|
40
|
+
g.restore();
|
|
41
|
+
// 窗框(白)+窗格+窗帘杆+淡紫窗帘
|
|
42
|
+
g.strokeStyle = '#fbf6ee'; g.lineWidth = 18; g.strokeRect(WX0 - 9, WY0 - 9, WX1 - WX0 + 18, WY1 - WY0 + 18);
|
|
43
|
+
g.lineWidth = 9; g.beginPath(); g.moveTo(581, WY0); g.lineTo(581, WY1); g.moveTo(WX0, 270); g.lineTo(WX1, 270); g.moveTo(WX0, 410); g.lineTo(WX1, 410); g.stroke();
|
|
44
|
+
g.fillStyle = '#fbf6ee'; g.fillRect(345, 560, 470, 20);
|
|
45
|
+
g.fillStyle = '#d8b040'; g.fillRect(270, 92, 640, 9);
|
|
46
|
+
for (const [x0, x1] of [[292, 352], [812, 880]]) { const cg = g.createLinearGradient(x0, 0, x1, 0); cg.addColorStop(0, '#c4baf2'); cg.addColorStop(0.5, '#e6e0fc'); cg.addColorStop(1, '#a89ee6'); g.fillStyle = cg; g.beginPath(); g.moveTo(x0, 98); g.lineTo(x1, 98); g.quadraticCurveTo(x1 + 18 * Math.sin(t * 2.2), 380, x1 + 4 + 22 * Math.sin(t * 2.2 - 0.6), 620); g.lineTo(x0 - 4 + 22 * Math.sin(t * 2.2 - 0.6), 620); g.closePath(); g.fill(); }
|
|
47
|
+
// 《日出·印象》
|
|
48
|
+
g.fillStyle = '#e0b850'; g.fillRect(938, 192, 250, 180);
|
|
49
|
+
const sg = g.createLinearGradient(0, 205, 0, 360); sg.addColorStop(0, '#9aa8c8'); sg.addColorStop(1, '#5a78a8'); g.fillStyle = sg; g.fillRect(950, 204, 226, 156);
|
|
50
|
+
g.fillStyle = '#f05a28'; g.beginPath(); g.arc(1092, 246, 11, 0, 7); g.fill();
|
|
51
|
+
for (let k = 0; k < 5; k++) { g.fillRect(1078 + (k % 2) * 6, 270 + k * 16, 26 - k * 3, 6); }
|
|
52
|
+
g.fillStyle = '#1e2a4a'; g.beginPath(); g.moveTo(1018, 318); g.lineTo(1062, 318); g.lineTo(1056, 328); g.lineTo(1024, 328); g.closePath(); g.fill(); g.fillRect(1038, 300, 3, 18);
|
|
53
|
+
// 椅背
|
|
54
|
+
g.fillStyle = '#8a5a40'; g.fillRect(1476, 470, 14, 420);
|
|
55
|
+
// 圆桌:桌腿、桌布、花瓶、茶壶
|
|
56
|
+
g.fillStyle = '#5a4a6a'; g.fillRect(992, 790, 16, 90); g.beginPath(); g.moveTo(940, 892); g.lineTo(1000, 868); g.lineTo(1060, 892); g.lineWidth = 8; g.strokeStyle = '#5a4a6a'; g.stroke();
|
|
57
|
+
const cl = g.createLinearGradient(840, 0, 1200, 0); cl.addColorStop(0, '#f2eefc'); cl.addColorStop(0.6, '#d8d0f4'); cl.addColorStop(1, '#b8aee8');
|
|
58
|
+
g.fillStyle = cl; g.beginPath(); g.moveTo(842, 632); g.lineTo(1198, 632); g.lineTo(1206, 800); g.quadraticCurveTo(1020, 815, 836, 800); g.closePath(); g.fill();
|
|
59
|
+
g.fillStyle = '#faf6ff'; g.beginPath(); g.ellipse(1020, 632, 180, 18, 0, 0, 7); g.fill();
|
|
60
|
+
g.fillStyle = '#a8d0e8'; g.beginPath(); g.moveTo(860, 630); g.quadraticCurveTo(850, 580, 875, 560); g.lineTo(905, 560); g.quadraticCurveTo(925, 590, 912, 630); g.closePath(); g.fill();
|
|
61
|
+
for (let k = 0; k < 22; k++) { const a = k / 22 * Math.PI * 2, rr = 22 + (k * 13 % 30), sw = Math.sin(t * 2.5 + k) * 3; g.fillStyle = ['#f06a8a', '#ffffff', '#c88af0', '#f8c040', '#ff9ab0'][k % 5]; g.beginPath(); g.arc(888 + Math.cos(a) * rr + sw, 525 + Math.sin(a) * rr * 0.8, 9, 0, 7); g.fill(); }
|
|
62
|
+
g.fillStyle = '#5a9a50'; for (let k = 0; k < 6; k++) { g.beginPath(); g.ellipse(870 + k * 8, 556, 5, 14, -0.6 + k * 0.25, 0, 7); g.fill(); }
|
|
63
|
+
g.fillStyle = '#f4f4fc'; g.beginPath(); g.moveTo(962, 630); g.lineTo(966, 578); g.quadraticCurveTo(992, 566, 1016, 578); g.lineTo(1020, 630); g.closePath(); g.fill(); g.fillStyle = '#7a8ac8'; g.fillRect(964, 600, 54, 8);
|
|
64
|
+
g.fillStyle = '#c89878'; g.beginPath(); g.ellipse(991, 574, 24, 7, 0, 0, 7); g.fill();
|
|
65
|
+
// 右侧盆栽:一大团绿+粉花,叶团随风摆
|
|
66
|
+
const sw = Math.sin(t * 2.8) * 24;
|
|
67
|
+
g.fillStyle = '#4fa050'; g.beginPath(); g.moveTo(1560, 780); g.bezierCurveTo(1500 + sw * 0.5, 560, 1600 + sw, 330, 1710 + sw, 300); g.bezierCurveTo(1830 + sw, 330, 1930 + sw * 0.5, 560, 1880, 780); g.closePath(); g.fill();
|
|
68
|
+
g.fillStyle = '#8acc60'; g.beginPath(); g.ellipse(1680 + sw, 480, 90, 140, -0.3, 0, 7); g.fill();
|
|
69
|
+
for (let k = 0; k < 18; k++) { const x = 1590 + (k * 71 % 270) + sw * (1 - (k * 37 % 400) / 500), y = 380 + (k * 53 % 380); g.fillStyle = k % 3 ? '#f070a8' : '#ffd0e4'; g.beginPath(); g.arc(x, y, 9, 0, 7); g.fill(); }
|
|
70
|
+
g.fillStyle = '#f2f4fc'; g.beginPath(); g.moveTo(1650, 780); g.lineTo(1786, 780); g.lineTo(1770, 900); g.lineTo(1666, 900); g.closePath(); g.fill();
|
|
71
|
+
g.fillStyle = '#4a6ad0'; for (let k = 0; k < 6; k++) g.fillRect(1664 + k * 20, 806 + (k % 2) * 30, 12, 20);
|
|
72
|
+
// 地面阴影(猫、桌、少女)
|
|
73
|
+
g.fillStyle = 'rgba(150,100,170,.5)'; g.beginPath(); g.ellipse(540, 905, 150, 18, 0, 0, 7); g.fill(); g.beginPath(); g.ellipse(1010, 892, 150, 14, 0, 0, 7); g.fill(); g.beginPath(); g.ellipse(1300, 930, 210, 18, 0, 0, 7); g.fill();
|
|
74
|
+
}
|
|
75
|
+
const inWin = (x, y) => x > WX0 && x < WX1 && y > WY0 && y < WY1;
|
|
76
|
+
const angle = (x, y, t) => {
|
|
77
|
+
if (x > 945 && x < 1182 && y > 200 && y < 365) return 0.03 * Math.sin(y * 0.3); // 《日出》:横向水波笔
|
|
78
|
+
if (inWin(x, y)) return y < 300 ? 0.05 * Math.sin(x * 0.05) : -0.5 + 0.6 * P.noise(x * 0.03, y * 0.03) + 0.22 * Math.sin(t * 3.2 + x * 0.02); // 花草的笔随风轻转
|
|
79
|
+
if (x > 1520 && y < 790 && y > 280) return Math.atan2(y - 800, x - 1715) + 0.3 * P.noise(x * 0.02, y * 0.02) + 0.12 * Math.sin(t * 1.9); // 盆栽:从盆口放射
|
|
80
|
+
if (y > 780) return 0.12 * P.noise(x * 0.004, y * 0.01) - 0.05;
|
|
81
|
+
if ((x > 280 && x < 360) || (x > 805 && x < 890)) return -Math.PI / 2 + 0.15 * P.noise(x * 0.02, y * 0.01);
|
|
82
|
+
if (x > 830 && x < 1210 && y > 620 && y < 810) return -Math.PI / 2 + 0.2;
|
|
83
|
+
const sunny = x > 1000 && y < 500;
|
|
84
|
+
return (sunny ? -0.95 : -0.55) + 0.55 * P.noise(x * 0.006, y * 0.006 + 5); // 墙:向右上斜,莫奈式短促
|
|
85
|
+
};
|
|
86
|
+
const SW = {
|
|
87
|
+
wall: P.swatch(['#f3c6d2', '#e8b6dc', '#f6dcc0', '#d8c0ec', '#fbe8b0', '#f0a8c0', '#c8b8f0', '#fff2d8', '#e8c8f0'], 0.35),
|
|
88
|
+
sunny: P.swatch(['#fff0b0', '#fbe08a', '#ffe8c8', '#f8d0d8', '#fff8e0', '#f0c0d8', '#e8d0f8'], 0.3),
|
|
89
|
+
floor: P.swatch(['#e8a090', '#d88aa0', '#f0b890', '#c890b8', '#f6c8a0', '#b0a0d8', '#e89880'], 0.4),
|
|
90
|
+
sky: P.swatch(['#8ab4e8', '#a8c8f0', '#f0f4ff', '#7aa0e0', '#c0d8f8'], 0.4),
|
|
91
|
+
green: P.swatch(['#5aa84a', '#8ac860', '#3a8a4a', '#b8d870', '#2f7a50', '#a0e080'], 0.4),
|
|
92
|
+
lav: P.swatch(['#b8b0f0', '#d0c8f8', '#9890e0', '#e8e0ff', '#ffffff'], 0.35),
|
|
93
|
+
cloth: P.swatch(['#ece8fa', '#b8b0ec', '#f8f4ff', '#a49ce0', '#fff8f0', '#9a96d8'], 0.45),
|
|
94
|
+
};
|
|
95
|
+
const palette = (x, y, col, r) => {
|
|
96
|
+
if (inWin(x, y)) { const red = col[0] > 180 && col[1] < 90; if (red || (x < 600 && y > 440 && y < 520)) return null; return y < 300 && col[2] > col[1] ? SW.sky(col, r) : (col[1] > col[0] ? SW.green(col, r) : null); }
|
|
97
|
+
if (x > 935 && x < 1192 && y > 188 && y < 376) return null; // 墙上的画保留
|
|
98
|
+
if (x > 1520 && y > 290 && y < 785) return col[1] > col[0] ? SW.green(col, r) : null;
|
|
99
|
+
if (x > 1640 && y >= 780 && y < 905) return null;
|
|
100
|
+
if (x > 835 && x < 935 && y > 490 && y < 640) return null; // 花瓶花束
|
|
101
|
+
if (col[0] > 240 && col[1] > 236 && col[2] > 225 && x > 330 && x < 830 && y > 115 && y < 585) return P.mix([255, 252, 246], col, 0.3); // 白窗框
|
|
102
|
+
if ((x > 280 && x < 360 || x > 800 && x < 890) && y > 90 && y < 625) return SW.lav(col, r);
|
|
103
|
+
if (x > 830 && x < 1212 && y > 612 && y < 815) return SW.cloth(col, r);
|
|
104
|
+
if (y > 780) return SW.floor(col, r);
|
|
105
|
+
if (x > 1000 && y < 520) return SW.sunny(col, r);
|
|
106
|
+
return SW.wall(col, r);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// 阳光斑:每个斑块是一簇亮黄白短笔,强度按自己的相位明灭,位置缓慢漂移
|
|
110
|
+
const SPOTS = (() => { const r = U.rng(5), o = []; for (let i = 0; i < 34; i++) { const onTable = i < 7; o.push({ x: onTable ? 880 + r() * 300 : 380 + r() * 1150, y: onTable ? 632 + r() * 10 : 830 + r() * 220, rx: onTable ? 50 + r() * 50 : 70 + r() * 110, ry: onTable ? 7 : 12 + r() * 18, ph: r() * 7, sp: 5 + r() * 6, seed: i }); } return o; })();
|
|
111
|
+
function sunSpots(c, t) {
|
|
112
|
+
c.save(); c.lineCap = 'round';
|
|
113
|
+
for (const s of SPOTS) {
|
|
114
|
+
const k = 0.5 + 0.5 * Math.sin(t * s.sp + s.ph) * Math.sin(t * s.sp * 0.53 + s.ph * 2), a = clamp(k * 1.3 - 0.15);
|
|
115
|
+
if (a <= 0.02) continue;
|
|
116
|
+
const r = U.rng(s.seed * 31), dx = Math.sin(t * 2.6 + s.ph) * 44, dy = Math.cos(t * 2.0 + s.ph) * 8; // 种子固定:斑块里每一笔不换位置,只整体漂移+明灭
|
|
117
|
+
for (let i = 0; i < 40; i++) { const u = r() * 2 - 1, v = r() * 2 - 1; if (u * u + v * v > 1) continue;
|
|
118
|
+
const x = s.x + dx + u * s.rx, y = s.y + dy + v * s.ry, L = 14 + r() * 16;
|
|
119
|
+
c.strokeStyle = `rgba(255,${244 + r() * 11 | 0},${180 + r() * 50 | 0},${0.95 * a})`; c.lineWidth = 8 + r() * 5;
|
|
120
|
+
c.beginPath(); c.moveTo(x - L / 2, y + r() * 2); c.lineTo(x + L / 2, y - r() * 3); c.stroke(); }
|
|
121
|
+
}
|
|
122
|
+
c.restore();
|
|
123
|
+
}
|
|
124
|
+
// 斜射光线中的亮点(右上墙面)
|
|
125
|
+
function wallGlints(c, t) {
|
|
126
|
+
c.save(); c.globalCompositeOperation = 'screen'; c.lineCap = 'round'; const r = U.rng(900);
|
|
127
|
+
for (let i = 0; i < 60; i++) { const x = 1050 + r() * 800, y = 20 + r() * 450, a = Math.max(0, Math.sin(t * 5 + i)); if (a < 0.3) continue;
|
|
128
|
+
c.strokeStyle = `rgba(255,248,200,${0.5 * a})`; c.lineWidth = 6; c.beginPath(); c.moveTo(x, y); c.lineTo(x + 14, y - 18); c.stroke(); }
|
|
129
|
+
c.restore();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const girlPal = { skin: '#f8d8c8', hair: '#f0cc6a', dress: '#f6f4fc', sleeve: '#f6f4fc', cuff: '#f6f4fc', hat: '#f0d890', hatBand: '#4a6ad8', fold: '#c8c0ec', cheek: 'rgba(240,130,130,.5)', lip: '#e0606a', iris: '#4a70c0', cupBody: '#f4f4fc', cupRim: '#b07850', shoe: '#c8b8e8', line: '#d6a84e', lw: 0 };
|
|
133
|
+
const catPal = { orange: '#f09a40', white: '#fcf4e8', stripe: '#d0702a', eye: '#78c060', line: '#b0602a', whisker: 'rgba(255,255,255,.7)', lw: 0 };
|
|
134
|
+
function girlExtras(g, G, t) {
|
|
135
|
+
// 长卷发(背后)
|
|
136
|
+
const n = G.A.nape, sw = Math.sin(t * 2.3) * 3;
|
|
137
|
+
g.fillStyle = '#ecc460'; g.beginPath(); g.moveTo(n[0] - 20, n[1] - 60); g.bezierCurveTo(n[0] + 60, n[1] - 40, n[0] + 70 + sw, n[1] + 80, n[0] + 66 + sw, n[1] + 170); g.bezierCurveTo(n[0] + 40, n[1] + 190, n[0] + 20, n[1] + 120, n[0] - 10, n[1] + 40); g.closePath(); g.fill();
|
|
138
|
+
}
|
|
139
|
+
function girlTop(g, G, t) {
|
|
140
|
+
// 蓝腰带+飘带、帽上小花、帽带飘动
|
|
141
|
+
const w = G.A.waist; g.fillStyle = '#4a6ad8'; g.beginPath(); g.moveTo(w[0] - 70, w[1] - 20); g.lineTo(w[0] + 72, w[1] - 22); g.lineTo(w[0] + 72, w[1] + 2); g.lineTo(w[0] - 64, w[1] + 4); g.closePath(); g.fill();
|
|
142
|
+
const fl = Math.sin(t * 6) * 6; g.beginPath(); g.moveTo(w[0] + 66, w[1] - 10); g.quadraticCurveTo(w[0] + 100 + fl, w[1] + 60, w[0] + 86 + fl, w[1] + 140); g.lineTo(w[0] + 72 + fl, w[1] + 136); g.quadraticCurveTo(w[0] + 82, w[1] + 60, w[0] + 58, w[1]); g.closePath(); g.fill();
|
|
143
|
+
if (G.bow) { const b = G.bow, rf = Math.sin(t * 7) * 8; g.fillStyle = '#4a6ad8'; g.beginPath(); g.moveTo(b[0], b[1]); g.quadraticCurveTo(b[0] + 30, b[1] + 40 + rf, b[0] + 20 + rf, b[1] + 110); g.lineTo(b[0] + 8 + rf, b[1] + 106); g.quadraticCurveTo(b[0] + 14, b[1] + 40, b[0] - 6, b[1] + 4); g.closePath(); g.fill(); }
|
|
144
|
+
const top = G.A.headTop; [['#f06a8a', -40, 2], ['#ffffff', -22, -2], ['#f8b040', -4, 4], ['#c88af0', 14, 0], ['#f06a8a', 30, 6]].forEach(([col, dx, dy]) => { g.fillStyle = col; g.beginPath(); g.arc(top[0] + dx + 20, top[1] + dy - 2, 8, 0, 7); g.fill(); });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function charStrokes(g, src, t) {
|
|
148
|
+
const x0 = 330, y0 = 190, w = 1200, h = 780, cell = 7, sd = src.getContext('2d', { willReadFrequently: true }).getImageData(x0, y0, w, h).data;
|
|
149
|
+
g.save(); g.globalCompositeOperation = 'source-atop'; g.lineCap = 'round'; g.lineWidth = 5;
|
|
150
|
+
for (let j = 0; j < h / cell; j++) for (let i = 0; i < w / cell; i++) {
|
|
151
|
+
const hx = U.hash(i, j), hy = U.hash(j + 7919, i), px = (i + hx) * cell, py = (j + hy) * cell, k = ((py | 0) * w + (px | 0)) * 4;
|
|
152
|
+
if (sd[k + 3] < 200) continue;
|
|
153
|
+
const X = x0 + px, Y = y0 + py, h3 = U.hash(i + 31, j + 17), h4 = U.hash(i + 97, j + 53);
|
|
154
|
+
let col = [sd[k], sd[k + 1], sd[k + 2]];
|
|
155
|
+
if (X > 1080 && col[0] > 235 && col[1] > 235 && col[2] > 240) col = P.mix([[255, 255, 255], [220, 214, 250], [250, 240, 220], [200, 196, 240]][(h3 * 4) | 0], col, 0.4);
|
|
156
|
+
else col = [col[0] + (h3 - .5) * 30, col[1] + (h4 - .5) * 30, col[2] + (hx - .5) * 30];
|
|
157
|
+
const a = X < 760 ? Math.atan2(Y - 790, X - 545) + Math.PI / 2 * 0.8 : (Y < 330 ? -0.2 : -Math.PI / 2 + 0.25 * Math.sin(Y * 0.03)), L = 13 * (0.7 + h4 * 0.6);
|
|
158
|
+
g.strokeStyle = P.rgb(col); g.beginPath(); g.moveTo(X - Math.cos(a) * L / 2, Y - Math.sin(a) * L / 2); g.lineTo(X + Math.cos(a) * L / 2, Y + Math.sin(a) * L / 2); g.stroke();
|
|
159
|
+
}
|
|
160
|
+
g.restore();
|
|
161
|
+
}
|
|
162
|
+
// 让笔触真正「不动」:P.strokes 的随机数是一条顺序流,palette 返回 null 时会多吃 3 个数(jitter),
|
|
163
|
+
// 而 null 与否取决于底稿颜色(罂粟一摇、盆栽一摆就变)→ 后面所有笔整体重洗。这里每格只吃 1 个数、永不返回 null。
|
|
164
|
+
const stable = (pal) => (x, y, col, r) => { const u = r(), rr = U.rng((u * 4294967296) >>> 0); return pal(x, y, col, rr) || P.jitter(col, rr, 24); };
|
|
165
|
+
return {
|
|
166
|
+
draw(c, lt, t) {
|
|
167
|
+
const ch = P.choreo(lt, t);
|
|
168
|
+
const b = P.scratch('impBase'), bg = b.getContext('2d', { willReadFrequently: true });
|
|
169
|
+
base(bg, t);
|
|
170
|
+
c.fillStyle = '#f2e6ee'; c.fillRect(0, 0, W, H); // 浅色画布底,笔触间露出
|
|
171
|
+
const fine = (x, y) => inWin(x, y) || (x > 945 && x < 1182 && y > 200 && y < 365);
|
|
172
|
+
P.strokes(c, b, { cell: 11, len: 20, width: 7, angle, seed: 12, t, boil: 0, outline: 0, palette: stable(palette), mask: (x, y) => !fine(x, y) });
|
|
173
|
+
P.strokes(c, b, { cell: 6, len: 11, width: 4.5, angle, seed: 13, t, boil: 0, outline: 0, palette: stable(palette), mask: fine }); // 窗景与墙上小画用更细的笔
|
|
174
|
+
sunSpots(c, t); wallGlints(c, t);
|
|
175
|
+
// 角色
|
|
176
|
+
const L = P.scratch('impChars'), lg = L.getContext('2d', { willReadFrequently: true }); lg.clearRect(0, 0, W, H);
|
|
177
|
+
const K = RIG.cat({ tail: ch.tail * 2.2, blink: ch.blink, breathe: ch.breathe * 3, look: Math.sin(t * 3) * 2 }); // 猫在阳光里扭头、甩尾
|
|
178
|
+
const G = RIG.girl({ cup: ch.cup, sip: ch.sip, hat: 'straw', hair: 'long', breathe: ch.breathe });
|
|
179
|
+
RIG.drawCat(lg, K, catPal);
|
|
180
|
+
girlExtras(lg, G, t);
|
|
181
|
+
RIG.drawGirl(lg, G, { ...girlPal, mode: 'fill' });
|
|
182
|
+
girlTop(lg, G, t);
|
|
183
|
+
// 角色笔触:自写循环,每格随机数用 U.hash(格号) —— 与其它格是否落笔无关,端杯一动也只有手臂那几笔变,不会整层重洗
|
|
184
|
+
charStrokes(lg, L, t);
|
|
185
|
+
c.drawImage(L, 0, 0);
|
|
186
|
+
// 五官与少量结构线:淡紫色细线(印象派不勾轮廓,只点出眼口)
|
|
187
|
+
c.save(); c.strokeStyle = 'rgba(110,90,170,.75)'; c.lineWidth = 2.4; c.lineCap = 'round';
|
|
188
|
+
if (ch.blink || ch.cup > 0.85) { c.beginPath(); c.arc(G.eye.x, G.eye.y - 2, 6, 0.3, Math.PI - 0.3); c.stroke(); }
|
|
189
|
+
else { c.fillStyle = '#4a60b0'; c.beginPath(); c.arc(G.eye.x - 1, G.eye.y + 1, 4.2, 0, 7); c.fill(); c.stroke(G.lid); }
|
|
190
|
+
c.lineWidth = 1.8; c.stroke(G.brow);
|
|
191
|
+
c.fillStyle = '#e05a6a'; c.beginPath(); c.moveTo(...G.lips[0]); c.lineTo(...G.lips[1]); c.lineTo(...G.lips[2]); c.closePath(); c.fill();
|
|
192
|
+
K.eyes.forEach(e => { if (K.blink) { c.beginPath(); c.arc(e.x, e.y, 8, 0.2, Math.PI - 0.2); c.stroke(); } else { c.fillStyle = '#7ac060'; c.beginPath(); c.arc(e.x, e.y, 8, 0, 7); c.fill(); c.fillStyle = '#3a3050'; c.beginPath(); c.ellipse(e.x + 2, e.y, 2.6, 6.5, 0, 0, 7); c.fill(); } });
|
|
193
|
+
c.fillStyle = '#e07a80'; c.beginPath(); c.arc(K.nose[0], K.nose[1], 5, 0, 7); c.fill();
|
|
194
|
+
c.lineWidth = 1.3; c.strokeStyle = 'rgba(255,255,255,.8)'; K.whiskers.forEach(w => { c.beginPath(); c.moveTo(...w[0]); c.lineTo(...w[1]); c.stroke(); });
|
|
195
|
+
c.restore();
|
|
196
|
+
// 猫毛在阳光里闪亮:背上的毛尖亮笔明灭
|
|
197
|
+
c.save(); c.globalCompositeOperation = 'screen'; c.lineCap = 'round';
|
|
198
|
+
const catBody = RIG.smooth(K.bodyPts);
|
|
199
|
+
for (let i = 0; i < 60; i++) { const h = U.hash(i, 77), x = 430 + U.hash(i, 3) * 230, y = 570 + U.hash(i, 5) * 200, a = Math.max(0, Math.sin(t * 7 + h * 30));
|
|
200
|
+
if (a < 0.35 || !(c.isPointInPath(catBody, x, y) || c.isPointInPath(K.head, x, y)) || x > 560 + (y - 560) * 0.1) continue; // 只亮在猫身上、受光的左半
|
|
201
|
+
c.strokeStyle = `rgba(255,250,210,${0.9 * a})`; c.lineWidth = 3.5; c.beginPath(); c.moveTo(x, y); c.lineTo(x + 6, y + 9); c.stroke(); }
|
|
202
|
+
c.restore();
|
|
203
|
+
P.steam(c, t, G.cup.x, G.cup.y - 8, { h: 60, n: 2, color: 'rgba(255,255,255,.85)', width: 5, spread: 14, wobble: 9 });
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
})();
|