@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,219 @@
|
|
|
1
|
+
// 1930 橡皮管卡通(Fleischer / 早期迪士尼 / Cuphead 式)—— 纯代码。
|
|
2
|
+
// 管线:①纸色单色底(墙纸竖纹、地板、窗外太阳脸)→ ②每个物体以自己的「着地点」为轴随 128BPM 节拍弹跳+挤压拉伸
|
|
3
|
+
// → ③角色:骨架只取锚点,画法整个换成卡通(大头、派切眼、橡皮管手臂、白手套)→ ④胶片层:抖片、闪烁、划痕、灰尘、暗角
|
|
4
|
+
// 时间:角色与母题都按 12fps「一拍二」步进(tq),只有胶片层按 24fps 变——这就是 30 年代赛璐珞的顿挫。
|
|
5
|
+
SCENES['33_rubberhose'] = (() => {
|
|
6
|
+
const W = 1920, H = 1080, { clamp, lerp, rng } = U, P = PAINT;
|
|
7
|
+
const INK = '#141210', PAPER = '#efe7d2', LT = '#d9d0b8', MID = '#a39a84', DK = '#5e574b', BEAT = 60 / 128;
|
|
8
|
+
const bnc = (tq, ph = 0) => Math.abs(Math.sin(Math.PI * (tq / BEAT + ph))); // 0 = 落地,1 = 最高
|
|
9
|
+
// 以着地点 (ax,ay) 为轴:上弹 amp 像素;落地时压扁、腾空时拉长
|
|
10
|
+
function boing(c, ax, ay, b, amp, fn, k = 1) {
|
|
11
|
+
c.save(); c.translate(ax, ay - b * amp); const sq = (1 - b) * 0.09 * k, st = b * 0.05 * k;
|
|
12
|
+
c.scale(1 + sq - st * 0.6, 1 - sq + st); c.translate(-ax, -ay); fn(); c.restore();
|
|
13
|
+
}
|
|
14
|
+
const ink = (c, w = 6) => { c.strokeStyle = INK; c.lineWidth = w; c.lineJoin = 'round'; c.lineCap = 'round'; };
|
|
15
|
+
const F = (c, p, col, w = 6) => { c.fillStyle = col; c.fill(p); ink(c, w); c.stroke(p); };
|
|
16
|
+
const circ = (x, y, r) => { const p = new Path2D(); p.arc(x, y, r, 0, Math.PI * 2); return p; };
|
|
17
|
+
const ell = (x, y, rx, ry, a = 0) => { const p = new Path2D(); p.ellipse(x, y, rx, ry, a, 0, Math.PI * 2); return p; };
|
|
18
|
+
// 派切眼:白眼眶+黑瞳,黑瞳上切掉一块楔形(30 年代的标志)
|
|
19
|
+
function pieEye(c, x, y, rx, ry, look, blink, lash) {
|
|
20
|
+
if (blink) { ink(c, 5); c.beginPath(); c.ellipse(x, y + ry * 0.2, rx, ry * 0.5, 0, 0.15, Math.PI - 0.15); c.stroke(); return; }
|
|
21
|
+
F(c, ell(x, y, rx, ry), '#fbf7ec', 4.5);
|
|
22
|
+
const px = x + look * rx * 0.35, py = y + ry * 0.25;
|
|
23
|
+
c.save(); c.clip(ell(x, y, rx, ry)); c.fillStyle = INK; c.beginPath(); c.ellipse(px, py, rx * 0.62, ry * 0.7, 0, 0, Math.PI * 2); c.fill();
|
|
24
|
+
c.fillStyle = '#fbf7ec'; c.beginPath(); c.moveTo(px + rx * 0.05, py - ry * 0.05); c.arc(px, py, ry, -1.45, -0.85); c.closePath(); c.fill(); c.restore();
|
|
25
|
+
if (lash) { ink(c, 4); for (let k = 0; k < 3; k++) { const a = -2.5 + k * 0.45; c.beginPath(); c.moveTo(x + Math.cos(a) * rx, y + Math.sin(a) * ry); c.lineTo(x + Math.cos(a) * (rx + 14), y + Math.sin(a) * (ry + 14)); c.stroke(); } }
|
|
26
|
+
}
|
|
27
|
+
// 橡皮管:没有肘的一根面条(二次贝塞尔),粗细恒定
|
|
28
|
+
function hose(c, A, B, bend, w, col) {
|
|
29
|
+
const mx = (A[0] + B[0]) / 2, my = (A[1] + B[1]) / 2, dx = B[0] - A[0], dy = B[1] - A[1], L = Math.hypot(dx, dy) || 1;
|
|
30
|
+
const C = [mx - dy / L * bend, my + dx / L * bend];
|
|
31
|
+
c.lineCap = 'round'; c.strokeStyle = INK; c.lineWidth = w + 10; c.beginPath(); c.moveTo(...A); c.quadraticCurveTo(...C, ...B); c.stroke();
|
|
32
|
+
c.strokeStyle = col; c.lineWidth = w; c.stroke();
|
|
33
|
+
}
|
|
34
|
+
// 白手套:四指卡通手,袖口外翻,手背三道线
|
|
35
|
+
function glove(c, x, y, ang, s = 1) {
|
|
36
|
+
c.save(); c.translate(x, y); c.rotate(ang); c.scale(s, s);
|
|
37
|
+
F(c, RIG.smooth([[-30, -14], [-8, -24], [16, -22], [30, -12], [34, 4], [24, 20], [4, 24], [-20, 18], [-32, 4]]), '#fbf7ec', 5);
|
|
38
|
+
[[22, -18], [30, -6], [30, 8]].forEach(([fx, fy]) => F(c, ell(fx + 6, fy, 11, 8, 0.2), '#fbf7ec', 5));
|
|
39
|
+
F(c, RIG.smooth([[-30, -20], [-46, -26], [-50, 0], [-46, 26], [-30, 20], [-26, 0]]), '#fbf7ec', 5);
|
|
40
|
+
ink(c, 3); [-6, 2, 10].forEach(xx => { c.beginPath(); c.moveTo(xx - 10, xx * 0.2 - 2); c.lineTo(xx + 2, xx * 0.2 - 4); c.stroke(); });
|
|
41
|
+
c.restore();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ---------- 静态底(缓存) ----------
|
|
45
|
+
const bg = () => P.cached('rh_bg', W, H, (g) => {
|
|
46
|
+
g.fillStyle = PAPER; g.fillRect(0, 0, W, H);
|
|
47
|
+
// 墙纸:竖条+小菱点
|
|
48
|
+
for (let x = 0; x < W; x += 64) { g.fillStyle = '#e5dcc5'; g.fillRect(x, 0, 30, 700); }
|
|
49
|
+
g.fillStyle = '#cfc5ab'; for (let y = 30; y < 690; y += 70) for (let x = 47; x < W; x += 64) { g.beginPath(); g.moveTo(x, y - 7); g.lineTo(x + 6, y); g.lineTo(x, y + 7); g.lineTo(x - 6, y); g.fill(); }
|
|
50
|
+
// 地板
|
|
51
|
+
g.fillStyle = '#cdc3a8'; g.fillRect(0, 712, W, H - 712);
|
|
52
|
+
ink(g, 3); g.strokeStyle = '#8a816c'; for (let y = 760; y < H; y += 60) { g.beginPath(); g.moveTo(0, y); g.lineTo(W, y); g.stroke(); }
|
|
53
|
+
for (let k = 0; k < 40; k++) { const y = 712 + Math.floor(k / 8) * 60, x = (k % 8) * 260 + (Math.floor(k / 8) % 2) * 130; g.beginPath(); g.moveTo(x, y); g.lineTo(x, y + 60); g.stroke(); }
|
|
54
|
+
g.fillStyle = DK; g.fillRect(0, 690, W, 24); ink(g, 5); g.beginPath(); g.moveTo(0, 690); g.lineTo(W, 690); g.moveTo(0, 714); g.lineTo(W, 714); g.stroke();
|
|
55
|
+
// 水彩式明暗:角落压暗
|
|
56
|
+
const vg = g.createRadialGradient(900, 480, 300, 900, 520, 1200); vg.addColorStop(0, 'rgba(0,0,0,0)'); vg.addColorStop(1, 'rgba(60,50,35,.35)'); g.fillStyle = vg; g.fillRect(0, 0, W, H);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
function windowAndSky(c, tq) {
|
|
60
|
+
// 窗洞里的天
|
|
61
|
+
c.save(); c.beginPath(); c.rect(360, 130, 440, 420); c.clip();
|
|
62
|
+
c.fillStyle = '#f6f0e0'; c.fillRect(360, 130, 440, 420);
|
|
63
|
+
// 远山与树(树随拍点弹)
|
|
64
|
+
c.fillStyle = LT; c.beginPath(); c.moveTo(360, 480); c.quadraticCurveTo(470, 410, 580, 470); c.quadraticCurveTo(690, 420, 800, 470); c.lineTo(800, 550); c.lineTo(360, 550); c.fill(); ink(c, 4); c.stroke();
|
|
65
|
+
boing(c, 450, 500, bnc(tq, 0.25), 14, () => { F(c, RIG.smooth([[442, 500], [446, 440], [458, 440], [462, 500]]), DK, 4); F(c, RIG.smooth([[400, 440], [420, 380], [452, 360], [490, 380], [505, 430], [470, 455], [430, 455]]), MID, 5); });
|
|
66
|
+
// 太阳脸:光芒旋转、脸跟拍点点头
|
|
67
|
+
const sx = 690, sy = 235, rot = tq * 1.6;
|
|
68
|
+
c.save(); c.translate(sx, sy); c.rotate(rot); c.fillStyle = '#fbf7ec';
|
|
69
|
+
for (let k = 0; k < 12; k++) { c.rotate(Math.PI / 6); c.beginPath(); c.moveTo(-12, -66); c.lineTo(0, -108 - (k % 2) * 16); c.lineTo(12, -66); c.closePath(); c.fill(); ink(c, 4); c.stroke(); }
|
|
70
|
+
c.restore();
|
|
71
|
+
boing(c, sx, sy + 58, bnc(tq), 8, () => {
|
|
72
|
+
F(c, circ(sx, sy, 60), '#fbf7ec', 5);
|
|
73
|
+
pieEye(c, sx - 20, sy - 10, 11, 16, -0.4, 0, false); pieEye(c, sx + 16, sy - 10, 11, 16, -0.4, 0, false);
|
|
74
|
+
ink(c, 5); c.beginPath(); c.arc(sx - 2, sy + 12, 26, 0.35, Math.PI - 0.35); c.stroke();
|
|
75
|
+
c.fillStyle = MID; c.beginPath(); c.arc(sx - 36, sy + 14, 9, 0, 7); c.arc(sx + 32, sy + 14, 9, 0, 7); c.fill();
|
|
76
|
+
});
|
|
77
|
+
// 云:两朵,随拍伸缩并慢慢飘
|
|
78
|
+
[[470 + tq * 30, 200, 0.1], [560 + tq * 22, 300, 0.6]].forEach(([x, y, ph]) => boing(c, x, y + 30, bnc(tq, ph), 10, () => {
|
|
79
|
+
F(c, RIG.smooth([[x - 60, y + 30], [x - 70, y + 6], [x - 40, y - 18], [x - 6, y - 30], [x + 30, y - 22], [x + 62, y - 4], [x + 66, y + 26]]), '#fbf7ec', 5); }));
|
|
80
|
+
c.restore();
|
|
81
|
+
// 窗框(白漆粗框+十字)
|
|
82
|
+
ink(c, 6); c.fillStyle = '#f4eedc';
|
|
83
|
+
[[350, 120, 460, 18], [350, 542, 460, 18], [350, 120, 18, 440], [792, 120, 18, 440], [571, 120, 18, 440], [350, 328, 460, 16]].forEach(r => { c.fillRect(...r); c.strokeRect(...r); });
|
|
84
|
+
c.fillStyle = '#f4eedc'; c.fillRect(320, 556, 520, 26); c.strokeRect(320, 556, 520, 26);
|
|
85
|
+
// 窗帘:圆点布,系起,下摆随拍甩
|
|
86
|
+
[[330, 1], [830, -1]].forEach(([x, sgn], k) => {
|
|
87
|
+
const sw = (bnc(tq, 0.5 + k * 0.5) - 0.5) * 18 * sgn;
|
|
88
|
+
const p = RIG.smooth([[x, 96], [x + sgn * 70, 96], [x + sgn * 54, 260], [x + sgn * 26, 330], [x + sgn * 70 + sw, 520], [x + sgn * 30 + sw, 600], [x - sgn * 4, 600]]);
|
|
89
|
+
c.fillStyle = MID; c.fill(p); c.save(); c.clip(p); c.fillStyle = '#efe7d2'; for (let yy = 100; yy < 620; yy += 34) for (let xx = x - 80; xx < x + 80; xx += 34) { c.beginPath(); c.arc(xx + (yy / 34 % 2) * 17, yy, 6, 0, 7); c.fill(); } c.restore();
|
|
90
|
+
ink(c, 5); c.stroke(p); c.fillStyle = INK; c.fillRect(x + sgn * 10 - 10, 318, 50, 14);
|
|
91
|
+
});
|
|
92
|
+
ink(c, 8); c.beginPath(); c.moveTo(300, 96); c.lineTo(860, 96); c.stroke();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function table(c, tq) {
|
|
96
|
+
// 桌:桌腿是弯的橡皮管,落拍压扁
|
|
97
|
+
boing(c, 1005, 905, bnc(tq, 0.5), 16, () => {
|
|
98
|
+
[[850, 1], [1160, -1]].forEach(([x, s]) => { ink(c, 26); c.beginPath(); c.moveTo(x, 640); c.quadraticCurveTo(x + s * 26, 770, x - s * 6, 900); c.stroke(); c.strokeStyle = '#8f8670'; c.lineWidth = 15; c.stroke(); });
|
|
99
|
+
F(c, RIG.smooth([[805, 620], [1000, 608], [1205, 620], [1210, 648], [1000, 660], [800, 648]]), '#bdb399', 6);
|
|
100
|
+
ink(c, 3); c.beginPath(); c.moveTo(830, 640); c.quadraticCurveTo(1000, 650, 1180, 640); c.stroke();
|
|
101
|
+
// 花瓶+跳舞的花(花有脸,叶子当手挥)
|
|
102
|
+
F(c, RIG.smooth([[858, 615], [846, 580], [868, 548], [902, 548], [920, 580], [906, 615]]), DK, 5);
|
|
103
|
+
c.fillStyle = PAPER; [[870, 575], [892, 590], [884, 565]].forEach(([x, y]) => { c.beginPath(); c.arc(x, y, 4, 0, 7); c.fill(); });
|
|
104
|
+
const sway = Math.sin(Math.PI * tq / BEAT) * 0.35, top = [884 + Math.sin(sway) * 120, 548 - Math.cos(sway) * 120];
|
|
105
|
+
ink(c, 12); c.beginPath(); c.moveTo(884, 550); c.quadraticCurveTo(884 - sway * 30, 490, top[0], top[1]); c.stroke(); c.strokeStyle = MID; c.lineWidth = 6; c.stroke();
|
|
106
|
+
const leaf = (s) => { c.save(); c.translate(884 - sway * 20, 500); c.rotate(s * (0.9 + sway * 1.4 * s)); F(c, ell(28 * s, 0, 30, 11), MID, 4.5); c.restore(); }; leaf(1); leaf(-1);
|
|
107
|
+
c.save(); c.translate(top[0], top[1]); c.rotate(sway);
|
|
108
|
+
for (let k = 0; k < 8; k++) { c.save(); c.rotate(k / 8 * Math.PI * 2 + tq * 2); F(c, ell(0, -36, 15, 24), '#fbf7ec', 4.5); c.restore(); }
|
|
109
|
+
F(c, circ(0, 0, 28), LT, 5); pieEye(c, -9, -5, 6, 9, 0.3, 0, false); pieEye(c, 9, -5, 6, 9, 0.3, 0, false);
|
|
110
|
+
ink(c, 4); c.beginPath(); c.arc(0, 6, 11, 0.3, Math.PI - 0.3); c.stroke(); c.restore();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function chair(c) {
|
|
115
|
+
ink(c, 6); c.fillStyle = '#8f8670';
|
|
116
|
+
[[1436, 1], [1496, -1]].forEach(([x, s]) => { ink(c, 22); c.beginPath(); c.moveTo(x, 460); c.quadraticCurveTo(x - s * 14, 700, x + s * 4, 905); c.stroke(); c.strokeStyle = '#8f8670'; c.lineWidth = 11; c.stroke(); });
|
|
117
|
+
const back = RIG.smooth([[1416, 470], [1440, 420], [1470, 404], [1500, 420], [1522, 470], [1500, 480], [1470, 466], [1440, 480]]);
|
|
118
|
+
c.fill(back); c.stroke(back); [500, 560].forEach(y => { c.fillStyle = '#8f8670'; c.fillRect(1436, y, 60, 14); c.strokeRect(1436, y, 60, 14); });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function girl(c, tq, lt, t) {
|
|
122
|
+
const ch = P.choreo(lt, t);
|
|
123
|
+
const cup = 0.5 - 0.5 * Math.cos(clamp(Math.floor(lt * 12) / 12 / 0.9) * Math.PI); // 端杯也按一拍二步进
|
|
124
|
+
const G = RIG.girl({ cup, sip: 0, dx: 0, dy: 0 });
|
|
125
|
+
const b = bnc(tq, 0.25);
|
|
126
|
+
boing(c, 1240, 930, b, 18, () => {
|
|
127
|
+
// 裙:深色波点长裙
|
|
128
|
+
F(c, G.skirt, DK, 6);
|
|
129
|
+
c.save(); c.clip(G.skirt); c.fillStyle = PAPER; for (let y = 640; y < 940; y += 40) for (let x = 1090; x < 1470; x += 40) { c.beginPath(); c.arc(x + (y / 40 % 2) * 20, y, 7, 0, 7); c.fill(); } c.restore();
|
|
130
|
+
ink(c, 6); c.stroke(G.skirt);
|
|
131
|
+
F(c, ell(1120, 924, 34, 15), INK, 4); F(c, ell(1170, 928, 30, 13), INK, 4); // 玛丽珍鞋
|
|
132
|
+
// 远手:搭桌上的橡皮管+手套
|
|
133
|
+
hose(c, [1340, 480], [1196, 612], -30, 14, '#f4eedc'); glove(c, 1180, 614, Math.PI, 0.9);
|
|
134
|
+
// 身体
|
|
135
|
+
F(c, G.torso, DK, 6);
|
|
136
|
+
c.save(); c.clip(G.torso); c.fillStyle = PAPER; for (let y = 440; y < 630; y += 40) for (let x = 1280; x < 1430; x += 40) { c.beginPath(); c.arc(x + (y / 40 % 2) * 20, y, 7, 0, 7); c.fill(); } c.restore(); ink(c, 6); c.stroke(G.torso);
|
|
137
|
+
// 头:大圆头,略比骨架大,朝左
|
|
138
|
+
const hc = [G.A.headC[0] - 14, G.A.headC[1] - 14], R0 = 82;
|
|
139
|
+
F(c, ell(G.A.neck[0], G.A.neck[1] - 6, 18, 26), '#f4eedc', 5);
|
|
140
|
+
F(c, RIG.smooth([[G.A.neck[0] - 44, G.A.neck[1] + 4], [G.A.neck[0] - 16, G.A.neck[1] + 28], [G.A.neck[0] + 6, G.A.neck[1] + 10], [G.A.neck[0] + 30, G.A.neck[1] + 28], [G.A.neck[0] + 54, G.A.neck[1] + 2], [G.A.neck[0], G.A.neck[1] - 6]]), '#fbf7ec', 5); // 小圆领
|
|
141
|
+
// 后脑卷发
|
|
142
|
+
F(c, RIG.smooth([[hc[0] + 10, hc[1] - 78], [hc[0] + 70, hc[1] - 62], [hc[0] + 100, hc[1] - 10], [hc[0] + 96, hc[1] + 50], [hc[0] + 66, hc[1] + 84], [hc[0] + 30, hc[1] + 60], [hc[0] + 10, hc[1]]]), '#ddd2ae', 6);
|
|
143
|
+
F(c, ell(hc[0], hc[1] + 4, R0 * 0.86, R0 * 0.9), '#f8f3e6', 6); // 脸
|
|
144
|
+
F(c, RIG.smooth([[hc[0] - 70, hc[1] - 30], [hc[0] - 50, hc[1] - 74], [hc[0] + 10, hc[1] - 88], [hc[0] + 70, hc[1] - 64], [hc[0] + 84, hc[1] - 6], [hc[0] + 40, hc[1] - 30], [hc[0] + 6, hc[1] - 46], [hc[0] - 36, hc[1] - 38]]), '#ddd2ae', 6); // 刘海
|
|
145
|
+
ink(c, 4); for (let k = 0; k < 3; k++) { c.beginPath(); c.arc(hc[0] + 70 + k * 6, hc[1] + 10 + k * 22, 10, 0.5, 5.5); c.stroke(); } // 卷
|
|
146
|
+
// 金发要读成头发不是软帽:发丝线+卡通高光(白月牙)
|
|
147
|
+
ink(c, 3.5); [[-40, -60, 20, -40], [-10, -74, 50, -50], [30, -78, 80, -40], [60, -50, 92, 10], [70, -10, 90, 50]].forEach(([a, b, c2, d]) => { c.beginPath(); c.moveTo(hc[0] + a, hc[1] + b); c.quadraticCurveTo(hc[0] + (a + c2) / 2 + 10, hc[1] + (b + d) / 2 - 12, hc[0] + c2, hc[1] + d); c.stroke(); });
|
|
148
|
+
c.fillStyle = '#fbf7ec'; c.beginPath(); c.ellipse(hc[0] + 40, hc[1] - 56, 22, 7, 0.5, 0, 7); c.fill();
|
|
149
|
+
// 头顶大蝴蝶结:比身体多晚半拍弹(跟随)
|
|
150
|
+
const bb = bnc(tq, 0.05) * 10;
|
|
151
|
+
c.save(); c.translate(hc[0] + 40, hc[1] - 84 - bb); c.rotate(0.3);
|
|
152
|
+
F(c, RIG.smooth([[0, 0], [-46, -30], [-52, 18]]), INK, 4); F(c, RIG.smooth([[0, 0], [46, -30], [52, 18]]), INK, 4); F(c, circ(0, 0, 12), INK, 4); c.restore();
|
|
153
|
+
const blink = ch.blink && !(cup > 0.85);
|
|
154
|
+
pieEye(c, hc[0] - 44, hc[1] - 4, 15, 23, -0.7, blink || cup > 0.85, true); pieEye(c, hc[0] - 8, hc[1] - 6, 17, 26, -0.7, blink || cup > 0.85, true);
|
|
155
|
+
c.fillStyle = INK; c.beginPath(); c.arc(hc[0] - 72, hc[1] + 20, 7, 0, 7); c.fill(); // 鼻头
|
|
156
|
+
c.fillStyle = MID; c.beginPath(); c.arc(hc[0] - 20, hc[1] + 34, 13, 0, 7); c.fill();
|
|
157
|
+
ink(c, 5); c.beginPath(); c.moveTo(hc[0] - 60, hc[1] + 46); c.quadraticCurveTo(hc[0] - 46, hc[1] + 56, hc[0] - 34, hc[1] + 44); c.stroke();
|
|
158
|
+
// 近手:橡皮管从肩到手,杯子在手前
|
|
159
|
+
const S = [G.A.shoulder[0] - 6, G.A.shoulder[1] + 4], Hd = G.A.hand;
|
|
160
|
+
F(c, ell(S[0], S[1], 34, 28), '#fbf7ec', 5); // 泡泡袖
|
|
161
|
+
hose(c, S, Hd, 40 - cup * 30, 14, '#f4eedc');
|
|
162
|
+
glove(c, Hd[0], Hd[1], -2.6 + cup * 0.8, 1);
|
|
163
|
+
const cp = G.cup; c.save(); c.translate(cp.x, cp.y); c.rotate(cp.tilt);
|
|
164
|
+
F(c, RIG.smooth([[-30, 0], [-26, 34], [0, 44], [26, 34], [30, 0]], true), '#fbf7ec', 5); ink(c, 5); c.beginPath(); c.arc(34, 18, 12, -1.3, 1.5); c.stroke();
|
|
165
|
+
c.fillStyle = MID; c.beginPath(); c.ellipse(0, 2, 28, 6, 0, 0, 7); c.fill(); c.stroke();
|
|
166
|
+
c.restore();
|
|
167
|
+
// 热气:卡通烟圈,一拍冒一个
|
|
168
|
+
for (let k = 0; k < 3; k++) { const q = ((tq / BEAT) + k / 3) % 1; ink(c, 4); c.beginPath(); c.ellipse(cp.x + Math.sin(q * 6) * 8, cp.y - 20 - q * 90, 10 + q * 10, 6 + q * 6, 0, 0, 7); c.globalAlpha = 1 - q; c.stroke(); c.globalAlpha = 1; }
|
|
169
|
+
}, 1.2);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function cat(c, tq, t) {
|
|
173
|
+
const K = RIG.cat({ tail: 0, blink: 0, breathe: 0 });
|
|
174
|
+
boing(c, 540, 905, bnc(tq, 0.75), 30, () => {
|
|
175
|
+
// 尾巴:一根波浪橡皮管
|
|
176
|
+
const base = [460, 880]; ink(c, 1); const pts = [];
|
|
177
|
+
for (let i = 0; i <= 20; i++) { const q = i / 20; pts.push([base[0] - q * 150, base[1] - q * 200 + Math.sin(q * 7 - tq * 9) * 26 * q]); }
|
|
178
|
+
c.lineCap = 'round'; c.strokeStyle = INK; c.lineWidth = 32; c.beginPath(); pts.forEach((p, i) => i ? c.lineTo(...p) : c.moveTo(...p)); c.stroke();
|
|
179
|
+
c.strokeStyle = MID; c.lineWidth = 20; c.stroke(); c.fillStyle = '#fbf7ec'; c.beginPath(); c.arc(...pts[20], 12, 0, 7); c.fill();
|
|
180
|
+
const body = RIG.smooth(K.bodyPts); // 骨架的身体是折线,卡通要圆:Catmull-Rom 一下
|
|
181
|
+
F(c, body, MID, 6);
|
|
182
|
+
c.save(); c.clip(body); c.fillStyle = '#fbf7ec'; c.fill(RIG.smooth([[566, 700], [612, 690], [642, 742], [636, 830], [596, 870], [560, 800]])); ink(c, 9); c.strokeStyle = DK; K.stripes.slice(3).forEach(s => { c.beginPath(); c.moveTo(...s[0]); c.lineTo(...s[1]); c.stroke(); }); c.restore();
|
|
183
|
+
ink(c, 6); c.stroke(body);
|
|
184
|
+
// 白爪(大圆手套脚)
|
|
185
|
+
F(c, ell(585, 892, 30, 18), '#fbf7ec', 5); F(c, ell(640, 892, 30, 18), '#fbf7ec', 5);
|
|
186
|
+
// 头:橘(中灰)头顶虎斑,白口鼻,派切大眼,咧嘴笑
|
|
187
|
+
const hb = bnc(tq, 0.95) * 8;
|
|
188
|
+
c.save(); c.translate(0, -hb);
|
|
189
|
+
F(c, K.head, MID, 6);
|
|
190
|
+
c.save(); c.clip(K.head); ink(c, 9); c.strokeStyle = DK; K.stripes.slice(0, 3).forEach(s => { c.beginPath(); c.moveTo(...s[0]); c.lineTo(...s[1]); c.stroke(); }); c.restore();
|
|
191
|
+
F(c, ell(630, 652, 40, 28), '#fbf7ec', 5);
|
|
192
|
+
pieEye(c, 610, 610, 15, 22, 0.6, 0, false); pieEye(c, 648, 607, 15, 22, 0.6, 0, false);
|
|
193
|
+
c.fillStyle = INK; c.beginPath(); c.ellipse(656, 636, 10, 7, 0, 0, 7); c.fill();
|
|
194
|
+
ink(c, 5); c.beginPath(); c.moveTo(612, 650); c.quadraticCurveTo(640, 676, 670, 646); c.stroke();
|
|
195
|
+
ink(c, 3); [[660, 640, 716, 626], [662, 648, 718, 650], [600, 646, 552, 634]].forEach(([a, b, c2, d]) => { c.beginPath(); c.moveTo(a, b); c.lineTo(c2, d); c.stroke(); });
|
|
196
|
+
c.restore();
|
|
197
|
+
}, 1.4);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ---------- 胶片层:P.film(24fps 闪烁、跨格存活的划痕、灰尘毛发、颗粒抖动、圆角片门) ----------
|
|
201
|
+
const film = (c, t) => P.film(c, t, { grain: 'rhg' });
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
draw(c, lt, t) {
|
|
205
|
+
const tq = U.stepTime(t, 12); // 一拍二:12 张/秒(步进帧率是这个风格的味道本身)
|
|
206
|
+
P.gateWeave(c, t, () => { // 抖片(gate weave,24fps)
|
|
207
|
+
c.drawImage(bg(), 0, 0);
|
|
208
|
+
windowAndSky(c, tq);
|
|
209
|
+
chair(c);
|
|
210
|
+
table(c, tq);
|
|
211
|
+
girl(c, tq, lt, t);
|
|
212
|
+
cat(c, tq, t);
|
|
213
|
+
});
|
|
214
|
+
// 胶片褪色成单色暖灰(防止任何残余彩色)
|
|
215
|
+
P.fade(c, { sat: 1, tint: '#f2e8d2' });
|
|
216
|
+
film(c, t);
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
})();
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// 中国皮影戏 —— 纯代码。
|
|
2
|
+
// 管线:①影窗(油灯在幕后的暖光斑+布纹,灯焰摇曳→光斑位置/亮度抖)→ ②每个「皮件」在离屏层上:填半透明彩色 → 镂空(destination-out 刻纹)
|
|
3
|
+
// → 补皮边 → 整层 multiply 印到幕布上(皮子透光=幕布色×皮色,重叠处自然变深)+偏移软影(皮件没贴紧幕布时的虚影)
|
|
4
|
+
// → ③铆钉、操纵杆(杆子在幕后,糊一点)→ ④布纹+木框
|
|
5
|
+
// 动作:杆子操纵的僵直关节——每块皮件是刚体,只绕铆钉转;手腕是松的,会甩;整个人随主杆轻摆;节奏按 10fps 步进。
|
|
6
|
+
SCENES['34_shadowpuppet'] = (() => {
|
|
7
|
+
const W = 1920, H = 1080, { clamp, lerp, rng } = U, P = PAINT;
|
|
8
|
+
const RED = '#c0352a', GRN = '#2f7d4c', AMB = '#d99a2e', YEL = '#e2bd4a', EDGE = '#3a160a', BLU = '#2c5a8a', ORG = '#d9782a';
|
|
9
|
+
const step = (t, fps = 10) => U.stepTime(t, fps); // 杆操的「一顿一顿」:10fps 步进
|
|
10
|
+
// ---------- 刻纹(在 clip 内 destination-out) ----------
|
|
11
|
+
// 刻纹库 P.carve(鱼子/团花/云纹/方格/刻线);皮件 P.piece(填→刻→补皮边→描边);印到幕布 P.stamp(软影+multiply)
|
|
12
|
+
const K = P.carve;
|
|
13
|
+
const piece = (g, path, col, carve, edge = 5) => P.piece(g, path, col, carve, { edge, line: EDGE });
|
|
14
|
+
const rivet = (c, x, y, r = 7) => { c.fillStyle = '#2a1006'; c.beginPath(); c.arc(x, y, r, 0, 7); c.fill(); c.fillStyle = '#b07a3a'; c.beginPath(); c.arc(x - 1, y - 1, r * 0.42, 0, 7); c.fill(); };
|
|
15
|
+
const rod = (c, A, B) => { c.save(); c.filter = 'blur(1.2px)'; c.globalCompositeOperation = 'multiply'; c.strokeStyle = 'rgba(60,30,12,.85)'; c.lineWidth = 5; c.beginPath(); c.moveTo(...A); c.lineTo(...B); c.stroke(); c.restore(); };
|
|
16
|
+
const rot = (p, o, a) => { const c = Math.cos(a), s = Math.sin(a), x = p[0] - o[0], y = p[1] - o[1]; return [o[0] + x * c - y * s, o[1] + x * s + y * c]; };
|
|
17
|
+
const tr = (path, o, a) => { const m = new DOMMatrix().translate(o[0], o[1]).rotate(a * 180 / Math.PI).translate(-o[0], -o[1]); const p = new Path2D(); p.addPath(path, m); return p; };
|
|
18
|
+
const stamp = (c, layer, dx = 7, dy = 5) => P.stamp(c, layer, { dx, dy });
|
|
19
|
+
|
|
20
|
+
// ---------- 影窗 ----------
|
|
21
|
+
function screen(c, t) {
|
|
22
|
+
const ft = step(t, 15), fl = 0.5 * P.noise(ft * 3, 1) + 0.3 * P.noise(ft * 9, 4);
|
|
23
|
+
const cx = 960 + fl * 26, cy = 470 + P.noise(ft * 2, 7) * 18;
|
|
24
|
+
const g = c.createRadialGradient(cx, cy, 30, cx, cy + 40, 1150);
|
|
25
|
+
g.addColorStop(0, `rgb(${255},${246 + fl * 8},${214 + fl * 20})`); g.addColorStop(0.35, '#f2d7a0'); g.addColorStop(0.7, '#cf9a58'); g.addColorStop(1, '#7a4a20');
|
|
26
|
+
c.fillStyle = g; c.fillRect(0, 0, W, H);
|
|
27
|
+
// 灯芯热点:一小团更亮的光,随灯焰跳
|
|
28
|
+
c.save(); c.globalCompositeOperation = 'screen'; const hg = c.createRadialGradient(cx, cy - 10, 0, cx, cy - 10, 260 + fl * 40); hg.addColorStop(0, `rgba(255,236,190,${0.55 + fl * 0.25})`); hg.addColorStop(1, 'rgba(255,236,190,0)'); c.fillStyle = hg; c.fillRect(0, 0, W, H); c.restore();
|
|
29
|
+
// 布纹(缓存)
|
|
30
|
+
c.save(); c.globalCompositeOperation = 'multiply'; c.drawImage(P.cached('py_weave', W, H, (w) => {
|
|
31
|
+
w.fillStyle = '#fff'; w.fillRect(0, 0, W, H); w.strokeStyle = 'rgba(120,90,50,.08)'; w.lineWidth = 1;
|
|
32
|
+
for (let x = 0; x < W; x += 3) { w.beginPath(); w.moveTo(x, 0); w.lineTo(x, H); w.stroke(); } for (let y = 0; y < H; y += 3) { w.beginPath(); w.moveTo(0, y); w.lineTo(W, y); w.stroke(); }
|
|
33
|
+
w.drawImage(P.grain('pyg', 0.06, [120, 80, 40], 0.3), 0, 0);
|
|
34
|
+
}), 0, 0); c.restore();
|
|
35
|
+
return fl;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ---------- 道具:窗(窗棂+梅枝)、桌(万字纹桌裙)、瓶花 ----------
|
|
39
|
+
const props = () => P.cached('py_props', W, H, (g) => {
|
|
40
|
+
// 窗:外框+冰裂/方格窗棂(大面积镂空)
|
|
41
|
+
const win = new Path2D(); win.rect(350, 120, 460, 440);
|
|
42
|
+
piece(g, win, '#8a4a22', (q) => {
|
|
43
|
+
q.fillRect(372, 142, 416, 396);
|
|
44
|
+
}, 4);
|
|
45
|
+
g.save(); g.beginPath(); g.rect(372, 142, 416, 396); g.clip();
|
|
46
|
+
g.strokeStyle = '#8a4a22'; g.lineWidth = 9; const r = rng(3);
|
|
47
|
+
for (let x = 372; x <= 788; x += 69) { g.beginPath(); g.moveTo(x, 142); g.lineTo(x, 538); g.stroke(); }
|
|
48
|
+
for (let y = 142; y <= 538; y += 66) { g.beginPath(); g.moveTo(372, y); g.lineTo(788, y); g.stroke(); }
|
|
49
|
+
g.strokeStyle = EDGE; g.lineWidth = 2; for (let x = 372; x <= 788; x += 69) { g.strokeRect(x - 4.5, 142, 9, 396); } for (let y = 142; y <= 538; y += 66) g.strokeRect(372, y - 4.5, 416, 9);
|
|
50
|
+
g.restore();
|
|
51
|
+
// 窗台
|
|
52
|
+
piece(g, (() => { const p = new Path2D(); p.rect(326, 556, 508, 26); return p; })(), '#8a4a22', (q) => K.dots(q, 336, 562, 830, 580, 14, 3), 3);
|
|
53
|
+
// 桌:桌面+桌裙(万字纹格镂空)+两条腿
|
|
54
|
+
const top = new Path2D(); top.rect(806, 612, 410, 30); piece(g, top, '#9a3a1c', (q) => K.dots(q, 816, 620, 1210, 640, 16, 3));
|
|
55
|
+
const apron = new Path2D(); apron.rect(826, 642, 370, 70); piece(g, apron, '#9a3a1c', (q) => K.lattice(q, 834, 648, 1190, 708, 30, 14));
|
|
56
|
+
[[836, 642], [1076, 642]].forEach(([x, y]) => { const l = new Path2D(); l.moveTo(x, y); l.lineTo(x + 28, y); l.lineTo(x + 24 + (x < 900 ? -6 : 6), 905); l.lineTo(x + 4 + (x < 900 ? -6 : 6), 905); l.closePath(); piece(g, l, '#9a3a1c', (q) => K.dots(q, x + 8, y + 90, x + 22, 890, 30, 4)); });
|
|
57
|
+
// 瓶+牡丹
|
|
58
|
+
const vase = RIG.smooth([[860, 612], [846, 578], [866, 548], [900, 548], [922, 578], [908, 612]]); piece(g, vase, BLU, (q) => K.clouds(q, 852, 560, 916, 608, 26));
|
|
59
|
+
g.strokeStyle = GRN; g.lineWidth = 7; [[878, 548, 850, 480], [890, 548, 920, 470]].forEach(([a, b, c2, d]) => { g.beginPath(); g.moveTo(a, b); g.lineTo(c2, d); g.stroke(); });
|
|
60
|
+
[[850, 474, 30, RED], [922, 462, 34, '#d0506a']].forEach(([x, y, rr, col]) => { const f = new Path2D(); for (let k = 0; k < 10; k++) { const a = k / 10 * Math.PI * 2; f.moveTo(x + Math.cos(a) * rr * 0.5, y + Math.sin(a) * rr * 0.5); f.ellipse(x + Math.cos(a) * rr * 0.6, y + Math.sin(a) * rr * 0.6, rr * 0.45, rr * 0.3, a, 0, 7); }
|
|
61
|
+
piece(g, f, col, (q) => { q.beginPath(); q.arc(x, y, rr * 0.22, 0, 7); q.fill(); K.dots(q, x - rr, y - rr, x + rr, y + rr, 12, 2); }, 3); });
|
|
62
|
+
// 椅:方背椅
|
|
63
|
+
const chair = new Path2D(); chair.rect(1430, 470, 64, 440); piece(g, chair, '#7a3a1a', (q) => K.lattice(q, 1436, 480, 1490, 900, 32, 18), 4);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
draw(c, lt, t) {
|
|
68
|
+
const fl = screen(c, t);
|
|
69
|
+
const ts = step(t), ls = step(lt);
|
|
70
|
+
stamp(c, props(), 6, 4);
|
|
71
|
+
// 窗外的皮影蝴蝶:翅膀按 12fps 扇(scaleX 翻面),杆子操纵着沿弧线飞
|
|
72
|
+
{ const L = P.scratch('pyBf'), g = L.getContext('2d'); g.reset();
|
|
73
|
+
const q = step(lt, 12), bx = 520 + q * 150, by = 300 - Math.sin(q * 3) * 50, flap = Math.abs(Math.cos(q * 15));
|
|
74
|
+
g.save(); g.translate(bx, by); g.rotate(0.3);
|
|
75
|
+
[-1, 1].forEach(sd => { g.save(); g.scale(sd * (0.25 + flap * 0.75), 1);
|
|
76
|
+
piece(g, RIG.smooth([[0, -4], [40, -46], [74, -30], [62, 4], [30, 10]]), sd > 0 ? RED : GRN, (k) => K.dots(k, 10, -40, 70, 6, 14, 3.5), 3);
|
|
77
|
+
piece(g, RIG.smooth([[0, 4], [34, 14], [46, 44], [16, 40]]), YEL, (k) => K.dots(k, 8, 10, 44, 40, 12, 3), 3); g.restore(); });
|
|
78
|
+
piece(g, (() => { const p = new Path2D(); p.ellipse(0, 6, 7, 22, 0, 0, 7); return p; })(), EDGE, null, 2); g.restore();
|
|
79
|
+
stamp(c, L, 5, 4); rod(c, [bx + 4, by + 24], [780, 1080]); }
|
|
80
|
+
// ---------- 猫 ----------
|
|
81
|
+
const ch = P.choreo(lt, t);
|
|
82
|
+
const KT = RIG.cat({ breathe: 0 });
|
|
83
|
+
{ const L = P.scratch('pyCat'), g = L.getContext('2d'); g.reset();
|
|
84
|
+
const sway = 0.02 * Math.sin(ts * 5);
|
|
85
|
+
g.save(); g.translate(540, 905); g.rotate(sway); g.translate(-540, -905);
|
|
86
|
+
// 尾巴:4 节皮件铰链,每节相对上一节转一个相位滞后的角度(鞭梢)
|
|
87
|
+
let o = [452, 884], ang = -3.0;
|
|
88
|
+
const segs = [];
|
|
89
|
+
for (let i = 0; i < 4; i++) { ang += 0.3 * Math.sin(ts * 7 - i * 0.9) + (i ? 0.38 : 0); const L_ = 64 - i * 6, e = [o[0] + Math.cos(ang) * L_, o[1] + Math.sin(ang) * L_];
|
|
90
|
+
const p = RIG.taper(o, e, 30 - i * 4, 26 - i * 4); segs.push([p, o, i]); o = e; }
|
|
91
|
+
segs.forEach(([p, oo, i]) => piece(g, p, i === 3 ? '#efe6cc' : ORG, i === 3 ? null : (k) => K.lines(k, [[[oo[0] - 6, oo[1] - 10], [oo[0] + 8, oo[1] + 10]]], 4), 3));
|
|
92
|
+
// 身体(橘皮,背上刻虎斑;白肚=镂空只留皮边)
|
|
93
|
+
const body = RIG.smooth(KT.bodyPts);
|
|
94
|
+
piece(g, body, ORG, (k) => {
|
|
95
|
+
k.fill(RIG.smooth([[566, 704], [612, 694], [640, 744], [632, 828], [596, 868], [562, 802]])); // 白胸肚:整块镂空
|
|
96
|
+
K.lines(k, [[[448, 742], [492, 752]], [[440, 782], [486, 792]], [[436, 824], [480, 832]], [[500, 692], [524, 718]], [[470, 860], [510, 866]]], 7);
|
|
97
|
+
K.dots(k, 470, 700, 540, 880, 22, 3);
|
|
98
|
+
}, 5);
|
|
99
|
+
g.strokeStyle = EDGE; g.lineWidth = 2.2; g.stroke(RIG.smooth([[566, 704], [612, 694], [640, 744], [632, 828], [596, 868], [562, 802]]));
|
|
100
|
+
// 前腿一件(白爪也是镂空)
|
|
101
|
+
[[588, 1], [636, 1]].forEach(([x]) => { const leg = RIG.taper([x, 750], [x, 892], 30, 34);
|
|
102
|
+
piece(g, leg, '#f0e4c4', (k) => { k.fillRect(x - 10, 770, 20, 104); }, 4); }); // 白腿白爪:几乎全镂空,只留皮边
|
|
103
|
+
g.strokeStyle = EDGE; g.lineWidth = 2; [[588], [636]].forEach(([x]) => { g.beginPath(); g.moveTo(x - 8, 892); g.lineTo(x - 8, 880); g.moveTo(x + 4, 892); g.lineTo(x + 4, 880); g.stroke(); });
|
|
104
|
+
g.restore();
|
|
105
|
+
// 头:单独一件,绕颈部铆钉点头(10fps 一顿一顿)
|
|
106
|
+
const nk = [588, 680], nod = 0.1 * Math.sin(ts * 6) + 0.05;
|
|
107
|
+
g.save(); g.translate(540, 905); g.rotate(sway); g.translate(-540, -905);
|
|
108
|
+
g.save(); g.translate(nk[0], nk[1]); g.rotate(nod); g.translate(-nk[0], -nk[1]);
|
|
109
|
+
piece(g, KT.head, ORG, (k) => {
|
|
110
|
+
k.beginPath(); k.ellipse(632, 650, 36, 24, 0, 0, 7); k.fill(); // 白口鼻镂空
|
|
111
|
+
K.lines(k, [[[585, 572], [590, 598]], [[605, 566], [607, 594]], [[625, 572], [622, 596]]], 6); // 头顶虎斑
|
|
112
|
+
k.beginPath(); k.moveTo(598, 612); k.quadraticCurveTo(612, 600, 626, 612); k.quadraticCurveTo(612, 620, 598, 612); k.fill(); // 眼:柳叶形镂空
|
|
113
|
+
k.beginPath(); k.moveTo(636, 610); k.quadraticCurveTo(650, 598, 664, 608); k.quadraticCurveTo(650, 618, 636, 610); k.fill();
|
|
114
|
+
K.clouds(k, 560, 620, 600, 680, 30);
|
|
115
|
+
}, 4);
|
|
116
|
+
g.strokeStyle = EDGE; g.lineWidth = 2; g.beginPath(); g.ellipse(632, 650, 36, 24, 0, 0, 7); g.stroke();
|
|
117
|
+
g.fillStyle = EDGE; g.beginPath(); g.arc(612, 610, 4, 0, 7); g.arc(650, 607, 4, 0, 7); g.fill(); g.beginPath(); g.arc(656, 640, 5, 0, 7); g.fill();
|
|
118
|
+
g.lineWidth = 1.8; [[660, 645, 712, 632], [660, 650, 715, 655]].forEach(([a, b, c2, d]) => { g.beginPath(); g.moveTo(a, b); g.lineTo(c2, d); g.stroke(); });
|
|
119
|
+
g.restore(); g.restore();
|
|
120
|
+
stamp(c, L, 7, 5);
|
|
121
|
+
const nk2 = rot(nk, [540, 905], sway); rivet(c, nk2[0], nk2[1]); segs.forEach(([, oo]) => { const q = rot(oo, [540, 905], sway); rivet(c, q[0], q[1], 5); });
|
|
122
|
+
rod(c, rot([520, 760], [540, 905], sway), [470, 1080]); rod(c, rot([600, 600], [540, 905], sway), [610, 1080]);
|
|
123
|
+
}
|
|
124
|
+
// ---------- 少女 ----------
|
|
125
|
+
{ const L = P.scratch('pyGirl'), g = L.getContext('2d'); g.reset();
|
|
126
|
+
const cupJ = 0.5 - 0.5 * Math.cos(clamp(ls / 0.85) * Math.PI); // 杆子端杯:10fps 步进
|
|
127
|
+
const G = RIG.girl({ cup: cupJ, hair: 'bun' });
|
|
128
|
+
const pivot = [1260, 930], sway = 0.012 * Math.sin(ts * 4.4);
|
|
129
|
+
g.save(); g.translate(...pivot); g.rotate(sway); g.translate(-pivot[0], -pivot[1]);
|
|
130
|
+
// 远侧袖(绿)+手
|
|
131
|
+
piece(g, G.farSleeve, GRN, (k) => K.clouds(k, 1220, 470, 1360, 630, 34));
|
|
132
|
+
piece(g, G.farHand, '#efe2c4', null, 3);
|
|
133
|
+
// 裙:绿长裙,竖刻褶线+下摆鱼子纹边
|
|
134
|
+
piece(g, G.skirt, GRN, (k) => {
|
|
135
|
+
K.lines(k, [[[1170, 700], [1150, 900]], [[1210, 690], [1196, 910]], [[1250, 680], [1244, 910]], [[1290, 680], [1292, 900]], [[1330, 700], [1340, 790]]], 3.5);
|
|
136
|
+
K.dots(k, 1110, 880, 1360, 920, 14, 3.2); K.flowers(k, 1380, 640, 1450, 760, 40, 4.5);
|
|
137
|
+
});
|
|
138
|
+
// 上衣:红袄,刻团花;领口/襟边一条黄色宽边
|
|
139
|
+
piece(g, G.torso, RED, (k) => K.flowers(k, 1300, 470, 1420, 610, 44, 5));
|
|
140
|
+
piece(g, RIG.smooth([[1290, 436], [1318, 430], [1316, 470], [1306, 540], [1300, 610], [1286, 610], [1284, 520]]), YEL, (k) => K.dots(k, 1286, 446, 1318, 600, 12, 2.5), 3);
|
|
141
|
+
// 头:发(琥珀色皮=金发,刻云纹)→ 空脸(脸整个镂空,只留皮边与五官线)→ 头茬(花钿、簪、步摇珠串)
|
|
142
|
+
const nk = G.A.neck, hTilt = 0.06 * Math.sin(ts * 3); // 头绕颈部铆钉微转
|
|
143
|
+
g.save(); g.translate(...nk); g.rotate(hTilt); g.translate(-nk[0], -nk[1]);
|
|
144
|
+
piece(g, G.hairBack, AMB, (k) => K.clouds(k, 1290, 300, 1420, 440, 30));
|
|
145
|
+
piece(g, G.bun, AMB, (k) => K.clouds(k, 1360, 340, 1420, 404, 22));
|
|
146
|
+
g.save(); g.globalCompositeOperation = 'destination-out'; g.fill(G.face); g.restore();
|
|
147
|
+
g.strokeStyle = EDGE; g.lineWidth = 3; g.stroke(G.face);
|
|
148
|
+
g.strokeStyle = AMB; g.lineWidth = 7; g.save(); g.clip(G.face); g.stroke(G.face); g.restore();
|
|
149
|
+
piece(g, G.bangs, AMB, (k) => K.clouds(k, 1250, 296, 1340, 360, 24));
|
|
150
|
+
// 五官:凤眼(细长挑梢)、柳叶眉、点唇——都是皮上留的线
|
|
151
|
+
const e = G.eye; g.strokeStyle = EDGE; g.lineWidth = 2.6; g.beginPath(); g.moveTo(e.x - 12, e.y + 1); g.quadraticCurveTo(e.x, e.y - 6, e.x + 16, e.y - 4); g.stroke();
|
|
152
|
+
g.lineWidth = 2; g.beginPath(); g.moveTo(e.x - 10, e.y + 2); g.quadraticCurveTo(e.x, e.y + 4, e.x + 12, e.y - 1); g.stroke();
|
|
153
|
+
g.fillStyle = EDGE; g.beginPath(); g.arc(e.x - 2, e.y - 1, 2.6, 0, 7); g.fill();
|
|
154
|
+
g.lineWidth = 2.4; g.stroke(G.brow);
|
|
155
|
+
g.fillStyle = RED; g.beginPath(); g.arc(G.lips[1][0] - 1, G.lips[1][1] + 2, 4, 0, 7); g.fill();
|
|
156
|
+
// 头茬:发顶一朵大花+两根簪+垂下的步摇
|
|
157
|
+
const ht = G.A.headTop; const flw = new Path2D(); for (let k = 0; k < 6; k++) { const a = k / 6 * Math.PI * 2; flw.moveTo(ht[0] + 20 + Math.cos(a) * 26, ht[1] + 6 + Math.sin(a) * 26); flw.arc(ht[0] + 20 + Math.cos(a) * 18, ht[1] + 6 + Math.sin(a) * 18, 12, 0, 7); }
|
|
158
|
+
piece(g, flw, RED, (k) => { k.beginPath(); k.arc(ht[0] + 20, ht[1] + 6, 7, 0, 7); k.fill(); }, 3);
|
|
159
|
+
g.strokeStyle = BLU; g.lineWidth = 5; g.beginPath(); g.moveTo(ht[0] + 40, ht[1] + 30); g.lineTo(ht[0] + 110, ht[1] - 10); g.stroke(); g.strokeStyle = EDGE; g.lineWidth = 1.6; g.stroke();
|
|
160
|
+
const sw = 0.4 * Math.sin(ts * 9); // 步摇:串珠随头动甩
|
|
161
|
+
g.fillStyle = YEL; g.strokeStyle = EDGE; g.lineWidth = 1.5; for (let k = 0; k < 4; k++) { const bx = ht[0] + 108 + Math.sin(sw) * k * 11, by = ht[1] - 4 + Math.cos(sw) * k * 11; g.beginPath(); g.arc(bx, by, 5, 0, 7); g.fill(); g.stroke(); }
|
|
162
|
+
g.restore();
|
|
163
|
+
// 近侧手臂:上臂/前臂/手三件刚体,铆钉相连;手腕松,会甩
|
|
164
|
+
const S = G.A.shoulder, E = G.A.elbow, Hd = G.A.hand;
|
|
165
|
+
piece(g, RIG.taper(S, E, 50, 42), RED, (k) => K.clouds(k, Math.min(S[0], E[0]) - 30, Math.min(S[1], E[1]) - 30, Math.max(S[0], E[0]) + 30, Math.max(S[1], E[1]) + 30, 30));
|
|
166
|
+
const Wr = [lerp(E[0], Hd[0], 0.8), lerp(E[1], Hd[1], 0.8)];
|
|
167
|
+
piece(g, RIG.taper(E, Wr, 44, 50), RED, (k) => K.flowers(k, Math.min(E[0], Wr[0]) - 30, Math.min(E[1], Wr[1]) - 30, Math.max(E[0], Wr[0]) + 30, Math.max(E[1], Wr[1]) + 30, 36, 4));
|
|
168
|
+
piece(g, RIG.taper([lerp(E[0], Hd[0], 0.66), lerp(E[1], Hd[1], 0.66)], Wr, 52, 58), YEL, (k) => K.dots(k, Wr[0] - 40, Wr[1] - 40, Wr[0] + 40, Wr[1] + 40, 10, 2.2), 3);
|
|
169
|
+
const dangle = 0.35 * Math.sin(ls * 11) * (1 - cupJ * 0.6);
|
|
170
|
+
g.save(); g.translate(...Wr); g.rotate(dangle); g.translate(-Wr[0], -Wr[1]);
|
|
171
|
+
const hand = new Path2D(); hand.ellipse(Hd[0], Hd[1], 17, 13, Math.atan2(Hd[1] - E[1], Hd[0] - E[0]), 0, 7);
|
|
172
|
+
g.save(); g.globalCompositeOperation = 'destination-out'; g.fill(hand); g.restore(); g.strokeStyle = EDGE; g.lineWidth = 2.6; g.stroke(hand); // 手也是空的
|
|
173
|
+
const cp = G.cup, cup = RIG.smooth([[cp.x - 24, cp.y], [cp.x - 20, cp.y + 34], [cp.x, cp.y + 42], [cp.x + 20, cp.y + 34], [cp.x + 24, cp.y]]);
|
|
174
|
+
piece(g, tr(cup, [cp.x, cp.y], cp.tilt), BLU, (k) => K.flowers(k, cp.x - 30, cp.y, cp.x + 30, cp.y + 44, 24, 3.5), 3);
|
|
175
|
+
g.restore();
|
|
176
|
+
g.restore();
|
|
177
|
+
stamp(c, L, 8, 5);
|
|
178
|
+
// 铆钉与杆:颈、肩、肘、腕;主杆在颈,手杆在腕
|
|
179
|
+
const R = p => rot(p, pivot, sway);
|
|
180
|
+
[nk, S, E, Wr].forEach(p => { const q = R(p); rivet(c, q[0], q[1]); });
|
|
181
|
+
rod(c, R(nk), [1420, 1080]); rod(c, R(Wr), [1180, 1080]); rod(c, [1170, 615], [1060, 1080]);
|
|
182
|
+
// 热气:皮影里用一缕刻出来的云纹皮条表示,跟着杯子飘
|
|
183
|
+
const sx = G.cup.x, sy = G.cup.y - 20;
|
|
184
|
+
c.save(); c.globalCompositeOperation = 'multiply'; c.strokeStyle = 'rgba(160,110,60,.55)'; c.lineWidth = 5; c.lineCap = 'round';
|
|
185
|
+
for (let k = 0; k < 2; k++) { c.beginPath(); for (let i = 0; i <= 14; i++) { const q = i / 14; const x = sx + k * 14 + Math.sin(q * 6 - ts * 6 + k) * 8, y = sy - q * 60; i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); } c.restore();
|
|
186
|
+
}
|
|
187
|
+
// 木框(影窗边)
|
|
188
|
+
c.drawImage(P.cached('py_frame', W, H, (g) => {
|
|
189
|
+
g.fillStyle = '#2a140a'; g.beginPath(); g.rect(0, 0, W, H); g.rect(28, 24, W - 56, H - 48); g.fill('evenodd');
|
|
190
|
+
g.strokeStyle = '#a8742e'; g.lineWidth = 3; g.strokeRect(34, 30, W - 68, H - 60);
|
|
191
|
+
const vg = g.createRadialGradient(960, 500, 500, 960, 540, 1150); vg.addColorStop(0, 'rgba(40,15,0,0)'); vg.addColorStop(1, 'rgba(40,15,0,.45)'); g.fillStyle = vg; g.fillRect(28, 24, W - 56, H - 48);
|
|
192
|
+
}), 0, 0);
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
})();
|