@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,265 @@
|
|
|
1
|
+
// 1912 分析立体主义(毕加索/布拉克)——纯代码。
|
|
2
|
+
// 管线:①缓存底版:赭灰米色「透明叠面」(每面一条明→暗线性渐变,alpha 0.35–0.7 叠出层次)
|
|
3
|
+
// + 点画/排线肌理面 + 布拉克式短划砂纹 + 贯穿的细直线(passage)+ 仿木纹(桌、木板)+ 藤编椅背
|
|
4
|
+
// → ②会动的:窗格碎面各自滑动(窗景缓存成一张大图,四格各自错位取景)、CAFÉ 字母跳动换位(12fps 卡点)、报纸微颤
|
|
5
|
+
// → ③角色:RIG 平涂进离屏层 → 自写切面器 facetize(抖动三角网,取色+随机明暗+两端渐变,三角不裁剪 = 轮廓被切成直边)
|
|
6
|
+
// → 补毕加索式五官(侧脸+正面杏眼、半边脸蓝灰面)、裙摆放射切线、直线外轮廓(端点外伸)
|
|
7
|
+
// → ④最上层再压几片低透明度的叠面,让人和猫「嵌」进空间
|
|
8
|
+
SCENES['11_cubism'] = (() => {
|
|
9
|
+
const W = 1920, H = 1080, { clamp, lerp } = U, P = PAINT, TAU = Math.PI * 2;
|
|
10
|
+
const C = { paper: '#d6c8ac', cream: '#ece3cf', ochre: '#b8975f', brown: '#6e5538', dbrown: '#3a2c1e', grey: '#a8a395', bgrey: '#8e9eab', dgrey: '#5a5852', ink: '#2e241a' };
|
|
11
|
+
const H2 = P.hex;
|
|
12
|
+
// 要 getImageData 的离屏层必须一开始就声明 willReadFrequently:否则第一帧走 GPU、读回后 Chrome 改走 CPU,抗锯齿不同 → 第一帧与之后不一致(不确定性)
|
|
13
|
+
const LAYERS = {}; const layer = (k) => { if (!LAYERS[k]) { const cv = document.createElement('canvas'); cv.width = W; cv.height = H; cv.getContext('2d', { willReadFrequently: true }); LAYERS[k] = cv; } return LAYERS[k]; };
|
|
14
|
+
const poly = (g, pts) => { g.beginPath(); pts.forEach((p, i) => i ? g.lineTo(p[0], p[1]) : g.moveTo(p[0], p[1])); g.closePath(); };
|
|
15
|
+
// 一个「切面」:沿某方向从亮到暗的渐变(立体主义的明暗切面)
|
|
16
|
+
const plane = (g, pts, col, alpha, r, dark = 0.4, light = 0.25) => {
|
|
17
|
+
const c0 = Array.isArray(col) ? col : H2(col);
|
|
18
|
+
let a = pts[0], b = pts[(r() * pts.length) | 0]; if (a === b) b = pts[(pts.length / 2) | 0];
|
|
19
|
+
const gr = g.createLinearGradient(a[0], a[1], b[0], b[1]);
|
|
20
|
+
gr.addColorStop(0, P.rgb(P.mix(c0, [250, 244, 228], light), alpha)); gr.addColorStop(0.55, P.rgb(c0, alpha)); gr.addColorStop(1, P.rgb(P.mix(c0, [45, 35, 25], dark), alpha));
|
|
21
|
+
g.fillStyle = gr; poly(g, pts); g.fill();
|
|
22
|
+
};
|
|
23
|
+
// 仿木纹(布拉克用木纹梳画的假木头)
|
|
24
|
+
const wood = (g, x, y, w, h, vertical, seed, base = '#b8874a', dark = '#7a5228') => {
|
|
25
|
+
const r = U.rng(seed); g.save(); g.beginPath(); g.rect(x, y, w, h); g.clip(); g.fillStyle = base; g.fillRect(x, y, w, h);
|
|
26
|
+
g.strokeStyle = dark; const L = vertical ? h : w, S = vertical ? w : h, kx = 0.35 + r() * 0.3, ky = 0.3 + r() * 0.4;
|
|
27
|
+
for (let i = 0; i < S / 6; i++) { g.lineWidth = 0.8 + r() * 1.6; g.globalAlpha = 0.35 + r() * 0.4; g.beginPath();
|
|
28
|
+
for (let s = 0; s <= L; s += 6) { const u = s / L, off = i * 6 + Math.sin(u * 7 + i * 0.4) * 3 + 14 * Math.exp(-((u - kx) ** 2) / 0.01) * Math.sign(i * 6 - S * ky);
|
|
29
|
+
const X = vertical ? x + off : x + s, Y = vertical ? y + s : y + off; s ? g.lineTo(X, Y) : g.moveTo(X, Y); } g.stroke(); }
|
|
30
|
+
g.globalAlpha = 1; const kX = vertical ? x + S * ky : x + L * kx, kY = vertical ? y + L * kx : y + S * ky;
|
|
31
|
+
for (let k = 0; k < 4; k++) { g.lineWidth = 1.6; g.beginPath(); g.ellipse(kX, kY, (vertical ? 6 : 16) + k * 5, (vertical ? 16 : 6) + k * 4, 0, 0, TAU); g.stroke(); }
|
|
32
|
+
g.fillStyle = dark; g.beginPath(); g.ellipse(kX, kY, vertical ? 4 : 10, vertical ? 10 : 4, 0, 0, TAU); g.fill(); g.restore();
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// ---------- ① 底版 ----------
|
|
36
|
+
const bg = () => P.cached('cb_bg', W, H, (g) => {
|
|
37
|
+
const r = U.rng(1912);
|
|
38
|
+
g.fillStyle = C.paper; g.fillRect(0, 0, W, H);
|
|
39
|
+
// 手工排的大叠面(参照原片构图)
|
|
40
|
+
const big = [
|
|
41
|
+
[[[0, 0], [330, 0], [180, 330], [0, 260]], C.grey, .6], [[[60, 0], [560, 0], [340, 110]], C.cream, .6], [[[0, 280], [200, 330], [110, 1080], [0, 1080]], C.dgrey, .45],
|
|
42
|
+
[[[160, 520], [350, 600], [280, 860], [120, 780]], C.ochre, .5], [[[820, 0], [1010, 0], [930, 330]], C.cream, .55], [[[880, 120], [1220, 40], [1100, 420], [860, 380]], C.bgrey, .5],
|
|
43
|
+
[[[1050, 0], [1500, 0], [1320, 240], [1180, 210]], C.grey, .45], [[[1500, 0], [1920, 0], [1920, 330], [1640, 260]], C.cream, .35], [[[1290, 170], [1720, 310], [1610, 620], [1240, 450]], C.grey, .45],
|
|
44
|
+
[[[1700, 340], [1920, 290], [1920, 900], [1760, 880]], C.ochre, .45], [[[840, 380], [1150, 320], [1210, 560], [880, 600]], C.grey, .5], [[[350, 560], [820, 600], [800, 900], [300, 930]], C.cream, .45],
|
|
45
|
+
[[[690, 620], [860, 560], [910, 1080], [630, 1080]], C.brown, .4], [[[1250, 600], [1560, 640], [1500, 1080], [1200, 1080]], C.dgrey, .35], [[[0, 640], [380, 560], [420, 1080], [0, 1080]], C.ochre, .35],
|
|
46
|
+
[[[560, 0], [880, 0], [820, 120], [600, 90]], C.ochre, .45], [[[1560, 620], [1920, 560], [1920, 1080], [1500, 1080]], C.ochre, .4], [[[300, 70], [520, 40], [470, 300], [280, 260]], C.dgrey, .3],
|
|
47
|
+
];
|
|
48
|
+
big.forEach(([pts, col, a]) => plane(g, pts, col, a, r));
|
|
49
|
+
// 桌下放射扇面
|
|
50
|
+
for (let i = 0; i < 9; i++) { const a0 = -Math.PI + 0.25 + i * 0.3, a1 = a0 + 0.16; plane(g, [[1000, 1090], [1000 + Math.cos(a0) * 620, 1090 + Math.sin(a0) * 520], [1000 + Math.cos(a1) * 620, 1090 + Math.sin(a1) * 520]], i % 2 ? C.cream : C.brown, 0.3, r, 0.5, 0.3); }
|
|
51
|
+
// 随机小切面
|
|
52
|
+
for (let i = 0; i < 70; i++) { const cx = r() * W, cy = r() * H, s = 60 + r() * 200, a0 = r() * TAU, n = r() < 0.6 ? 3 : 4;
|
|
53
|
+
const pts = []; for (let k = 0; k < n; k++) { const a = a0 + k / n * TAU + (r() - .5) * 0.8; pts.push([cx + Math.cos(a) * s * (0.5 + r() * 0.6), cy + Math.sin(a) * s * (0.5 + r() * 0.6)]); }
|
|
54
|
+
if (cx > 1480 && cy < 230) continue;
|
|
55
|
+
plane(g, pts, [C.cream, C.grey, C.ochre, C.bgrey, C.brown, C.dgrey][(r() * 6) | 0], 0.18 + r() * 0.3, r); }
|
|
56
|
+
// 点画面、排线面
|
|
57
|
+
[[[930, 430], [1080, 380], [1060, 520], [960, 560]], [[1540, 420], [1700, 380], [1660, 520]], [[130, 380], [260, 420], [210, 560]], [[720, 700], [820, 690], [800, 820]]].forEach((pts, k) => {
|
|
58
|
+
g.save(); poly(g, pts); g.clip(); g.fillStyle = 'rgba(40,30,20,.45)'; for (let y = 0; y < H; y += 9) for (let x = (y / 9 % 2) * 4.5; x < W; x += 9) { if (x < pts[0][0] - 200 || x > pts[0][0] + 260 || y < pts[0][1] - 200 || y > pts[0][1] + 260) continue; g.beginPath(); g.arc(x, y, 1.6, 0, TAU); g.fill(); } g.restore(); });
|
|
59
|
+
[[[1040, 470], [1200, 400], [1220, 520], [1080, 560]], [[40, 300], [110, 280], [80, 640], [20, 660]], [[1230, 690], [1330, 660], [1310, 840]], [[600, 160], [700, 130], [690, 300]]].forEach((pts, k) => {
|
|
60
|
+
g.save(); poly(g, pts); g.clip(); g.strokeStyle = 'rgba(50,38,26,.45)'; g.lineWidth = 1.2; g.beginPath(); for (let d = -1200; d < 1200; d += 7) { g.moveTo(pts[0][0] + d, pts[0][1] - 300); g.lineTo(pts[0][0] + d + (k % 2 ? 300 : -120), pts[0][1] + 300); } g.stroke(); g.restore(); });
|
|
61
|
+
// 左侧竖排线(纸页)
|
|
62
|
+
g.save(); poly(g, [[0, 280], [100, 290], [70, 800], [0, 820]]); g.clip(); g.fillStyle = 'rgba(236,227,207,.7)'; g.fillRect(0, 280, 110, 560); g.strokeStyle = 'rgba(60,45,30,.4)'; g.lineWidth = 1; g.beginPath(); for (let y = 290; y < 820; y += 8) { g.moveTo(0, y); g.lineTo(110, y - 6); } g.stroke(); g.restore();
|
|
63
|
+
// 右侧木板(布拉克的假木纹)
|
|
64
|
+
g.save(); g.translate(1615, 590); g.rotate(-0.04); wood(g, -66, -205, 130, 410, true, 7, '#c4925a', '#7a4f26'); g.strokeStyle = C.ink; g.lineWidth = 2; g.strokeRect(-66, -205, 130, 410); g.restore();
|
|
65
|
+
// 藤编椅背+椅框
|
|
66
|
+
g.save(); poly(g, [[1484, 492], [1588, 470], [1588, 692], [1486, 700]]); g.clip(); g.fillStyle = '#c9a466'; g.fillRect(1480, 460, 120, 250);
|
|
67
|
+
g.fillStyle = 'rgba(70,46,24,.85)'; for (let y = 470; y < 705; y += 10) for (let x = 1480 + (y / 10 % 2) * 5; x < 1600; x += 10) { g.beginPath(); g.arc(x, y, 2.6, 0, TAU); g.fill(); }
|
|
68
|
+
g.strokeStyle = 'rgba(240,220,170,.5)'; g.lineWidth = 1.2; g.beginPath(); for (let d = -260; d < 260; d += 10) { g.moveTo(1480 + d, 460); g.lineTo(1480 + d + 250, 710); g.moveTo(1600 - d, 460); g.lineTo(1600 - d - 250, 710); } g.stroke(); g.restore();
|
|
69
|
+
g.strokeStyle = C.dbrown; g.lineWidth = 9; g.lineCap = 'round'; g.beginPath(); g.moveTo(1478, 900); g.lineTo(1478, 470); g.quadraticCurveTo(1530, 455, 1590, 486); g.lineTo(1590, 710); g.stroke();
|
|
70
|
+
g.lineWidth = 7; g.beginPath(); g.moveTo(1380, 720); g.lineTo(1488, 718); g.moveTo(1384, 720); g.lineTo(1384, 900); g.stroke();
|
|
71
|
+
// 桌:木纹桌面+深色裙板+抽屉+独脚+菱形
|
|
72
|
+
g.save(); poly(g, [[818, 600], [1202, 596], [1196, 644], [824, 646]]); g.clip(); wood(g, 810, 590, 400, 60, false, 3, '#b98b52', '#6e4a24'); g.restore();
|
|
73
|
+
g.strokeStyle = C.ink; g.lineWidth = 2.5; poly(g, [[818, 600], [1202, 596], [1196, 644], [824, 646]]); g.stroke();
|
|
74
|
+
plane(g, [[826, 646], [1194, 644], [1188, 702], [834, 704]], '#5a4128', 0.95, r, 0.35, 0.15);
|
|
75
|
+
g.fillStyle = '#8a6a44'; g.fillRect(952, 668, 118, 26); g.strokeStyle = C.ink; g.lineWidth = 2; g.strokeRect(952, 668, 118, 26); g.fillStyle = '#efe6d0'; g.beginPath(); g.arc(1011, 681, 5, 0, TAU); g.fill();
|
|
76
|
+
plane(g, [[988, 704], [1018, 704], [1022, 892], [984, 892]], '#8a6338', 0.95, r, 0.45, 0.25);
|
|
77
|
+
g.fillStyle = '#d8c39a'; poly(g, [[1004, 740], [1036, 780], [1004, 820], [972, 780]]); g.fill(); g.strokeStyle = C.ink; g.lineWidth = 2; g.stroke();
|
|
78
|
+
g.fillStyle = '#3a2c1e'; poly(g, [[1004, 780], [1036, 780], [1004, 820]]); g.fill();
|
|
79
|
+
plane(g, [[900, 860], [1110, 860], [1150, 900], [860, 900]], C.brown, 0.6, r);
|
|
80
|
+
// 碟(杯子举走后仍在桌上)
|
|
81
|
+
g.fillStyle = '#e8e0cc'; g.beginPath(); g.ellipse(990, 636, 54, 9, 0, 0, TAU); g.fill(); g.strokeStyle = C.ink; g.lineWidth = 2; g.stroke();
|
|
82
|
+
// 窗右侧透明「盒子」面与透视线
|
|
83
|
+
plane(g, [[815, 110], [892, 140], [890, 545], [815, 565]], C.bgrey, 0.3, r);
|
|
84
|
+
g.strokeStyle = 'rgba(46,36,26,.7)'; g.lineWidth = 1.4; g.beginPath(); g.moveTo(815, 105); g.lineTo(892, 140); g.lineTo(890, 545); g.lineTo(815, 565); g.stroke();
|
|
85
|
+
// 布拉克短划砂纹(亮暗两种)
|
|
86
|
+
for (let i = 0; i < 9000; i++) { const x = r() * W, y = r() * H; g.fillStyle = r() < 0.55 ? 'rgba(246,238,220,.28)' : 'rgba(60,45,30,.16)'; g.fillRect(x, y, 3 + r() * 5, 1.5); }
|
|
87
|
+
// 贯穿的细直线
|
|
88
|
+
g.strokeStyle = 'rgba(40,30,20,.55)'; g.lineWidth = 1.3;
|
|
89
|
+
for (let i = 0; i < 46; i++) { const x = r() * W, y = r() * H, a = r() * Math.PI, L = 120 + r() * 520; if (x > 1460 && y < 240) continue; g.beginPath(); g.moveTo(x, y); g.lineTo(x + Math.cos(a) * L, y + Math.sin(a) * L); g.stroke(); }
|
|
90
|
+
g.drawImage(P.grain('cb', 0.05, [50, 40, 30], 0.18), 0, 0);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// 窗景(缓存一张比窗大的图,四格各自错位取景)
|
|
94
|
+
const winScene = () => P.cached('cb_win', 560, 540, (g) => {
|
|
95
|
+
const r = U.rng(77); g.fillStyle = '#9fb0bd'; g.fillRect(0, 0, 560, 540);
|
|
96
|
+
const ox = 300, oy = 60; // 场景坐标 = 屏幕坐标 - (ox, oy)
|
|
97
|
+
for (let i = 0; i < 55; i++) { const cx = r() * 560, cy = r() * 540, s = 40 + r() * 170, a0 = r() * TAU; const pts = [0, 1, 2].map(k => { const a = a0 + k * 2.1 + (r() - .5) * 0.6; return [cx + Math.cos(a) * s, cy + Math.sin(a) * s * (0.4 + r())]; });
|
|
98
|
+
plane(g, pts, ['#c9d3da', '#8fa3b3', '#e4e6e2', '#6f8597', '#b4c1cb', '#5f7385'][(r() * 6) | 0], 0.45 + r() * 0.4, r, 0.45, 0.35); }
|
|
99
|
+
// 斜向长条光束
|
|
100
|
+
[[150, 0, 60], [230, 0, 30], [80, 120, 40]].forEach(([x, y, w]) => plane(g, [[x, y], [x + w, y], [x + w + 300, y + 420], [x + 300, y + 420]], '#e8ecec', 0.55, r, 0.3, 0.5));
|
|
101
|
+
// 埃菲尔铁塔(右侧,碎成两段)
|
|
102
|
+
const tx = 690 - ox; g.fillStyle = '#4a4c50'; poly(g, [[tx - 6, 120 - oy], [tx + 6, 120 - oy], [tx + 14, 330 - oy], [tx - 14, 330 - oy]]); g.fill();
|
|
103
|
+
poly(g, [[tx - 22, 330 - oy], [tx + 22, 330 - oy], [tx + 70, 560 - oy], [tx + 44, 560 - oy], [tx, 470 - oy], [tx - 44, 560 - oy], [tx - 70, 560 - oy]]); g.fill();
|
|
104
|
+
g.strokeStyle = 'rgba(220,226,230,.6)'; g.lineWidth = 1; g.beginPath(); for (let y = 340; y < 560; y += 9) { const w = 22 + (y - 330) * 0.21; g.moveTo(tx - w, y - oy); g.lineTo(tx + w, y - oy + 9); g.moveTo(tx + w, y - oy); g.lineTo(tx - w, y - oy + 9); } g.stroke();
|
|
105
|
+
g.fillStyle = '#4a4c50'; g.fillRect(tx - 46, 452 - oy, 92, 9);
|
|
106
|
+
// 白色海鸥弧
|
|
107
|
+
g.strokeStyle = '#f4f2ec'; g.lineWidth = 4; g.lineCap = 'round'; [[420, 200], [455, 190], [400, 390], [440, 380]].forEach(([x, y]) => { g.beginPath(); g.arc(x - ox, y - oy + 18, 18, -2.6, -0.5); g.stroke(); });
|
|
108
|
+
g.drawImage(P.grain('cbw', 0.04, [255, 255, 255], 0.2), 0, 0, 560, 540, 0, 0, 560, 540);
|
|
109
|
+
});
|
|
110
|
+
function windowPanes(c, lt, t) {
|
|
111
|
+
const panes = [[362, 120, 218, 212], [588, 120, 214, 212], [362, 340, 218, 212], [588, 340, 214, 212]];
|
|
112
|
+
const st = P.boilSeed(t, 10);
|
|
113
|
+
panes.forEach(([x, y, w, h], i) => {
|
|
114
|
+
const dx = Math.sin(lt * 9 + i * 1.7) * 18 + (U.hash(st, i) - 0.5) * 6, dy = Math.cos(lt * 7 + i) * 8;
|
|
115
|
+
c.save(); c.beginPath(); c.rect(x, y, w, h); c.clip(); c.drawImage(winScene(), 300 - 30 + dx, 60 - 30 + dy, 560, 540);
|
|
116
|
+
// 每格一片自己滑动的碎面
|
|
117
|
+
const sx = x + ((lt * 260 + i * 70) % (w + 120)) - 60;
|
|
118
|
+
c.fillStyle = 'rgba(232,236,236,.5)'; poly(c, [[sx, y + 20 + i * 12], [sx + 70, y + 60], [sx + 10, y + 150]]); c.fill(); c.strokeStyle = 'rgba(40,40,46,.6)'; c.lineWidth = 1.5; c.stroke();
|
|
119
|
+
c.restore();
|
|
120
|
+
});
|
|
121
|
+
c.strokeStyle = '#4a3420'; c.lineWidth = 15; c.strokeRect(352, 112, 457, 448); c.lineWidth = 10; c.beginPath(); c.moveTo(584, 112); c.lineTo(584, 560); c.moveTo(352, 336); c.lineTo(809, 336); c.stroke();
|
|
122
|
+
c.strokeStyle = C.ink; c.lineWidth = 2; c.strokeRect(345, 105, 471, 462);
|
|
123
|
+
c.fillStyle = '#8a6a44'; poly(c, [[330, 566], [826, 566], [818, 584], [338, 584]]); c.fill(); c.stroke();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ---------- CAFÉ:模版字,12fps 跳动、偶尔换位 ----------
|
|
127
|
+
const ORDERS = ['CAFÉ', 'CAFÉ', 'ACFÉ', 'CAFÉ', 'CAÉF', 'CFAÉ', 'CAFÉ', 'CAFÉ', 'ACFÉ', 'CAFÉ'];
|
|
128
|
+
function cafe(c, lt) {
|
|
129
|
+
const L = P.scratch('cbCafe'), g = L.getContext('2d'); g.clearRect(0, 0, 420, 170);
|
|
130
|
+
const st = Math.floor(Math.max(0, lt) * 12), order = ORDERS[st % ORDERS.length];
|
|
131
|
+
const slots = [20, 108, 196, 284];
|
|
132
|
+
g.font = '104px "AlfaSlabOne-400"'; g.textBaseline = 'alphabetic'; g.fillStyle = '#3b2e22';
|
|
133
|
+
[...order].forEach((ch, i) => { const h = U.hash(st, i * 3 + 1), jy = (h - 0.5) * 26 - (i === 1 ? 8 : 0), rot = (U.hash(st, i * 5 + 2) - 0.5) * 0.22;
|
|
134
|
+
g.save(); g.translate(slots[i] + 40, 128 + jy); g.rotate(rot + (i === 2 ? -0.06 : 0)); g.fillText(ch, -40, 0); g.restore(); });
|
|
135
|
+
// 模版字的断口
|
|
136
|
+
g.globalCompositeOperation = 'destination-out'; g.fillStyle = '#000';
|
|
137
|
+
[[44, 60], [132, 92], [220, 70], [306, 84]].forEach(([x, y], i) => { g.fillRect(x - 40 + (i * 7) % 20, y, 90, 6); g.fillRect(x + 4, 40, 5, 100); });
|
|
138
|
+
g.globalCompositeOperation = 'source-over';
|
|
139
|
+
c.drawImage(L, 0, 0, 420, 170, 868, 120, 420, 170);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ---------- 报纸 JOU(缓存成小图,逐帧微颤) ----------
|
|
143
|
+
const paper = () => P.cached('cb_jou', 300, 190, (g) => {
|
|
144
|
+
g.fillStyle = '#ebe3cc'; g.fillRect(0, 0, 300, 190); g.strokeStyle = 'rgba(60,45,30,.6)'; g.lineWidth = 1; g.strokeRect(0.5, 0.5, 299, 189);
|
|
145
|
+
g.fillStyle = '#2b2219'; g.font = '84px "LibreBaskerville-700"'; g.textBaseline = 'alphabetic'; g.fillText('JOU', 16, 82);
|
|
146
|
+
g.fillRect(12, 94, 276, 3); g.font = '12px "LibreBaskerville-700"'; g.fillText('LA BATAILLE', 14, 118); g.fillText("S'EST ENGAGÉ UN CHAT", 112, 118); g.fillRect(12, 126, 276, 1.5);
|
|
147
|
+
g.fillStyle = 'rgba(50,40,30,.55)'; for (let col = 0; col < 3; col++) for (let y = 136; y < 185; y += 5) g.fillRect(14 + col * 94, y, 84 - ((y * 7 + col * 13) % 23), 2);
|
|
148
|
+
g.strokeStyle = 'rgba(40,30,20,.5)'; g.beginPath(); g.moveTo(106, 128); g.lineTo(106, 186); g.moveTo(200, 128); g.lineTo(200, 186); g.stroke();
|
|
149
|
+
});
|
|
150
|
+
function news(c, lt, t) {
|
|
151
|
+
const st = P.boilSeed(t, 8), a = -0.12 + (U.hash(st, 9) - 0.5) * 0.025, dx = (U.hash(st, 4) - 0.5) * 3;
|
|
152
|
+
c.save(); c.translate(205 + dx, 912); c.rotate(a); c.drawImage(paper(), -150, -95);
|
|
153
|
+
// 翘起的折角(随帧翻动)
|
|
154
|
+
const f = 18 + Math.sin(lt * 20) * 6; c.fillStyle = '#d8ceb4'; poly(c, [[150, -95], [150 - f, -95], [150, -95 + f]]); c.fill(); c.strokeStyle = 'rgba(40,30,20,.6)'; c.stroke();
|
|
155
|
+
c.restore();
|
|
156
|
+
// 报纸下的支脚线(原片里的两根斜线)
|
|
157
|
+
c.strokeStyle = 'rgba(40,30,20,.7)'; c.lineWidth = 2; c.beginPath(); c.moveTo(320, 960); c.lineTo(352, 1010); c.moveTo(80, 1010); c.lineTo(60, 1060); c.stroke();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ---------- 切面器:抖动三角网,三角不裁剪(轮廓被切成直边) ----------
|
|
161
|
+
function facetize(g, src, box, cell, seed, t, { boilFrac = 0.25, alpha = 0.95, edge = 'rgba(40,30,20,.32)', light = 0.2, dark = 0.32, minA = 120, only = null } = {}) {
|
|
162
|
+
const [x0, y0, x1, y1] = box, w = x1 - x0, h = y1 - y0, d = src.getContext('2d').getImageData(x0, y0, w, h).data;
|
|
163
|
+
const nx = Math.ceil(w / cell), ny = Math.ceil(h / cell), r = U.rng(seed), pts = [];
|
|
164
|
+
const st = P.boilSeed(t, 8);
|
|
165
|
+
for (let j = 0; j <= ny; j++) for (let i = 0; i <= nx; i++) { const k = j * (nx + 1) + i; pts.push([x0 + i * cell + (r() - .5) * cell * 0.85 + (U.hash(k, st * 3 + seed) - .5) * cell * 0.16, y0 + j * cell + (r() - .5) * cell * 0.85 + (U.hash(k, st * 5 + seed) - .5) * cell * 0.16]); }
|
|
166
|
+
const at = (x, y) => { const ix = clamp(Math.round(x - x0), 0, w - 1), iy = clamp(Math.round(y - y0), 0, h - 1), k = (iy * w + ix) * 4; return [d[k], d[k + 1], d[k + 2], d[k + 3]]; };
|
|
167
|
+
g.lineJoin = 'round';
|
|
168
|
+
for (let j = 0; j < ny; j++) for (let i = 0; i < nx; i++) {
|
|
169
|
+
const A = pts[j * (nx + 1) + i], B = pts[j * (nx + 1) + i + 1], Cc = pts[(j + 1) * (nx + 1) + i], D = pts[(j + 1) * (nx + 1) + i + 1];
|
|
170
|
+
const tris = r() < 0.5 ? [[A, B, D], [A, D, Cc]] : [[A, B, Cc], [B, D, Cc]];
|
|
171
|
+
tris.forEach((tri, k) => {
|
|
172
|
+
const cx = (tri[0][0] + tri[1][0] + tri[2][0]) / 3, cy = (tri[0][1] + tri[1][1] + tri[2][1]) / 3, s = at(cx, cy);
|
|
173
|
+
const shadeR = r(); if (s[3] < minA || (only && !only(s))) return;
|
|
174
|
+
const flick = U.hash(i * 31 + j, k + st * 7) < boilFrac ? (U.hash(i, j + st) - 0.5) * 0.25 : 0;
|
|
175
|
+
const sh = 0.8 + shadeR * 0.38 + flick, col = [s[0] * sh, s[1] * sh, s[2] * sh];
|
|
176
|
+
const gr = g.createLinearGradient(tri[0][0], tri[0][1], tri[2][0], tri[2][1]);
|
|
177
|
+
gr.addColorStop(0, P.rgb(P.mix(col, [250, 244, 228], light), alpha)); gr.addColorStop(1, P.rgb(P.mix(col, [40, 32, 24], dark), alpha));
|
|
178
|
+
g.fillStyle = gr; poly(g, tri); g.fill(); g.strokeStyle = edge; g.lineWidth = 1; g.stroke();
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// 直线外轮廓:每段两端各外伸 ext(立体派的线常常画出界)
|
|
183
|
+
const segs = (g, pts, ext = 16, col = 'rgba(40,30,20,.72)', w = 2.2) => { g.strokeStyle = col; g.lineWidth = w; g.lineCap = 'round'; g.beginPath();
|
|
184
|
+
for (let i = 0; i < pts.length - 1; i++) { const a = pts[i], b = pts[i + 1], L = Math.hypot(b[0] - a[0], b[1] - a[1]) || 1, ux = (b[0] - a[0]) / L, uy = (b[1] - a[1]) / L;
|
|
185
|
+
g.moveTo(a[0] - ux * ext, a[1] - uy * ext); g.lineTo(b[0] + ux * ext, b[1] + uy * ext); } g.stroke(); };
|
|
186
|
+
|
|
187
|
+
const girlPal = { skin: '#e2cfae', hair: '#c9a256', hairLine: '#8a6a2e', dress: '#6a7a8b', sleeve: '#3c4857', fold: null, apron: '#c9cbc4', cuff: '#d9d4c4', shoe: '#2e241a', cheek: null, lip: '#a8443a', iris: '#2b2219', line: C.ink, lw: 0, mode: 'fill', cupBody: '#e6e2d8', cupRim: '#2a1e14' };
|
|
188
|
+
const catPal = { orange: '#c8773a', white: '#e8dfc9', stripe: '#7e4420', eye: '#c9a43a', line: C.ink, lw: 0, earInner: '#d79a88' };
|
|
189
|
+
|
|
190
|
+
function girl(c, G, lt, t, ch) {
|
|
191
|
+
const L = layer('girl'), g = L.getContext('2d'); g.reset(); // reset 清像素+清状态(lineJoin/lineCap 等会从上一帧残留,导致第一帧与之后不一致)
|
|
192
|
+
RIG.drawGirl(g, G, girlPal);
|
|
193
|
+
// 金发再加一条垂到背后的辫子(切面后变成三角发片)
|
|
194
|
+
g.fillStyle = '#c9a256'; g.beginPath(); const a = G.A; g.moveTo(a.nape[0] - 8, a.nape[1] - 40); g.lineTo(a.nape[0] + 52, a.nape[1] + 10); g.lineTo(a.back[0] + 36, a.back[1] + 70); g.lineTo(a.back[0] + 6, a.back[1] + 92); g.lineTo(a.nape[0] - 6, a.nape[1] + 30); g.closePath(); g.fill();
|
|
195
|
+
facetize(g, L, [1060, 230, 1540, 945], 58, 19, t, { boilFrac: 0.2, edge: 'rgba(40,30,20,.22)', light: 0.28, dark: 0.4 });
|
|
196
|
+
// 发区再细切一遍(更多金色三角)
|
|
197
|
+
g.save(); g.beginPath(); g.arc(a.headC[0] + 30, a.headC[1] - 10, 95, 0, TAU); g.rect(a.back[0] - 60, a.nape[1] - 30, 110, 260); g.clip();
|
|
198
|
+
facetize(g, L, [1240, 250, 1500, 640], 26, 23, t, { boilFrac: 0.3, alpha: 0.85, light: 0.3, minA: 200, only: s => s[0] > 150 && s[2] < 130 && s[0] - s[2] > 60 }); g.restore();
|
|
199
|
+
// 裙摆:从腰部放射的切线+明暗交替楔面
|
|
200
|
+
g.save(); g.clip(G.skirt); const O = [a.waist[0] - 40, a.waist[1] + 10];
|
|
201
|
+
[[1100, 930], [1150, 935], [1200, 935], [1250, 935], [1300, 935], [1350, 930], [1400, 900], [1450, 780]].forEach((p, i, arr) => { if (i < arr.length - 1) { g.fillStyle = i % 2 ? 'rgba(235,228,210,.22)' : 'rgba(30,36,46,.28)'; poly(g, [O, p, arr[i + 1]]); g.fill(); } g.strokeStyle = 'rgba(30,24,18,.42)'; g.lineWidth = 1.8; g.beginPath(); g.moveTo(...O); g.lineTo(...p); g.stroke(); });
|
|
202
|
+
g.restore();
|
|
203
|
+
// 毕加索式脸:前半张脸压一块蓝灰面,正面杏眼,红唇
|
|
204
|
+
// 脸重铺:肤色两块明暗面(切面会把发色带进脸里,这里还原)
|
|
205
|
+
g.save(); g.clip(G.face); g.fillStyle = '#e4d1b0'; g.fill(G.face); g.fillStyle = '#c9b28c'; poly(g, [[a.ear[0] - 30, a.ear[1] - 60], [a.ear[0] + 20, a.ear[1] - 40], [a.chin[0] + 50, a.chin[1] + 10], [a.chin[0] + 10, a.chin[1] - 10]]); g.fill(); g.restore();
|
|
206
|
+
g.fillStyle = '#e4d1b0'; g.fill(G.neck);
|
|
207
|
+
g.save(); g.clip(G.face); g.fillStyle = 'rgba(82,104,128,.82)'; poly(g, [[a.forehead[0] - 20, a.forehead[1] - 10], [a.forehead[0] + 22, a.forehead[1] - 4], [a.chin[0] + 8, a.chin[1] - 30], [a.chin[0] + 6, a.chin[1] + 10], [a.chin[0] - 40, a.chin[1] + 10]]); g.fill(); g.restore();
|
|
208
|
+
const e = G.eye; g.fillStyle = '#efe8d8'; g.beginPath(); g.moveTo(e.x - 14, e.y); g.quadraticCurveTo(e.x + 2, e.y - 12, e.x + 18, e.y); g.quadraticCurveTo(e.x + 2, e.y + 11, e.x - 14, e.y); g.fill(); g.strokeStyle = C.ink; g.lineWidth = 2.4; g.stroke();
|
|
209
|
+
if (ch.blink) { g.beginPath(); g.moveTo(e.x - 14, e.y); g.lineTo(e.x + 18, e.y); g.stroke(); } else { g.fillStyle = '#231a12'; g.beginPath(); g.arc(e.x + 3, e.y, 5.5, 0, TAU); g.fill(); }
|
|
210
|
+
g.lineWidth = 2.2; g.beginPath(); g.moveTo(e.x - 12, e.y - 14); g.quadraticCurveTo(e.x + 4, e.y - 22, e.x + 20, e.y - 13); g.stroke();
|
|
211
|
+
g.fillStyle = '#a8443a'; g.beginPath(); g.moveTo(...G.lips[0]); g.lineTo(G.lips[1][0] + 3, G.lips[1][1]); g.lineTo(...G.lips[2]); g.closePath(); g.fill();
|
|
212
|
+
// 耳(螺旋)
|
|
213
|
+
g.strokeStyle = C.ink; g.lineWidth = 2; g.beginPath(); for (let k = 0; k < 14; k++) { const q = k / 13, ang = q * 5.5, rr = 11 * (1 - q * 0.7); const x = a.ear[0] + Math.cos(ang) * rr, y = a.ear[1] + Math.sin(ang) * rr * 1.3; k ? g.lineTo(x, y) : g.moveTo(x, y); } g.stroke();
|
|
214
|
+
// 白领(两块明暗三角)
|
|
215
|
+
{ const n = a.neck; g.fillStyle = '#e9e2d0'; poly(g, [[n[0] - 30, n[1] + 2], [n[0] + 4, n[1] + 12], [n[0] - 14, n[1] + 46]]); g.fill(); g.fillStyle = '#bdb5a2'; poly(g, [[n[0] + 4, n[1] + 12], [n[0] + 40, n[1] + 2], [n[0] + 18, n[1] + 44]]); g.fill();
|
|
216
|
+
g.strokeStyle = C.ink; g.lineWidth = 1.8; poly(g, [[n[0] - 30, n[1] + 2], [n[0] + 40, n[1] + 2], [n[0] + 18, n[1] + 44], [n[0] + 4, n[1] + 12], [n[0] - 14, n[1] + 46]]); g.stroke();
|
|
217
|
+
g.fillStyle = '#2e241a'; [0, 1, 2].forEach(k => { g.beginPath(); g.arc(n[0] + 6 - k * 2, n[1] + 60 + k * 26, 3, 0, TAU); g.fill(); }); }
|
|
218
|
+
// 直线外轮廓
|
|
219
|
+
segs(g, [a.forehead, [a.forehead[0] - 12, a.forehead[1] + 40], [a.chin[0] - 14, a.chin[1] - 18], a.chin, a.neck]);
|
|
220
|
+
segs(g, [a.headTop, [a.nape[0] + 30, a.nape[1] - 40], a.nape, a.back, [a.back[0] + 14, a.back[1] + 90]]);
|
|
221
|
+
segs(g, [a.neck, a.chest, [a.chest[0] - 4, a.chest[1] + 100], a.lap, a.knee, [a.knee[0] - 50, a.knee[1] + 160], a.hemL]);
|
|
222
|
+
segs(g, [a.shoulder, a.elbow, a.hand], 12, 'rgba(40,30,20,.7)', 2.2);
|
|
223
|
+
// 杯:重画清楚(半边暗面)
|
|
224
|
+
const k = G.cup; g.save(); g.translate(k.x, k.y); g.rotate(k.tilt); g.fillStyle = '#ebe6da'; poly(g, [[-24, 0], [-21, 40], [21, 40], [24, 0]]); g.fill(); g.fillStyle = '#56606a'; poly(g, [[4, 0], [24, 0], [21, 40], [4, 40]]); g.fill();
|
|
225
|
+
g.strokeStyle = C.ink; g.lineWidth = 2.2; poly(g, [[-24, 0], [-21, 40], [21, 40], [24, 0]]); g.stroke(); g.beginPath(); g.arc(28, 18, 11, -1.3, 1.4); g.lineWidth = 4; g.stroke();
|
|
226
|
+
g.fillStyle = '#231a12'; g.beginPath(); g.ellipse(0, 1, 23, 5, 0, 0, TAU); g.fill(); g.restore();
|
|
227
|
+
c.drawImage(L, 0, 0);
|
|
228
|
+
}
|
|
229
|
+
function cat(c, K, lt, t, ch) {
|
|
230
|
+
const L = layer('cat'), g = L.getContext('2d'); g.reset();
|
|
231
|
+
RIG.drawCat(g, K, catPal);
|
|
232
|
+
facetize(g, L, [320, 520, 740, 920], 26, 31, t, { boilFrac: 0.35, light: 0.25, dark: 0.36 });
|
|
233
|
+
// 头的直线轮廓+毕加索式双眼(一只正面、一只侧面)+侧面鼻
|
|
234
|
+
const h = K.headC, e = K.eyes;
|
|
235
|
+
segs(g, [[h[0] - 66, h[1] + 4], [h[0] - 44, h[1] - 50], [h[0] - 40, h[1] - 88], [h[0] - 6, h[1] - 50], [h[0] + 30, h[1] - 58], [h[0] + 52, h[1] - 86], [h[0] + 60, h[1] - 30], [h[0] + 66, h[1] + 14], [h[0] + 30, h[1] + 52], [h[0] - 34, h[1] + 50], [h[0] - 66, h[1] + 4]], 10);
|
|
236
|
+
segs(g, K.bodyPts.concat([K.bodyPts[0]]), 12);
|
|
237
|
+
e.forEach((p, i) => { g.fillStyle = '#d6b040'; g.beginPath(); g.moveTo(p.x - 12, p.y); g.lineTo(p.x, p.y - 9); g.lineTo(p.x + 12, p.y); g.lineTo(p.x, p.y + 9); g.closePath(); g.fill(); g.strokeStyle = C.ink; g.lineWidth = 2; g.stroke();
|
|
238
|
+
if (ch.blink) { g.beginPath(); g.moveTo(p.x - 12, p.y); g.lineTo(p.x + 12, p.y); g.stroke(); } else { g.fillStyle = '#1a140e'; g.fillRect(p.x - 2 + (i ? 3 : 0), p.y - 7, 5, 14); } });
|
|
239
|
+
g.fillStyle = '#c4706a'; poly(g, [[K.nose[0] - 6, K.nose[1] - 8], [K.nose[0] + 12, K.nose[1] + 2], [K.nose[0] - 4, K.nose[1] + 6]]); g.fill();
|
|
240
|
+
g.strokeStyle = 'rgba(30,24,18,.8)'; g.lineWidth = 1.4; K.whiskers.forEach(w => { g.beginPath(); g.moveTo(...w[0]); g.lineTo(w[1][0] + 20, w[1][1]); g.stroke(); });
|
|
241
|
+
c.drawImage(L, 0, 0, 820, 1080, 0, 0, 820, 1080);
|
|
242
|
+
}
|
|
243
|
+
// 最上层的透明叠面(缓慢错动),把角色嵌进空间
|
|
244
|
+
function overPlanes(c, lt, t) {
|
|
245
|
+
const st = P.boilSeed(t, 8), r = U.rng(5);
|
|
246
|
+
[[[1150, 260], [1330, 220], [1290, 470]], [[1180, 520], [1300, 470], [1330, 700], [1200, 720]], [[420, 640], [560, 600], [520, 780]], [[880, 300], [1060, 260], [1000, 420]], [[1350, 600], [1450, 560], [1470, 780]]].forEach((pts, i) => {
|
|
247
|
+
const dx = Math.sin(lt * 6 + i) * 8 + (U.hash(st, i) - .5) * 3; const q = pts.map(([x, y]) => [x + dx, y]);
|
|
248
|
+
plane(c, q, [C.cream, C.bgrey, C.ochre, C.cream, C.grey][i], 0.2, r, 0.35, 0.4); c.strokeStyle = 'rgba(40,30,20,.45)'; c.lineWidth = 1.2; poly(c, q); c.stroke(); });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
draw(c, lt, t) {
|
|
253
|
+
const ch = P.choreo(lt, t);
|
|
254
|
+
c.drawImage(bg(), 0, 0);
|
|
255
|
+
windowPanes(c, lt, t);
|
|
256
|
+
cafe(c, lt);
|
|
257
|
+
news(c, lt, t);
|
|
258
|
+
const G = RIG.girl({ cup: ch.cup, sip: ch.sip, hair: 'long', breathe: ch.breathe });
|
|
259
|
+
girl(c, G, lt, t, ch);
|
|
260
|
+
const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe });
|
|
261
|
+
cat(c, K, lt, t, ch);
|
|
262
|
+
overPlanes(c, lt, t);
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
})();
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// 1923 包豪斯海报(施莱默《三元芭蕾》人偶+蒙德里安格窗+构成主义斜杆)——纯代码。
|
|
2
|
+
// 管线:①缓存底版(米白旧纸+斑驳+竖排大字 bauhaus+红黑条+katze+右下两行小字+地线+黄三角)
|
|
3
|
+
// → ②会动的构成(斜长黑杆像指针绕轴转、越转越陡;格窗色块按拍轮换;红圆右侧同心弧扫动;杯上波浪热气)
|
|
4
|
+
// → ③角色全由几何体拼:人偶=圆头/梯形躯干/黑杆四肢/白关节盘(锚点全取自 RIG.girl);猫=圆身方头三角耳,拨球
|
|
5
|
+
SCENES['12_bauhaus'] = (() => {
|
|
6
|
+
const W = 1920, H = 1080, { clamp, lerp, ease } = U, P = PAINT, TAU = Math.PI * 2;
|
|
7
|
+
const C = { paper: '#ebe3cf', ink: '#161514', red: '#d3262b', blue: '#2a58b4', yellow: '#efc12f', lblue: '#b9cee0', lblue2: '#a6c0da', grey: '#c6c3bb', cream: '#f2ecdf', orange: '#ee8a2a', orangeD: '#c25a14', chrome: '#c9c9c6' };
|
|
8
|
+
const font = (px, f = 'Poppins-800') => `${px}px "${f}"`;
|
|
9
|
+
|
|
10
|
+
// 斑驳小噪声图(放大使用):给纸、红圆、黑字做旧
|
|
11
|
+
const mottle = (key, seed, col, amt) => P.cached('bh_m_' + key, 320, 180, (g) => {
|
|
12
|
+
const im = g.createImageData(320, 180), r = U.rng(seed);
|
|
13
|
+
for (let y = 0; y < 180; y++) for (let x = 0; x < 320; x++) { const v = P.fbm(x * 0.045 + seed, y * 0.045, 4), i = (y * 320 + x) * 4;
|
|
14
|
+
im.data[i] = col[0]; im.data[i + 1] = col[1]; im.data[i + 2] = col[2]; im.data[i + 3] = clamp(v * 1.6 + 0.1) * 255 * amt + r() * 10; }
|
|
15
|
+
g.putImageData(im, 0, 0); });
|
|
16
|
+
|
|
17
|
+
// ---------- ① 底版 ----------
|
|
18
|
+
const bg = () => P.cached('bh_bg', W, H, (g) => {
|
|
19
|
+
g.fillStyle = C.paper; g.fillRect(0, 0, W, H);
|
|
20
|
+
g.globalAlpha = 0.5; g.drawImage(mottle('paper', 3, [150, 130, 95], 0.35), 0, 0, W, H); g.globalAlpha = 1;
|
|
21
|
+
// 长细斜线(贯穿画面,压在窗后)
|
|
22
|
+
g.strokeStyle = C.ink; g.lineWidth = 2.2; g.beginPath(); g.moveTo(40, 170); g.lineTo(1900, 840); g.stroke();
|
|
23
|
+
// 左上黑横条、红竖条
|
|
24
|
+
g.fillStyle = C.ink; g.fillRect(60, 61, 256, 13);
|
|
25
|
+
g.fillStyle = C.red; g.fillRect(292, 105, 24, 468);
|
|
26
|
+
// 竖排 bauhaus(自下而上读)
|
|
27
|
+
g.save(); g.translate(262, 1000); g.rotate(-Math.PI / 2); g.font = font(210); g.fillStyle = C.ink; g.textBaseline = 'alphabetic';
|
|
28
|
+
const w = g.measureText('bauhaus').width; g.scale(820 / w, 1); g.fillText('bauhaus', 0, 0); g.restore();
|
|
29
|
+
// 小灰方块
|
|
30
|
+
g.fillStyle = '#9a9894'; g.fillRect(875, 150, 31, 31);
|
|
31
|
+
// 地线、黄三角
|
|
32
|
+
g.fillStyle = C.ink; g.fillRect(345, 898, 1515, 17);
|
|
33
|
+
g.fillStyle = C.yellow; g.beginPath(); g.moveTo(1738, 898); g.lineTo(1822, 898); g.lineTo(1822, 793); g.closePath(); g.fill();
|
|
34
|
+
// 右侧细斜线
|
|
35
|
+
g.strokeStyle = C.ink; g.lineWidth = 2; g.beginPath(); g.moveTo(1610, 740); g.lineTo(1900, 842); g.stroke();
|
|
36
|
+
// katze
|
|
37
|
+
g.fillStyle = C.ink; g.fillRect(345, 946, 25, 25);
|
|
38
|
+
g.font = font(150); g.fillStyle = C.red; g.textBaseline = 'alphabetic'; g.fillText('katze', 392, 1042);
|
|
39
|
+
g.strokeStyle = C.ink; g.lineWidth = 2; g.beginPath(); g.moveTo(800, 1040); g.lineTo(1180, 1040); g.stroke();
|
|
40
|
+
// 右下两行小字
|
|
41
|
+
g.fillStyle = C.ink; g.fillRect(1224, 953, 12, 60);
|
|
42
|
+
g.font = font(31, 'Poppins-600'); g.letterSpacing = '1px'; g.fillText('staatliches bauhaus weimar', 1250, 977); g.fillText('ausstellung juli – september', 1250, 1014); g.letterSpacing = '0px';
|
|
43
|
+
// 桌(黑面+镀铬管腿+黑底座)
|
|
44
|
+
g.fillStyle = C.ink; g.fillRect(820, 643, 380, 21); g.fillRect(925, 883, 170, 17);
|
|
45
|
+
const cg = g.createLinearGradient(1000, 0, 1017, 0); cg.addColorStop(0, '#8a8a88'); cg.addColorStop(0.4, '#f2f2ee'); cg.addColorStop(1, '#7a7a78');
|
|
46
|
+
g.fillStyle = cg; g.fillRect(1000, 664, 17, 220); g.strokeStyle = '#555'; g.lineWidth = 1.5; g.strokeRect(1000, 664, 17, 220);
|
|
47
|
+
// 布劳耶式悬臂椅(镀铬管)
|
|
48
|
+
const tube = (pts) => { [[15, '#4a4a48'], [10, C.chrome], [3, '#fafaf6']].forEach(([w, col], k) => { g.strokeStyle = col; g.lineWidth = w; g.lineJoin = 'round'; g.lineCap = 'round'; g.beginPath(); pts.forEach((p, i) => i ? g.lineTo(p[0] - (k === 2 ? 2 : 0), p[1] - (k === 2 ? 2 : 0)) : g.moveTo(p[0], p[1])); g.stroke(); }); };
|
|
49
|
+
tube([[1508, 888], [1318, 888], [1300, 868], [1300, 770], [1318, 742], [1450, 736]]);
|
|
50
|
+
tube([[1466, 738], [1470, 480]]);
|
|
51
|
+
g.fillStyle = C.ink; g.fillRect(1450, 486, 14, 44);
|
|
52
|
+
// 纸面颗粒+黑色斑点(旧印刷)
|
|
53
|
+
g.drawImage(P.grain('bh', 0.02, [40, 35, 30], 0.35), 0, 0);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// 红圆(斑驳,缓存)
|
|
57
|
+
const redDisc = () => P.cached('bh_red', 520, 520, (g) => {
|
|
58
|
+
g.fillStyle = C.red; g.beginPath(); g.arc(260, 260, 252, 0, TAU); g.fill();
|
|
59
|
+
g.save(); g.clip(); g.globalCompositeOperation = 'source-atop'; g.globalAlpha = 0.55; g.drawImage(mottle('red', 9, [245, 150, 140], 0.5), -100, -60, 760, 640); g.restore();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// ---------- ② 蒙德里安格窗(色块轮换) ----------
|
|
63
|
+
const GX = 366, GY = 126, CW = 100, CH = 101, GAP = 9.5;
|
|
64
|
+
const base = [['cream', 'lblue', 'quarter', 'lblue'], ['yellow', 'grey', 'cream', 'lblue'], ['lblue', 'grid', 'lblue2', 'cream'], ['blue', 'cream', 'grid', 'lblue']];
|
|
65
|
+
const cycle = ['red', 'yellow', 'blue', 'cream', 'lblue', 'grey'];
|
|
66
|
+
const swaps = { '0,0': 0, '1,2': 2, '2,0': 4, '3,1': 1, '0,3': 3, '2,3': 5 }; // 哪几格在换色,各自相位
|
|
67
|
+
const fillOf = { cream: C.cream, lblue: C.lblue, lblue2: C.lblue2, yellow: C.yellow, grey: C.grey, blue: C.blue, red: C.red, ink: '#222' };
|
|
68
|
+
function grid(c, lt) {
|
|
69
|
+
c.fillStyle = C.ink; c.fillRect(352, 112, 456, 463); c.fillRect(800, 565, 32, 10);
|
|
70
|
+
const step = Math.floor(clamp(lt, 0, 9) * 13);
|
|
71
|
+
for (let r = 0; r < 4; r++) for (let k = 0; k < 4; k++) {
|
|
72
|
+
const x = GX + k * (CW + GAP) - (k ? 0 : 0), y = GY + r * (CH + GAP);
|
|
73
|
+
let kind = base[r][k]; const sw = swaps[r + ',' + k];
|
|
74
|
+
if (sw !== undefined && lt > 0.12) { const s = (step + sw * 2) % 9; if (s < cycle.length) kind = cycle[(s + sw) % cycle.length]; }
|
|
75
|
+
if (kind === 'grid') { c.fillStyle = C.cream; c.fillRect(x, y, CW, CH); c.strokeStyle = 'rgba(30,30,30,.7)'; c.lineWidth = 1; c.beginPath(); for (let q = 6; q < CW; q += 9) { c.moveTo(x + q, y); c.lineTo(x + q, y + CH); } for (let q = 6; q < CH; q += 9) { c.moveTo(x, y + q); c.lineTo(x + CW, y + q); } c.stroke(); }
|
|
76
|
+
else if (kind === 'quarter') { c.fillStyle = C.grey; c.fillRect(x, y, CW, CH); c.fillStyle = C.cream; c.beginPath(); c.moveTo(x, y + CH); c.arc(x, y + CH, CW * 0.72, -Math.PI / 2, 0); c.closePath(); c.fill(); }
|
|
77
|
+
else { c.fillStyle = fillOf[kind]; c.fillRect(x, y, CW, CH); }
|
|
78
|
+
}
|
|
79
|
+
c.save(); c.globalAlpha = 0.35; c.drawImage(mottle('paper', 3, [150, 130, 95], 0.35), 352, 112, 456, 463, 352, 112, 456, 463); c.restore();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ---------- 斜长黑杆:绕毂转动(原片 575–591 帧 28°→49.5°) ----------
|
|
83
|
+
const PIV = [1125, 195];
|
|
84
|
+
function rod(c, lt) {
|
|
85
|
+
const k = ease.out(clamp((lt - 0.26) / 0.22)), deg = 28 + 21.5 * k + Math.sin(clamp((lt - 0.48) / 0.3) * Math.PI) * 1.5;
|
|
86
|
+
const a = deg * Math.PI / 180, u = [Math.cos(a), -Math.sin(a)], n = [Math.sin(a), Math.cos(a)]; // n = 垂直方向(右下)
|
|
87
|
+
const at = (s, o = 0) => [PIV[0] + u[0] * s + n[0] * o, PIV[1] + u[1] * s + n[1] * o];
|
|
88
|
+
// 毂圆
|
|
89
|
+
c.strokeStyle = C.ink; c.lineWidth = 2.2; c.beginPath(); c.arc(PIV[0], PIV[1], 76, 0, TAU); c.stroke();
|
|
90
|
+
c.beginPath(); c.arc(PIV[0], PIV[1], 58, -a + 0.9, -a + 0.9 + 3.6); c.stroke();
|
|
91
|
+
// 细平行线
|
|
92
|
+
c.lineWidth = 2.4; c.beginPath(); c.moveTo(...at(-540, 30)); c.lineTo(...at(240, 30)); c.stroke();
|
|
93
|
+
// 粗黑杆
|
|
94
|
+
c.lineWidth = 27; c.lineCap = 'butt'; c.beginPath(); c.moveTo(...at(-432)); c.lineTo(...at(150)); c.stroke();
|
|
95
|
+
// 红短杆
|
|
96
|
+
c.strokeStyle = C.red; c.lineWidth = 12; c.beginPath(); c.moveTo(...at(52, -30)); c.lineTo(...at(150, -30)); c.stroke();
|
|
97
|
+
c.fillStyle = C.ink; c.beginPath(); c.arc(PIV[0], PIV[1], 13, 0, TAU); c.fill();
|
|
98
|
+
}
|
|
99
|
+
// 红圆右侧的同心弧(像声波一样往外扫)
|
|
100
|
+
function arcs(c, lt) {
|
|
101
|
+
c.strokeStyle = C.ink; c.lineCap = 'butt';
|
|
102
|
+
const sweep = clamp(lt / 0.4);
|
|
103
|
+
[[272, -1.05, 0.95, 2.2], [292, -1.0, 0.9, 2.2], [312, -0.95, 0.85, 2.2], [332, -0.88, 0.8, 2.2], [352, -0.8, 0.7, 2.2], [420, -0.25, 0.62, 2]].forEach(([r, a0, a1, w], i) => {
|
|
104
|
+
const d = Math.sin(lt * 9 - i * 0.7) * 0.05; c.lineWidth = w; c.beginPath(); c.arc(1410, 595, r + Math.sin(lt * 12 - i) * 3, a0 + d, lerp(a0, a1, 0.55 + 0.45 * sweep) + d); c.stroke(); });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ---------- 人偶少女(施莱默):锚点取自 RIG.girl ----------
|
|
108
|
+
const joint = (c, x, y, r) => { c.fillStyle = C.cream; c.beginPath(); c.arc(x, y, r, 0, TAU); c.fill(); c.strokeStyle = C.ink; c.lineWidth = 5; c.stroke(); c.fillStyle = C.ink; c.beginPath(); c.arc(x, y, r * 0.3, 0, TAU); c.fill(); };
|
|
109
|
+
const bar = (c, A, B, w, col = C.ink) => { c.strokeStyle = col; c.lineWidth = w; c.lineCap = 'round'; c.beginPath(); c.moveTo(A[0], A[1]); c.lineTo(B[0], B[1]); c.stroke(); };
|
|
110
|
+
function doll(c, G, lt, t, ch) {
|
|
111
|
+
const a = G.A, hc = [a.headC[0] + 14, a.headC[1] - 18];
|
|
112
|
+
// 远侧手臂:平放桌上
|
|
113
|
+
bar(c, [1352, 482], [1300, 600], 24); bar(c, [1300, 600], [1150, 614], 22); joint(c, 1300, 600, 17); joint(c, 1140, 616, 16);
|
|
114
|
+
// 背后的金色发片(半圆)
|
|
115
|
+
c.fillStyle = C.yellow; c.beginPath(); c.arc(a.back[0] - 18, a.back[1] - 32, 56, -Math.PI / 2, Math.PI / 2); c.closePath(); c.fill();
|
|
116
|
+
// 裙:黑扇面+蓝梯形
|
|
117
|
+
c.fillStyle = C.ink; c.beginPath(); c.moveTo(1356, 612); c.arc(1356, 612, 178, -0.05, 1.05); c.closePath(); c.fill();
|
|
118
|
+
c.fillStyle = C.blue; c.beginPath(); c.moveTo(1278, 606); c.lineTo(1420, 606); c.lineTo(1446, 728); c.lineTo(1236, 728); c.closePath(); c.fill();
|
|
119
|
+
// 腿
|
|
120
|
+
const hip = [1390, 716], knee = [1224, 722], ank = [1198, 878];
|
|
121
|
+
bar(c, hip, knee, 42); bar(c, knee, ank, 36);
|
|
122
|
+
c.fillStyle = C.ink; c.beginPath(); c.moveTo(1214, 860); c.lineTo(1224, 900); c.lineTo(1124, 900); c.lineTo(1150, 878); c.closePath(); c.fill();
|
|
123
|
+
joint(c, hip[0], hip[1], 24); joint(c, knee[0], knee[1], 21); joint(c, ank[0], ank[1], 10);
|
|
124
|
+
// 躯干:蓝梯形(随呼吸)
|
|
125
|
+
const top = a.neck[1] + 12, bot = 612, br = ch.breathe * 140;
|
|
126
|
+
c.fillStyle = C.blue; c.beginPath(); c.moveTo(1298, top - br); c.lineTo(1398, top + 8 - br); c.lineTo(1414, bot); c.lineTo(1290, bot); c.closePath(); c.fill();
|
|
127
|
+
c.fillStyle = C.ink; c.beginPath(); c.moveTo(1340, top - br); c.lineTo(1398, top + 8 - br); c.lineTo(1414, bot); c.lineTo(1380, bot); c.closePath(); c.globalAlpha = 0.9; c.fill(); c.globalAlpha = 1;
|
|
128
|
+
// 脖子
|
|
129
|
+
bar(c, [a.neck[0] + 8, a.neck[1] - 24], [a.neck[0] + 8, top - br + 4], 15);
|
|
130
|
+
// 头(随喝的仰头一起转)
|
|
131
|
+
c.save(); c.translate(a.neck[0], a.neck[1]); c.rotate(G.tilt); c.translate(-a.neck[0], -a.neck[1]);
|
|
132
|
+
const R = 70;
|
|
133
|
+
c.fillStyle = C.cream; c.beginPath(); c.arc(hc[0], hc[1], R, 0, TAU); c.fill();
|
|
134
|
+
// 金色几何发片:后半个头+额前三角+发髻圆
|
|
135
|
+
c.fillStyle = C.yellow; c.beginPath(); c.moveTo(hc[0], hc[1]); c.arc(hc[0], hc[1], R, -1.95, 1.15); c.closePath(); c.fill();
|
|
136
|
+
c.beginPath(); c.moveTo(hc[0] - 50, hc[1] - 50); c.lineTo(hc[0] + 2, hc[1] - R); c.lineTo(hc[0] + 4, hc[1] - 18); c.closePath(); c.fill();
|
|
137
|
+
c.beginPath(); c.arc(hc[0] + 78, hc[1] + 30, 34, 0, TAU); c.fill(); c.strokeStyle = C.ink; c.lineWidth = 3; c.stroke();
|
|
138
|
+
c.strokeStyle = C.ink; c.lineWidth = 5; c.beginPath(); c.arc(hc[0], hc[1], R, 0, TAU); c.stroke();
|
|
139
|
+
c.lineWidth = 2.5; c.beginPath(); c.moveTo(hc[0], hc[1] - R); c.lineTo(hc[0], hc[1] + R); c.stroke();
|
|
140
|
+
// 五官:黑竖条眼、红方腮、伸出头外的黑横杠(口)
|
|
141
|
+
if (ch.blink) { c.fillStyle = C.ink; c.fillRect(hc[0] - 42, hc[1] - 4, 14, 5); } else { c.fillStyle = C.ink; c.fillRect(hc[0] - 40, hc[1] - 18, 10, 30); }
|
|
142
|
+
c.fillStyle = C.red; c.fillRect(hc[0] - 22, hc[1] + 2, 20, 20);
|
|
143
|
+
c.fillStyle = C.ink; c.fillRect(hc[0] - 62, hc[1] + 30, 24, 6);
|
|
144
|
+
c.restore();
|
|
145
|
+
// 近侧手臂(IK 跟着端杯)
|
|
146
|
+
bar(c, a.shoulder, a.elbow, 28); bar(c, a.elbow, a.hand, 24);
|
|
147
|
+
mug(c, G, t);
|
|
148
|
+
joint(c, a.shoulder[0], a.shoulder[1], 22); joint(c, a.elbow[0], a.elbow[1], 19); joint(c, a.hand[0], a.hand[1], 17);
|
|
149
|
+
}
|
|
150
|
+
function mug(c, G, t) {
|
|
151
|
+
const k = G.cup; c.save(); c.translate(k.x, k.y); c.rotate(k.tilt);
|
|
152
|
+
c.fillStyle = '#fbf8f0'; c.strokeStyle = C.ink; c.lineWidth = 3.5; c.fillRect(-28, -6, 56, 64); c.strokeRect(-28, -6, 56, 64);
|
|
153
|
+
c.fillStyle = C.blue; c.fillRect(-28, 14, 56, 8);
|
|
154
|
+
c.lineWidth = 6; c.beginPath(); c.arc(30, 24, 14, -1.3, 1.3); c.stroke();
|
|
155
|
+
c.restore();
|
|
156
|
+
// 一根细波浪热气(像弹簧)
|
|
157
|
+
c.strokeStyle = C.ink; c.lineWidth = 2.2; c.beginPath();
|
|
158
|
+
for (let i = 0; i <= 60; i++) { const q = i / 60, y = k.y - 16 - q * 170, x = k.x - 4 + Math.sin(q * 26 - t * 18) * 9 * (1 - q * 0.3); i ? c.lineTo(x, y) : c.moveTo(x, y); }
|
|
159
|
+
const gg = c.createLinearGradient(0, k.y, 0, k.y - 190); gg.addColorStop(0, C.ink); gg.addColorStop(1, 'rgba(22,21,20,0)'); c.strokeStyle = gg; c.stroke();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ---------- 几何猫:圆身方头三角耳,拨球 ----------
|
|
163
|
+
function cat(c, K, lt, t, ch) {
|
|
164
|
+
const bc = [K.box[0][0] + 146, K.box[1][1] - 100], br = 100 * (1 + ch.breathe * 2);
|
|
165
|
+
// 动作弧线
|
|
166
|
+
c.strokeStyle = C.ink; c.lineWidth = 2.5; [[150, -2.5, -1.75], [130, -2.45, -1.95]].forEach(([r, a0, a1]) => { c.beginPath(); c.arc(bc[0] + 50, bc[1] + 20, r, a0 + Math.PI * 0.62, a1 + Math.PI * 0.62); c.stroke(); });
|
|
167
|
+
// 尾巴:粗橙弧,带深橙条纹,跟着 choreo 摆
|
|
168
|
+
const ta0 = Math.PI * 0.62 + ch.tail * 0.5, ta1 = Math.PI * 1.22 + ch.tail * 0.5, tc = [bc[0] - 20, bc[1] + 10];
|
|
169
|
+
c.lineCap = 'butt'; c.strokeStyle = C.orange; c.lineWidth = 26; c.beginPath(); c.arc(tc[0], tc[1], 108, ta0, ta1); c.stroke();
|
|
170
|
+
c.strokeStyle = C.orangeD; c.lineWidth = 26; c.setLineDash([10, 14]); c.beginPath(); c.arc(tc[0], tc[1], 108, ta0 + 0.08, ta1); c.stroke(); c.setLineDash([]);
|
|
171
|
+
// 身体圆+左侧条纹(夹在圆里)
|
|
172
|
+
c.fillStyle = C.orange; c.beginPath(); c.arc(bc[0], bc[1], br, 0, TAU); c.fill();
|
|
173
|
+
c.save(); c.beginPath(); c.arc(bc[0], bc[1], br, 0, TAU); c.clip();
|
|
174
|
+
c.fillStyle = C.orangeD; [[-100, -40, 70], [-100, -12, 80], [-100, 16, 74], [-100, 44, 60]].forEach(([x, y, w]) => c.fillRect(bc[0] + x, bc[1] + y, w, 13));
|
|
175
|
+
c.fillStyle = C.cream; c.beginPath(); c.moveTo(bc[0] + 10, bc[1] + br); c.arc(bc[0], bc[1], br, 0.15, 1.45); c.closePath(); c.fill();
|
|
176
|
+
c.fillStyle = 'rgba(255,240,220,.18)'; c.beginPath(); c.arc(bc[0] - 30, bc[1] - 30, 40, 0, TAU); c.fill();
|
|
177
|
+
c.restore();
|
|
178
|
+
// 爪:三颗白圆;最右那只在 0.06–0.2s 伸出去拨球
|
|
179
|
+
const tap = Math.sin(clamp((lt - 0.05) / 0.16) * Math.PI);
|
|
180
|
+
[[-62, 0], [16, 0], [88 + tap * 36, -tap * 10]].forEach(([dx, dy], i) => { const x = bc[0] + dx, y = 878 + dy; c.fillStyle = C.cream; c.beginPath(); c.arc(x, y, 23, 0, TAU); c.fill(); c.strokeStyle = C.ink; c.lineWidth = 2.5; c.stroke(); });
|
|
181
|
+
// 蓝球:被拨后滚一下(转角 = 位移 / 半径)
|
|
182
|
+
const roll = ease.out(clamp((lt - 0.13) / 0.34)), bx = 712 + roll * 104, rb = 31;
|
|
183
|
+
c.fillStyle = C.blue; c.beginPath(); c.arc(bx, 898 - rb, rb, 0, TAU); c.fill();
|
|
184
|
+
c.save(); c.translate(bx, 898 - rb); c.rotate(roll * 104 / rb); c.strokeStyle = C.cream; c.lineWidth = 4; c.beginPath(); c.moveTo(-rb + 6, 0); c.lineTo(rb - 6, 0); c.stroke(); c.fillStyle = C.cream; c.beginPath(); c.arc(0, -rb * 0.55, 5, 0, TAU); c.fill(); c.restore();
|
|
185
|
+
if (roll > 0 && roll < 0.95) { c.strokeStyle = C.ink; c.lineWidth = 2; [0, 1].forEach(j => { c.beginPath(); c.moveTo(bx - rb - 14 - j * 16, 850 + j * 18); c.lineTo(bx - rb - 44 - j * 16 - 30 * (1 - roll), 850 + j * 18); c.stroke(); }); }
|
|
186
|
+
// 头:方块(微转),黄三角耳,白口鼻块
|
|
187
|
+
const hc = [K.headC[0] + 14, K.headC[1] + 48], s = 104, rot = -0.06 + ch.tail * 0.08;
|
|
188
|
+
c.save(); c.translate(hc[0], hc[1]); c.rotate(rot);
|
|
189
|
+
c.fillStyle = C.yellow; c.beginPath(); c.moveTo(-52, -50); c.lineTo(-38, -104); c.lineTo(-4, -50); c.closePath(); c.fill(); c.beginPath(); c.moveTo(14, -50); c.lineTo(50, -104); c.lineTo(54, -50); c.closePath(); c.fill();
|
|
190
|
+
c.fillStyle = C.orange; c.fillRect(-s / 2, -s / 2, s, s);
|
|
191
|
+
c.fillStyle = C.cream; c.fillRect(-s / 2, 8, s, s / 2 - 8);
|
|
192
|
+
c.fillStyle = C.orangeD; c.fillRect(-s / 2, -s / 2, s, 12);
|
|
193
|
+
[[-22, -10], [22, -10]].forEach(([x, y]) => { if (ch.blink) { c.fillStyle = C.ink; c.fillRect(x - 13, y, 26, 4); return; } c.fillStyle = '#fff'; c.beginPath(); c.arc(x, y, 15, 0, TAU); c.fill(); c.strokeStyle = C.ink; c.lineWidth = 3; c.stroke(); c.fillStyle = C.ink; c.fillRect(x - 1, y - 11, 8, 22); });
|
|
194
|
+
c.fillStyle = C.ink; c.beginPath(); c.moveTo(-8, 16); c.lineTo(8, 16); c.lineTo(0, 27); c.closePath(); c.fill();
|
|
195
|
+
c.strokeStyle = C.ink; c.lineWidth = 1.8; c.beginPath(); [[16, 20, 140, 6], [16, 26, 142, 30], [16, 32, 136, 52], [-16, 22, -120, 10], [-16, 30, -122, 34]].forEach(([x0, y0, x1, y1]) => { c.moveTo(x0, y0); c.lineTo(x1, y1); }); c.stroke();
|
|
196
|
+
c.restore();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
draw(c, lt, t) {
|
|
201
|
+
const ch = P.choreo(lt, t);
|
|
202
|
+
c.drawImage(bg(), 0, 0);
|
|
203
|
+
c.drawImage(redDisc(), 1410 - 260, 595 - 260);
|
|
204
|
+
arcs(c, lt);
|
|
205
|
+
grid(c, lt);
|
|
206
|
+
rod(c, lt);
|
|
207
|
+
const G = RIG.girl({ cup: ch.cup, sip: ch.sip, breathe: ch.breathe });
|
|
208
|
+
doll(c, G, lt, t, ch);
|
|
209
|
+
const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe });
|
|
210
|
+
cat(c, K, lt, t, ch);
|
|
211
|
+
c.drawImage(P.grain('bh2', 0.012, [30, 28, 26], 0.5), 0, 0);
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
})();
|