@biunow/agent-biuart 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/server.mjs +575 -0
- package/data/styles.json +533 -0
- package/engine/audit_clip.py +46 -0
- package/engine/clip.html +24 -0
- package/engine/clip.js +104 -0
- package/engine/clip_schema.json +19 -0
- package/engine/clips/t1_3b1b.js +203 -0
- package/engine/clips/t2_keynote_ui.js +347 -0
- package/engine/clips/t3_finance_chart.js +206 -0
- package/engine/clips/y1_kurzgesagt.js +183 -0
- package/engine/clips/y2_vox.js +150 -0
- package/engine/clips/y3_whiteboard.js +209 -0
- package/engine/clips/y4_storytime.js +194 -0
- package/engine/clips/y5_kinetic_type.js +134 -0
- package/engine/compare.py +19 -0
- package/engine/demos/_shared/hero/c1_0.png +0 -0
- package/engine/demos/_shared/hero/c1_1.png +0 -0
- package/engine/demos/_shared/hero/c1_2.png +0 -0
- package/engine/demos/_shared/hero/c3_line.png +0 -0
- package/engine/demos/_shared/hero.js +96 -0
- package/engine/demos/long_scroll/eras.js +23 -0
- package/engine/demos/long_scroll/frames/8bit_act/01.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/02.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/03.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/04.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/05.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/06.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/07.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/08.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/meta.json +69 -0
- package/engine/demos/long_scroll/frames/8bit_walk/01.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/02.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/03.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/04.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/05.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/06.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/07.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/08.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/meta.json +69 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/01.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/02.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/03.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/04.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/05.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/06.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/07.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/08.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/meta.json +69 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/01.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/02.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/03.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/04.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/05.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/06.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/07.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/08.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/meta.json +69 -0
- package/engine/demos/long_scroll/frames/monet_throw/01.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/02.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/03.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/04.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/05.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/06.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/07.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/08.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/meta.json +71 -0
- package/engine/demos/long_scroll/frames/monet_walk/01.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/02.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/03.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/04.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/05.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/06.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/07.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/08.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/meta.json +71 -0
- package/engine/demos/long_scroll/lib/acts.js +87 -0
- package/engine/demos/long_scroll/lib/crossing.js +463 -0
- package/engine/demos/long_scroll/lib/world.js +143 -0
- package/engine/demos/long_scroll/lib/xing2_ding.js +42 -0
- package/engine/demos/long_scroll/lib/xing2_jia.js +114 -0
- package/engine/demos/long_scroll/lib/xing2_yi.js +133 -0
- package/engine/demos/long_scroll/segments/02_egypt.js +234 -0
- package/engine/demos/long_scroll/segments/14_8bit.js +118 -0
- package/engine/demos/long_scroll/segments/28_monet.js +86 -0
- package/engine/demos/t1_3b1b/eras.js +11 -0
- package/engine/demos/t1_3b1b/t1_s1.js +101 -0
- package/engine/demos/t1_3b1b/t1_s2.js +74 -0
- package/engine/demos/t1_3b1b/t1_s3.js +65 -0
- package/engine/demos/t2_keynote_ui/eras.js +10 -0
- package/engine/demos/t2_keynote_ui/t2_s1.js +57 -0
- package/engine/demos/t2_keynote_ui/t2_s2.js +70 -0
- package/engine/demos/t2_keynote_ui/t2_s3.js +61 -0
- package/engine/demos/t3_finance_chart/eras.js +11 -0
- package/engine/demos/t3_finance_chart/t3_s1.js +48 -0
- package/engine/demos/t3_finance_chart/t3_s2.js +42 -0
- package/engine/demos/t3_finance_chart/t3_s3.js +51 -0
- package/engine/demos/y1_kurzgesagt/eras.js +10 -0
- package/engine/demos/y1_kurzgesagt/kurzgesagt.js +347 -0
- package/engine/demos/y2_vox/eras.js +10 -0
- package/engine/demos/y2_vox/vox.js +240 -0
- package/engine/demos/y3_whiteboard/eras.js +10 -0
- package/engine/demos/y3_whiteboard/whiteboard.js +162 -0
- package/engine/demos/y4_storytime/eras.js +14 -0
- package/engine/demos/y4_storytime/s1_desk.js +18 -0
- package/engine/demos/y4_storytime/s2_screen.js +82 -0
- package/engine/demos/y4_storytime/s3_react.js +39 -0
- package/engine/demos/y4_storytime/s4_wide.js +16 -0
- package/engine/demos/y4_storytime/s5_close.js +18 -0
- package/engine/demos/y4_storytime/story.js +25 -0
- package/engine/demos/y4_storytime/vo_env.js +2 -0
- package/engine/demos/y4_storytime/world.js +73 -0
- package/engine/demos/y5_kinetic_type/eras.js +16 -0
- package/engine/demos/y5_kinetic_type/k1_title.js +38 -0
- package/engine/demos/y5_kinetic_type/k2_complete.js +55 -0
- package/engine/demos/y5_kinetic_type/k3_chat.js +47 -0
- package/engine/demos/y5_kinetic_type/k4_agent.js +49 -0
- package/engine/demos/y5_kinetic_type/kinetic.js +52 -0
- package/engine/engine.js +277 -0
- package/engine/eras.js +60 -0
- package/engine/eras_gallery.js +115 -0
- package/engine/eras_style_studies.js +20 -0
- package/engine/examples/assets/K/347/272/277/345/233/276_/346/250/252/345/261/217.png +0 -0
- package/engine/examples/assets//347/273/210/347/253/257_for/345/276/252/347/216/257/346/261/202/345/222/214.png +0 -0
- package/engine/examples/assets//347/273/210/347/253/257_sum/346/261/202/345/222/214.png +0 -0
- package/engine/examples/assets//350/220/245/346/224/266/345/233/276_/347/253/226/345/261/217.png +0 -0
- package/engine/examples/assets//350/264/242/347/273/217/345/233/276/350/241/250_/346/250/252/345/261/217.png +0 -0
- package/engine/examples/t1_3b1b.json +19 -0
- package/engine/examples/t2_fullbleed_pulse.json +15 -0
- package/engine/examples/t2_keynote_ui.json +15 -0
- package/engine/examples/t3_finance_chart.json +29 -0
- package/engine/examples/y1_kurzgesagt.json +16 -0
- package/engine/examples/y2_vox.json +14 -0
- package/engine/examples/y3_whiteboard.json +17 -0
- package/engine/examples/y4_storytime.json +52 -0
- package/engine/examples/y5_kinetic_type.json +14 -0
- package/engine/index.html +42 -0
- package/engine/lib/THREE_LICENSE.txt +21 -0
- package/engine/lib/brush.js +149 -0
- package/engine/lib/camera.js +99 -0
- package/engine/lib/chart.js +152 -0
- package/engine/lib/collage.js +97 -0
- package/engine/lib/diagram.js +264 -0
- package/engine/lib/finish.js +70 -0
- package/engine/lib/fonts/AlfaSlabOne-400.woff +0 -0
- package/engine/lib/fonts/Anton-400.woff +0 -0
- package/engine/lib/fonts/ArchivoBlack-400.woff +0 -0
- package/engine/lib/fonts/Bangers-400.woff +0 -0
- package/engine/lib/fonts/Caveat-600.woff +0 -0
- package/engine/lib/fonts/Cinzel-400.woff +0 -0
- package/engine/lib/fonts/Cinzel-700.woff +0 -0
- package/engine/lib/fonts/CormorantGaramond-500.woff +0 -0
- package/engine/lib/fonts/CormorantGaramond-500i.woff +0 -0
- package/engine/lib/fonts/CormorantGaramond-700.woff +0 -0
- package/engine/lib/fonts/EBGaramond-400.woff +0 -0
- package/engine/lib/fonts/EBGaramond-400i.woff +0 -0
- package/engine/lib/fonts/EBGaramond-600.woff +0 -0
- package/engine/lib/fonts/Exo2-800i.woff +0 -0
- package/engine/lib/fonts/GochiHand-400.woff +0 -0
- package/engine/lib/fonts/IMFellEnglish-400.woff +0 -0
- package/engine/lib/fonts/IMFellEnglish-400i.woff +0 -0
- package/engine/lib/fonts/Inter-var.woff +0 -0
- package/engine/lib/fonts/JosefinSans-700.woff +0 -0
- package/engine/lib/fonts/Kalam-700.woff +0 -0
- package/engine/lib/fonts/LICENSES.md +40 -0
- package/engine/lib/fonts/LXGWWenKai-500.woff +0 -0
- package/engine/lib/fonts/LibreBaskerville-400.woff +0 -0
- package/engine/lib/fonts/LibreBaskerville-700.woff +0 -0
- package/engine/lib/fonts/LilitaOne-400.woff +0 -0
- package/engine/lib/fonts/Marcellus-400.woff +0 -0
- package/engine/lib/fonts/NotoSansSC-500.woff +0 -0
- package/engine/lib/fonts/NotoSansSC-700.woff +0 -0
- package/engine/lib/fonts/NotoSansSC-800.woff +0 -0
- package/engine/lib/fonts/NotoSansSC-900.woff +0 -0
- package/engine/lib/fonts/NotoSansSC-sub.woff +0 -0
- package/engine/lib/fonts/NotoSerifJP-600.woff +0 -0
- package/engine/lib/fonts/NotoSerifSC-sub.woff +0 -0
- package/engine/lib/fonts/OFL.txt +94 -0
- package/engine/lib/fonts/PlayfairDisplay-400i.woff +0 -0
- package/engine/lib/fonts/PlayfairDisplay-700.woff +0 -0
- package/engine/lib/fonts/PlayfairDisplay-700i.woff +0 -0
- package/engine/lib/fonts/Poppins-600.woff +0 -0
- package/engine/lib/fonts/Poppins-700.woff +0 -0
- package/engine/lib/fonts/Poppins-800.woff +0 -0
- package/engine/lib/fonts/PressStart2P-400.woff +0 -0
- package/engine/lib/fonts/Righteous-400.woff +0 -0
- package/engine/lib/fonts/RobotoCondensed-var.woff +0 -0
- package/engine/lib/fonts/Rye-400.woff +0 -0
- package/engine/lib/fonts/UnifrakturMaguntia-400.woff +0 -0
- package/engine/lib/fonts/cmunrm.woff +0 -0
- package/engine/lib/fonts/cmunti.woff +0 -0
- package/engine/lib/fonts/index.json +1 -0
- package/engine/lib/fonts.js +56 -0
- package/engine/lib/kit.js +91 -0
- package/engine/lib/media.js +118 -0
- package/engine/lib/medium.js +258 -0
- package/engine/lib/motion.js +111 -0
- package/engine/lib/paint.js +200 -0
- package/engine/lib/palettes.js +57 -0
- package/engine/lib/post.js +108 -0
- package/engine/lib/render.js +189 -0
- package/engine/lib/rig.js +390 -0
- package/engine/lib/rig_huashu.js +171 -0
- package/engine/lib/three.min.js +7 -0
- package/engine/lib/toon.js +186 -0
- package/engine/lib/typo.js +136 -0
- package/engine/lib/ui.js +141 -0
- package/engine/lib/util.js +92 -0
- package/engine/render.py +157 -0
- package/engine/scenes/01_cave.js +364 -0
- package/engine/scenes/02_egypt.js +524 -0
- package/engine/scenes/03_greek.js +448 -0
- package/engine/scenes/04_roman.js +555 -0
- package/engine/scenes/05_gothic.js +522 -0
- package/engine/scenes/06_renaissance.js +338 -0
- package/engine/scenes/08_impressionism.js +206 -0
- package/engine/scenes/09_postimp.js +183 -0
- package/engine/scenes/10_nouveau.js +334 -0
- package/engine/scenes/11_cubism.js +265 -0
- package/engine/scenes/12_bauhaus.js +214 -0
- package/engine/scenes/13_pop.js +269 -0
- package/engine/scenes/14_8bit.js +238 -0
- package/engine/scenes/15_raytrace.js +300 -0
- package/engine/scenes/16_2026.js +565 -0
- package/engine/scenes/17_ink.js +273 -0
- package/engine/scenes/18_klimt.js +227 -0
- package/engine/scenes/19_munch.js +153 -0
- package/engine/scenes/20_dunhuang.js +215 -0
- package/engine/scenes/21_kusama.js +124 -0
- package/engine/scenes/22_constructivism.js +205 -0
- package/engine/scenes/23_dali.js +221 -0
- package/engine/scenes/24_hopper.js +150 -0
- package/engine/scenes/25_ghibli.js +251 -0
- package/engine/scenes/26_vaporwave.js +191 -0
- package/engine/scenes/27_kirby.js +255 -0
- package/engine/scenes/28_monet.js +176 -0
- package/engine/scenes/29_seurat.js +104 -0
- package/engine/scenes/30_matisse.js +151 -0
- package/engine/scenes/31_haring.js +109 -0
- package/engine/scenes/32_rembrandt.js +217 -0
- package/engine/scenes/33_rubberhose.js +219 -0
- package/engine/scenes/34_shadowpuppet.js +195 -0
- package/engine/scenes/35_shinkai.js +197 -0
- package/engine/scenes/36_picasso_blue.js +123 -0
- package/engine/scenes/38_giotto.js +133 -0
- package/engine/scenes/39_piero.js +53 -0
- package/engine/scenes/40_raphael.js +96 -0
- package/engine/scenes/41_michelangelo.js +112 -0
- package/engine/scenes/42_guardi.js +70 -0
- package/engine/scenes/43_lorenzetti.js +145 -0
- package/engine/scenes/44_cortona.js +86 -0
- package/engine/scenes/45_renoir.js +86 -0
- package/engine/scenes/46_pissarro.js +82 -0
- package/engine/scenes/47_sisley.js +94 -0
- package/engine/scenes/48_gauguin.js +107 -0
- package/engine/scenes/49_chagall.js +60 -0
- package/engine/scenes/50_ma_yuan.js +131 -0
- package/engine/scenes/51_persian_miniature.js +149 -0
- package/engine/scenes/52_south_park.js +105 -0
- package/engine/scenes/53_pixar.js +60 -0
- package/engine/scenes/54_ghibli_lab.js +68 -0
- package/engine/scenes/55_dunhuang.js +161 -0
- package/engine/scenes/index.js +28 -0
- package/engine/spec_assets.py +76 -0
- package/engine/transitions.js +929 -0
- package/engine//346/227/266/344/273/243/344/275/234/350/200/205/350/247/204/350/214/203.md +49 -0
- package/lib/host.mjs +754 -0
- package/lib/render-bridge.mjs +543 -0
- package/lib/render-cli.mjs +98 -0
- package/lib/storyboard-lint.mjs +54 -0
- package/manifest.json +52 -0
- package/package.json +8 -4
- package/runtime/package.json +8 -0
- package/skills/biuart/SKILL.md +154 -0
- package/skills/biuart/references/01-/346/213/206/350/247/243.md +55 -0
- package/skills/biuart/references/02-/346/234/272/345/210/266.md +41 -0
- package/skills/biuart/references/03-/344/270/200/345/270/247/345/205/210/350/241/214/345/233/233/346/235/241/350/267/257/347/272/277.md +39 -0
- package/skills/biuart/references/04-/347/272/257/344/273/243/347/240/201/347/273/230/345/210/266.md +92 -0
- package/skills/biuart/references/05-/350/212/202/345/245/217/344/270/216/351/205/215/344/271/220.md +19 -0
- package/skills/biuart/references/06-/345/217/243/346/222/255/351/251/261/345/212/250/347/232/204/350/211/272/346/234/257/347/237/255/347/211/207.md +31 -0
- package/skills/biuart/references/07-/346/255/243/351/235/242/347/273/217/351/252/214.md +196 -0
- package/skills/biuart/references/08-/351/243/216/346/240/274/344/275/234/350/200/205/350/247/204/350/214/203.md +61 -0
- package/skills/biuart/references/09-/350/247/206/351/242/221/345/212/250/347/224/273/350/257/255/346/263/225.md +382 -0
- package/skills/biuart/references/10-/350/247/222/350/211/262.md +34 -0
- package/skills/biuart/references/11-/351/225/277/345/215/267/347/251/277/350/266/212/347/211/207.md +43 -0
- package/skills/biuart/references/12-/345/217/243/346/222/255/346/225/264/347/211/207/344/270/216/347/273/217/351/252/214/345/233/236/346/265/201.md +25 -0
- package/skills/biuart/references/13-/345/217/243/346/222/255/344/270/216/350/257/255/351/237/263/345/244/215/345/210/273.md +7 -0
- package/skills/biuart/references/15-/350/267/250/345/234/272/346/231/257/351/243/216/346/240/274/346/217/220/347/202/274.md +48 -0
- package/skills/biuart/references/16-/351/243/216/346/240/274/346/274/224/347/244/27295/345/210/206/345/215/217/350/256/256.md +103 -0
- package/skills/biuart/references/about-author.md +99 -0
- package/skills/biuart/references/capabilities.md +91 -0
- package/skills/biuart/references/compatibility.md +34 -0
- package/skills/biuart/references/images.md +97 -0
- package/skills/biuart/references/voice-model-review.md +18 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/t1_3b1b.md +122 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/t2_keynote_ui.md +117 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/t3_finance_chart.md +116 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y1_kurzgesagt.md +193 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y2_vox.md +195 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y3_whiteboard.md +190 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y4_storytime.md +166 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y5_kinetic_type.md +158 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y6_presenter_explainer.md +24 -0
- package/skills/biuart/references//345/217/243/346/222/255API.md +125 -0
- package/skills/biuart/references//350/211/262/346/235/277.md +38 -0
- package/skills/biuart/references//350/247/222/350/211/262/347/262/276/347/273/206/345/272/246.md +61 -0
- package/skills/biuart/references//351/225/234/345/244/264/350/241/250/346/250/241/346/235/277.md +80 -0
- package/skills/biuart/references//351/225/234/345/244/264/350/257/255/350/250/200.md +73 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/01_cave.md +46 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/02_egypt.md +64 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/03_greek.md +50 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/04_roman.md +99 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/05_gothic.md +32 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/06_renaissance.md +41 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/08_impressionism.md +34 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/09_postimp.md +17 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/10_nouveau.md +31 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/11_cubism.md +30 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/12_bauhaus.md +30 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/13_pop.md +29 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/14_8bit.md +27 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/15_raytrace.md +32 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/16_2026.md +55 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/17_ink.md +50 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/18_klimt.md +37 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/19_munch.md +42 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/20_dunhuang.md +49 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/21_kusama.md +32 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/22_constructivism.md +35 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/23_dali.md +29 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/24_hopper.md +30 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/25_ghibli.md +55 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/26_vaporwave.md +27 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/27_kirby.md +30 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/28_monet.md +39 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/29_seurat.md +31 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/30_matisse.md +26 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/31_haring.md +22 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/32_rembrandt.md +29 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/33_rubberhose.md +31 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/34_shadowpuppet.md +40 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/35_shinkai.md +34 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/36_picasso_blue.md +25 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/37_rick_morty.md +48 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/38_giotto.md +67 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/39_piero.md +59 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/40_raphael.md +65 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/41_michelangelo.md +65 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/42_guardi.md +65 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/43_lorenzetti.md +69 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/44_cortona.md +66 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/45_renoir.md +66 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/46_pissarro.md +66 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/47_sisley.md +67 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/48_gauguin.md +66 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/49_chagall.md +59 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/50_ma_yuan.md +68 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/51_persian_miniature.md +66 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/52_south_park.md +65 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/53_pixar.md +61 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/55_dunhuang.md +41 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/INDEX.md +89 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/_/350/275/254/345/234/272_/350/277/201/347/247/273/346/265/213/350/257/225.md +53 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/_/351/207/215/345/201/232/350/277/233/345/272/246.md +37 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/_/351/237/263/350/275/250_/350/211/272/346/234/257/345/217/262/351/200/237/351/200/232.md +135 -0
- package/studio/app.js +380 -0
- package/studio/assert-panel-permit.mjs +67 -0
- package/studio/composer.css +465 -0
- package/studio/composer.js +480 -0
- package/studio/index.html +97 -0
- package/studio/optimize-prompt.mjs +84 -0
- package/studio/optimize-prompt.test.mjs +47 -0
- package/studio/recipes.mjs +87 -0
- package/studio/select-menu.js +450 -0
- package/studio/styles.css +71 -0
- package/studio/to-english-prompt.mjs +149 -0
- package/studio/to-english-prompt.test.mjs +72 -0
- package/README.md +0 -3
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// 全片骨架:一条长画卷,N 个世界段首尾相接,花叔从左走到右(横向长卷)。
|
|
2
|
+
// 每个世界段是一个文件 segments/<id>.js,调用 WORLD.add({...});本文件负责:时间轴、主角行进、相机、画风边界、按边界裁开的主角、帽子上的收藏品。
|
|
3
|
+
//
|
|
4
|
+
// 段的接口(坐标都是「段内世界坐标」:x 从 0 到 w,地面线 GROUND=760 是全片统一的,跨边界时脚底在同一高度):
|
|
5
|
+
// WORLD.add({
|
|
6
|
+
// id: '10_monet', year: 1899, name: '莫奈《睡莲》', // 角标:年份+名
|
|
7
|
+
// w: 2400, // 段宽(世界 px),≥ 1900;含走路与互动的空间
|
|
8
|
+
// hero: { walk: 'monet_walk', mat: 'monet', h: 300 }, // 走路帧库(demos/long_scroll/frames/<key>/),材质(XING.actor 的 mat),站立身高
|
|
9
|
+
// acts: [ { at: 1100, dur: 1.6, key: 'monet_throw', frame: u => 帧号, y: lx => 脚底y(可选) } ], // 在段内 x=at 处停下做互动,dur 秒;frame(u) 返回该动作帧库的帧号(u=动作内秒)
|
|
10
|
+
// ground: lx => y, // 可选:地面高度函数(桥面、台阶),两端必须回到 GROUND
|
|
11
|
+
// back(c, s), front(c, s), // 画背景/前景。s = { camX(段内), t(全片秒), lt(进段后秒), hx(主角段内x), hy, act:{i, u}|null, W:1920, H:1080 }
|
|
12
|
+
// // c 已经裁在本段的带里;世界坐标到屏幕 = lx - s.camX
|
|
13
|
+
// seam(c, y, sx, t), // 可选:进入本段的那条边界怎么画(本段的签名语言:纸边、浪花、金箔、像素…),sx = 边界在屏幕上的 x
|
|
14
|
+
// item: { draw(c, x, y, s, t) , gotAt: 段内秒 } // 本段送给帽子的收藏品(用本段画风画),从 gotAt 起出现在帽子上,之后跟着他穿越所有世界
|
|
15
|
+
// });
|
|
16
|
+
// 预览:index.html?film=demos/long_scroll&only=28_monet,14_8bit 只排这几段(世界从 0 开始);?from=秒
|
|
17
|
+
(() => {
|
|
18
|
+
const W = 1920, H = 1080, { clamp, lerp, ss } = U;
|
|
19
|
+
const WD = window.WORLD = { segs: [], GROUND: 760, V: +(new URLSearchParams(location.search).get('v') || 560), HERO_H: 300 }; // 步速:?v=560 可试更快的全片节奏
|
|
20
|
+
WD.add = seg => { WD.segs.push(seg); };
|
|
21
|
+
WD.START_X = 320; // 开场主角从段内 x=320 起步(画面左侧留出世界)
|
|
22
|
+
|
|
23
|
+
// 布局:按 FILM_ORDER(demos/long_scroll/eras.js 里)排;?only= 过滤
|
|
24
|
+
WD.layout = () => {
|
|
25
|
+
const q = new URLSearchParams(location.search), only = q.get('only');
|
|
26
|
+
let segs = (window.FILM_ORDER || WD.segs.map(s => s.id)).map(id => WD.segs.find(s => s.id === id)).filter(Boolean);
|
|
27
|
+
if (only) { const set = only.split(','); segs = segs.filter(s => set.includes(s.id)); }
|
|
28
|
+
let x = 0, t = 0; const plan = [];
|
|
29
|
+
for (const s of segs) {
|
|
30
|
+
s.x0 = x; s.x1 = x + s.w;
|
|
31
|
+
// 主角时间表:段内按 acts 切成「走→停做动作→走」
|
|
32
|
+
const acts = (s.acts || []).slice().sort((a, b) => a.at - b.at); let lx = plan.length ? 0 : Math.min(WD.START_X, (acts[0] ? acts[0].at : s.w) - 60); // 开场主角从画内起步,第 0 帧整个人可见(v1 审片:开头只露半个身子)
|
|
33
|
+
s.tIn = t;
|
|
34
|
+
for (const a of acts) { t += (a.at - lx) / (a.v || s.v || WD.V); plan.push({ seg: s, kind: 'walk', t0: t - (a.at - lx) / (a.v || s.v || WD.V), t1: t, x0: x + lx, x1: x + a.at });
|
|
35
|
+
plan.push({ seg: s, kind: 'act', act: a, t0: t, t1: t + a.dur, x0: x + a.at, x1: x + a.at + (a.dx || 0) }); t += a.dur; lx = a.at + (a.dx || 0); }
|
|
36
|
+
const tw = (s.w - lx) / (s.v || WD.V); plan.push({ seg: s, kind: 'walk', t0: t, t1: t + tw, x0: x + lx, x1: x + s.w }); t += tw;
|
|
37
|
+
s.tOut = t; x += s.w;
|
|
38
|
+
}
|
|
39
|
+
WD.plan = plan; WD.total = t + (window.FILM_TAIL || 0); WD.active = segs; return WD;
|
|
40
|
+
};
|
|
41
|
+
WD.heroAt = t => {
|
|
42
|
+
const p = WD.plan.find(q => t >= q.t0 && t < q.t1) || WD.plan[WD.plan.length - 1];
|
|
43
|
+
const u = clamp((t - p.t0) / Math.max(1e-6, p.t1 - p.t0));
|
|
44
|
+
const x = lerp(p.x0, p.x1, p.kind === 'walk' ? u : (p.act.move ? p.act.move(u) : u));
|
|
45
|
+
return { x, p, u: t - p.t0 };
|
|
46
|
+
};
|
|
47
|
+
const segAt = x => WD.active.find(s => x >= s.x0 && x < s.x1) || WD.active[WD.active.length - 1];
|
|
48
|
+
// 边界线:轻微波浪(每条边界相位不同)
|
|
49
|
+
WD.seamX = (s, y) => s.x0 + 22 * Math.sin(y * 0.011 + s.x0 * 0.001) + 8 * Math.sin(y * 0.047 + s.x0 * 0.003);
|
|
50
|
+
const band = (c, a, b, camX) => { c.beginPath();
|
|
51
|
+
for (let y = -10; y <= H + 10; y += 20) c.lineTo(a ? WD.seamX(a, y) - camX : -60, y);
|
|
52
|
+
for (let y = H + 10; y >= -10; y -= 20) c.lineTo(b ? WD.seamX(b, y) - camX : W + 60, y);
|
|
53
|
+
c.closePath(); c.clip(); };
|
|
54
|
+
|
|
55
|
+
WD.draw = (c, t) => {
|
|
56
|
+
if (!WD.plan) WD.layout();
|
|
57
|
+
const hero = WD.heroAt(t), hs = segAt(hero.x);
|
|
58
|
+
// 相机:主角在画面左 38%,做动作时镜头往前让出空间(act.lead 可调),平滑
|
|
59
|
+
const camX = WD.camAt(t);
|
|
60
|
+
const vis = WD.active.filter(s => s.x1 > camX - 80 && s.x0 < camX + W + 80);
|
|
61
|
+
const ctxFor = s => ({ camX: camX - s.x0, t, lt: t - s.tIn, hx: hero.x - s.x0, act: hero.p.seg === s && hero.p.kind === 'act' ? { a: hero.p.act, u: hero.u } : null, W, H, G: WD.GROUND });
|
|
62
|
+
// 背景
|
|
63
|
+
for (const s of vis) { const k = WD.active.indexOf(s), nx = WD.active[k + 1]; c.save(); band(c, k ? s : null, nx, camX); s.back(c, ctxFor(s)); c.restore(); }
|
|
64
|
+
// 主角:按边界裁开,每段用自己的画风帧
|
|
65
|
+
for (const s of vis) {
|
|
66
|
+
if (hero.x < s.x0 - 200 || hero.x > s.x1 + 200) continue;
|
|
67
|
+
const k = WD.active.indexOf(s), nx = WD.active[k + 1], S = ctxFor(s);
|
|
68
|
+
c.save(); band(c, k ? s : null, nx, camX);
|
|
69
|
+
WD.drawHero(c, s, S, hero);
|
|
70
|
+
c.restore();
|
|
71
|
+
}
|
|
72
|
+
// 前景
|
|
73
|
+
for (const s of vis) { if (!s.front) continue; const k = WD.active.indexOf(s), nx = WD.active[k + 1]; c.save(); band(c, k ? s : null, nx, camX); s.front(c, ctxFor(s)); c.restore(); }
|
|
74
|
+
// 边界本身(属于进入的那一段)
|
|
75
|
+
for (const s of vis) { const k = WD.active.indexOf(s); if (!k) continue; c.save(); for (let y = -10; y < H + 10; y += 6) { const sx = WD.seamX(s, y) - camX; if (sx < -120 || sx > W + 120) break; if (s.seam) s.seam(c, y, sx, t); else { c.fillStyle = '#f4ead2'; c.fillRect(sx - 6, y, 8 + U.hash(y, 3) * 6, 7); } } c.restore(); }
|
|
76
|
+
// 跨边界的颜料迸散
|
|
77
|
+
for (const s of WD.active) { const k = WD.active.indexOf(s); if (!k) continue; const tc = WD.crossT(s); const a = (t - tc) / 0.5; if (a > 0 && a < 1 && XING.burst) XING.burst(c, s.x0 - camX, WD.GROUND - WD.HERO_H * 0.5, a, 160, s.burst || 'monet', k + 3); }
|
|
78
|
+
// 角标:年份+名(随所在段切换,切换时淡入)
|
|
79
|
+
if (window.FILM_LABEL !== false) WD.label(c, hs, t);
|
|
80
|
+
};
|
|
81
|
+
// 相机:只进不退(v1 审片:互动结尾镜头往回收 68–256px,17 段都有,读成「倒带」)。
|
|
82
|
+
// 目标 = 主角 x − 720 + 互动时向前让出的 lead(只升不降);取历史最大值保证单调;再做高斯平滑(单调信号卷积正核仍单调),停与起都不顿。
|
|
83
|
+
WD.camAt = t => {
|
|
84
|
+
if (!WD._cam) {
|
|
85
|
+
const R = 120, n = Math.ceil(WD.total * R) + 2, raw = new Float64Array(n), last = WD.active[WD.active.length - 1];
|
|
86
|
+
let mx = -1e9;
|
|
87
|
+
for (let k = 0; k < n; k++) { const tt = k / R, h = WD.heroAt(tt);
|
|
88
|
+
const lead = h.p.kind === 'act' ? (h.p.act.lead ?? 160) * ss(0, 0.6, h.u) : 0;
|
|
89
|
+
mx = Math.max(mx, h.x - 720 + lead); raw[k] = mx; }
|
|
90
|
+
const sig = 0.14 * R, K = Math.ceil(sig * 3), ker = []; let ks = 0; for (let j = -K; j <= K; j++) { const w = Math.exp(-j * j / (2 * sig * sig)); ker.push(w); ks += w; }
|
|
91
|
+
const sm = new Float64Array(n);
|
|
92
|
+
for (let k = 0; k < n; k++) { let a = 0; for (let j = -K; j <= K; j++) a += ker[j + K] * raw[clamp(k + j, 0, n - 1)]; sm[k] = clamp(a / ks, 0, Math.max(0, last.x1 - W)); }
|
|
93
|
+
WD._cam = { R, sm };
|
|
94
|
+
}
|
|
95
|
+
const { R, sm } = WD._cam, f = clamp(t * R, 0, sm.length - 1), k = Math.floor(f), u = f - k;
|
|
96
|
+
return sm[k] + (sm[Math.min(k + 1, sm.length - 1)] - sm[k]) * u;
|
|
97
|
+
};
|
|
98
|
+
WD.crossT = s => { const p = WD.plan.find(q => q.seg === s); return p ? p.t0 : 0; };
|
|
99
|
+
// 第 k 个互动开始的全片时刻(段里的事件请用它或 s.act.u 定时,不要写死「进段后第几秒」——全片步速会在合成时统一调整)
|
|
100
|
+
WD.actT = (s, k = 0) => { const ps = WD.plan.filter(q => q.seg === s && q.kind === 'act'); return ps[k] ? ps[k].t0 : s.tIn; };
|
|
101
|
+
// 纪念品落帽时刻:item.atAct = 互动开始后第几秒(推荐);旧写法 item.gotAt = 进段后第几秒(步速变了会错位)
|
|
102
|
+
WD.itemT = g => g.item.atAct != null ? WD.actT(g, g.item.act || 0) + g.item.atAct : g.tIn + (g.item.gotAt ?? (g.tOut - g.tIn) * 0.7);
|
|
103
|
+
|
|
104
|
+
// 主角:画风帧+帽子上的收藏品
|
|
105
|
+
WD.drawHero = (c, s, S, hero) => {
|
|
106
|
+
const H0 = s.hero || {}, h = H0.h || WD.HERO_H, lx = hero.x - s.x0;
|
|
107
|
+
const gy = (hero.p.kind === 'act' && hero.p.act.y) ? hero.p.act.y(lx, hero.u) : (s.ground ? s.ground(lx) : WD.GROUND);
|
|
108
|
+
let key = H0.walk, i;
|
|
109
|
+
const walked = WD.plan.filter(q => q.kind === 'walk' && q.t0 <= S.t).reduce((a, q) => a + Math.min(S.t, q.t1) - q.t0, 0);
|
|
110
|
+
if (hero.p.kind === 'act' && hero.p.seg === s) { key = hero.p.act.key || H0.walk; i = hero.p.act.frame ? hero.p.act.frame(hero.u) : 0; }
|
|
111
|
+
else { const n = XING.nFrames(key); i = Math.floor(walked * (H0.fps || 8.5) * WD.V / 330) % n; } // 步频随步速同比例 // 步频:8.5 帧/秒,同一相位跨边界
|
|
112
|
+
const o = { x: hero.x - S.camX - s.x0 + s.x0 - s.x0, y: gy, h, mat: H0.mat || 'none', t: S.t, wx: hero.x, shadow: H0.shadow ?? 'rgba(0,0,0,.14)', flip: H0.flip };
|
|
113
|
+
o.x = lx - S.camX;
|
|
114
|
+
if (H0.draw) H0.draw(c, key, i, o, S); else XING.actor(c, key, i, o);
|
|
115
|
+
// 收藏品:按获得顺序叠在帽顶;每件用它来自的那个世界的画风画(纪念品集合)
|
|
116
|
+
// 段可以接管:s.hideHatItems(S) 返回 true 时这里不画(帽子被叫飞、摘帽放桌上时由段自己画,用 WD.drawHatStack)
|
|
117
|
+
if (window.FILM_HAT_ITEMS !== true) return; // 帽上纪念品默认关闭,保持角色轮廓清晰
|
|
118
|
+
if (s.hideHatItems && s.hideHatItems(S)) return;
|
|
119
|
+
const [tx, ty] = XING.hatTop(key, i, o); WD.drawHatStack(c, tx, ty + 4, h, S.t);
|
|
120
|
+
};
|
|
121
|
+
// 在 (x, y) 处从下往上画到时刻 t 为止已获得的全部纪念品;返回堆顶 y。段接管帽子时调用它(例如帽子飞起时,传帽子的位置)
|
|
122
|
+
WD.drawHatStack = (c, tx, y, h, t, rot = 0) => {
|
|
123
|
+
c.save(); c.translate(tx, y); c.rotate(rot); y = 0; tx = 0; const S = { t };
|
|
124
|
+
for (const g of WD.active) { if (!g.item) continue; const tg = WD.itemT(g); if (S.t < tg) continue;
|
|
125
|
+
const pop = ss(tg, tg + 0.25, S.t), sc = (g.item.s || 1) * (0.6 + 0.4 * pop) * h / 300;
|
|
126
|
+
const wob = Math.sin(S.t * 6 + g.x0) * 2 * ss(tg, tg + 0.3, S.t);
|
|
127
|
+
c.save(); c.translate(tx + wob, y); g.item.draw(c, 0, 0, sc, S.t); c.restore();
|
|
128
|
+
y -= (g.item.hgt || 26) * sc; }
|
|
129
|
+
c.restore(); return y;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
WD.label = (c, s, t) => {
|
|
133
|
+
const a = ss(s.tIn, s.tIn + 0.35, t); if (!s.year) return;
|
|
134
|
+
const yr = typeof s.year === 'number' ? (s.year < 0 ? `${-s.year} BC` : String(s.year)) : s.year;
|
|
135
|
+
c.save(); c.globalAlpha = a; c.textAlign = 'right'; c.textBaseline = 'alphabetic';
|
|
136
|
+
c.font = '600 54px "Poppins-700", sans-serif'; const w1 = c.measureText(yr).width;
|
|
137
|
+
c.font = '30px "LXGWWenKai-500", sans-serif'; const w2 = c.measureText(s.name || '').width;
|
|
138
|
+
const bw = Math.max(w1, w2) + 48; // 半透明纸片底:跨世界时角标始终可读(审片建议)
|
|
139
|
+
c.fillStyle = 'rgba(250,246,236,.78)'; c.beginPath(); c.roundRect(1890 - bw, 50, bw, 118, 14); c.fill();
|
|
140
|
+
c.fillStyle = 'rgba(30,30,40,.88)'; c.font = '600 54px "Poppins-700", sans-serif'; c.fillText(yr, 1866, 108);
|
|
141
|
+
c.font = '30px "LXGWWenKai-500", sans-serif'; c.fillText(s.name || '', 1866, 152); c.restore();
|
|
142
|
+
};
|
|
143
|
+
})();
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// 一组段共用的小工具(完整片子里 27_kirby、13_pop、14_8bit、25_ghibli、26_vaporwave、16_2026 共用;本示范只用到 14_8bit)。不改 world.js / crossing.js。
|
|
2
|
+
// 段文件开头用 D4.need() 同步加载本文件(index.html 不用改)。
|
|
3
|
+
// D4.hero(seg, s) 复刻 world.js drawHero 的取帧逻辑:返回 { key, i, o:{x,y,h}, hat:[x,y], act, u }(屏幕坐标),front 里画道具对准手/帽子用
|
|
4
|
+
// D4.since(seg, s, k) 距本段第 k 个互动开始的秒数(互动前为负)——段内事件一律相对互动定时,步速改了也不错位
|
|
5
|
+
// D4.frameCanvas(key, i, tag, fn) 取第 i 帧的原图,交给 fn(g, im) 加工成新画布并缓存(画风材质:像素化、海报化、霓虹描边…)
|
|
6
|
+
// D4.drawFrame(c, key, i, o, cv) 按 XING.actor 的锚点规则把加工后的帧画到 (o.x, o.y),cv.pad 为加工时四周留的边
|
|
7
|
+
(() => {
|
|
8
|
+
const D4 = window.D4 = {};
|
|
9
|
+
D4.SCALE = {}; // 某些互动帧库(全身+胸像、站+坐混排)的 ref_h 被拉偏:按 key 整套补一个缩放,D4.hero 与 hero.draw 共用
|
|
10
|
+
const { clamp } = U;
|
|
11
|
+
D4.hero = (seg, s) => {
|
|
12
|
+
const WD = WORLD, hero = WD.heroAt(s.t), H0 = seg.hero || {};
|
|
13
|
+
let key = H0.walk, i;
|
|
14
|
+
if (hero.p.kind === 'act' && hero.p.seg === seg) { key = hero.p.act.key || H0.walk; i = hero.p.act.frame ? hero.p.act.frame(hero.u) : 0; }
|
|
15
|
+
else { const walked = WD.plan.filter(q => q.kind === 'walk' && q.t0 <= s.t).reduce((a, q) => a + Math.min(s.t, q.t1) - q.t0, 0); i = Math.floor(walked * (H0.fps || 8.5) * WD.V / 330) % XING.nFrames(key); } // 与 world.js 同:步频随步速同比例
|
|
16
|
+
const lx = hero.x - seg.x0;
|
|
17
|
+
const gy = (hero.p.kind === 'act' && hero.p.seg === seg && hero.p.act.y) ? hero.p.act.y(lx, hero.u) : (seg.ground ? seg.ground(lx) : WD.GROUND);
|
|
18
|
+
const o = { x: lx - s.camX, y: gy, h: H0.h || WD.HERO_H, scale: D4.SCALE[key] || 1 };
|
|
19
|
+
return { key, i, o, lx, hat: XING.hatTop(key, i, o), act: hero.p.kind === 'act' && hero.p.seg === seg ? hero.p.act : null, u: hero.u };
|
|
20
|
+
};
|
|
21
|
+
const FC = {};
|
|
22
|
+
D4.img = (key, i) => window.IMG[`demos/long_scroll/frames/${key}/${XING.SPR[key].meta.frames[i].file}`];
|
|
23
|
+
D4.frameCanvas = (key, i, tag, fn) => {
|
|
24
|
+
const k = key + '|' + i + '|' + tag; if (FC[k]) return FC[k];
|
|
25
|
+
const im = D4.img(key, i); const cv = fn(im); FC[k] = cv; return cv;
|
|
26
|
+
};
|
|
27
|
+
D4.drawFrame = (c, key, i, o, cv, alpha) => {
|
|
28
|
+
const f = XING.SPR[key].meta.frames[i], s = o.h / XING.SPR[key].meta.ref_h * (o.scale || 1), pad = cv.pad || 0, k = cv.k || 1;
|
|
29
|
+
const fx = o.flip ? -1 : 1;
|
|
30
|
+
c.save(); if (alpha != null) c.globalAlpha *= alpha;
|
|
31
|
+
if (o.shadow) { c.fillStyle = o.shadow; c.beginPath(); c.ellipse(o.x, o.y + 2, o.h * 0.2, o.h * 0.035, 0, 0, Math.PI * 2); c.fill(); }
|
|
32
|
+
c.translate(o.x, o.y); c.scale(fx, 1);
|
|
33
|
+
if (cv.pixel) c.imageSmoothingEnabled = false;
|
|
34
|
+
c.drawImage(cv, -(f.ax + pad) * s, -(f.ay + pad) * s, cv.width / k * s, cv.height / k * s);
|
|
35
|
+
c.restore();
|
|
36
|
+
};
|
|
37
|
+
// 一个 im → 留边画布
|
|
38
|
+
D4.padded = (im, pad) => { const cv = PAINT.canvas(im.width + 2 * pad, im.height + 2 * pad); cv.getContext('2d').drawImage(im, pad, pad); cv.pad = pad; return cv; };
|
|
39
|
+
// 抛物线:从 a 到 b(u 0..1),返回 [x,y,u]
|
|
40
|
+
D4.arc = (a, b, u, peak = 200) => { const e = clamp(u); return [a[0] + (b[0] - a[0]) * e, a[1] + (b[1] - a[1]) * e - Math.sin(e * Math.PI) * peak, e]; };
|
|
41
|
+
D4.since = (seg, s, k = 0) => s.t - WORLD.actT(seg, k);
|
|
42
|
+
})();
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// 一组段共用的小工具(完整片子里 01_cave 02_egypt 03_greek 20_dunhuang 17_ink 05_gothic 共用;本示范只用到 02_egypt)。各段文件开头用 XJ_LOAD 同步加载,不进 index.html。
|
|
2
|
+
// XJ.actor(c, key, i, o, name, proc) 用本段材质画主角帧:proc(g, w, h, i, img) 在「帧+pad」画布上做该画风的材料处理,结果按 key/帧/name 缓存
|
|
3
|
+
// XJ.texInside(g, w, h, draw, op) 纹理只乘在角色上(纹理层 destination-in 剪影 → multiply 回去),白帽不变灰:纹理请用接近白的底
|
|
4
|
+
// XJ.ta(seg, s) 互动内秒(全片时刻 − WORLD.actT(seg);互动前为负)——段里所有事件都按它定时,步速改了也对得上
|
|
5
|
+
// XJ.hatNow(seg, s) 互动进行中:帽顶在屏幕上的位置
|
|
6
|
+
// XJ.arc(p0, p1, q, lift) 两点间抛物线
|
|
7
|
+
// XJ.actFrame(table) [[秒, 帧], ...] → frame(u) 函数(u = 动作内秒)
|
|
8
|
+
(() => {
|
|
9
|
+
if (window.XJ) return;
|
|
10
|
+
const { clamp, lerp } = U, P = PAINT, TAU = Math.PI * 2;
|
|
11
|
+
const XJ = window.XJ = {};
|
|
12
|
+
const MATS = {};
|
|
13
|
+
XJ.mat = (key, i, name, proc, pad = 6) => {
|
|
14
|
+
const k = key + '|' + i + '|' + name; if (MATS[k]) return MATS[k];
|
|
15
|
+
const S = XING.SPR[key], im = S.imgs[i];
|
|
16
|
+
const out = P.canvas(im.width + 2 * pad, im.height + 2 * pad), g = out.getContext('2d', { willReadFrequently: true });
|
|
17
|
+
g.drawImage(im, pad, pad);
|
|
18
|
+
if (proc) proc(g, out.width, out.height, i, im, pad);
|
|
19
|
+
out.pad = pad; MATS[k] = out; return out;
|
|
20
|
+
};
|
|
21
|
+
XJ.actor = (c, key, i, o, name, proc) => {
|
|
22
|
+
XING.hatTop(key, i, o); // 触发 crossing.js 里的 ensure(读帧、算帽顶)
|
|
23
|
+
const S = XING.SPR[key], f = S.meta.frames[i], im = XJ.mat(key, i, name, proc), pad = im.pad, s = o.h / S.meta.ref_h * (o.scale || 1);
|
|
24
|
+
const x0 = o.x - (f.ax + pad) * s, y0 = o.y - (f.ay + pad) * s;
|
|
25
|
+
c.save(); if (o.alpha != null) c.globalAlpha *= o.alpha;
|
|
26
|
+
if (o.shadow) { c.fillStyle = o.shadow; c.beginPath(); c.ellipse(o.x, o.y + 2, o.h * 0.2, o.h * 0.035, 0, 0, TAU); c.fill(); }
|
|
27
|
+
if (!o.lock) c.drawImage(im, x0, y0, im.width * s, im.height * s);
|
|
28
|
+
else { // 脚底锁:上半身照常(平滑);膝盖往下用一个连续的错切把下半段平移到 o.lock,鞋(底部 9%)整块平移满——支撑脚贴住地面不打滑
|
|
29
|
+
const iw = im.width, foot = f.ay + pad, cut = Math.max(0, foot - S.meta.ref_h * 0.4), full = foot - S.meta.ref_h * 0.09;
|
|
30
|
+
c.drawImage(im, 0, 0, iw, cut, x0, y0, iw * s, cut * s);
|
|
31
|
+
c.save(); c.beginPath(); c.rect(x0 - 200, y0 + cut * s, iw * s + 400, (full - cut) * s); c.clip();
|
|
32
|
+
c.translate(0, y0 + cut * s); c.transform(1, 0, o.lock / ((full - cut) * s), 1, 0, 0); c.drawImage(im, x0, -cut * s, iw * s, im.height * s); c.restore();
|
|
33
|
+
c.drawImage(im, 0, full, iw, im.height - full, x0 + o.lock, y0 + full * s, iw * s, (im.height - full) * s);
|
|
34
|
+
}
|
|
35
|
+
c.restore();
|
|
36
|
+
};
|
|
37
|
+
// 走路脚底锁的平移量(屏幕 px)。st = 每帧支撑脚相对锚点的 x(身高 300 时,从走路帧逐帧量出),fps = 该帧库在步速 330 时的帧率(与 hero.fps 一致)。
|
|
38
|
+
// 原理:一个半周期(4 帧)里支撑脚的世界位置不变;平滑行进的身体相对它走了 V·τ,画里的脚相对锚点退了 st0−stj,两者之差就是这一刻该把脚挪回去的量。
|
|
39
|
+
// 段两端 220px 内渐隐到 0(跨边界时两边帧库不同,避免左右半身错位)。
|
|
40
|
+
XJ.walkLock = (seg, S, i, st, fps, h = 300) => {
|
|
41
|
+
if (S.act) return 0; const t = S.t, P0 = WORLD.plan.find(q => t >= q.t0 && t < q.t1); if (!P0 || P0.kind !== 'walk') return 0;
|
|
42
|
+
const walked = WORLD.plan.filter(q => q.kind === 'walk' && q.t0 <= t).reduce((a, q) => a + Math.min(t, q.t1) - q.t0, 0);
|
|
43
|
+
const rate = fps * WORLD.V / 330, ph = walked * rate, h0 = Math.floor(ph / 4) * 4, tau = (ph - h0) / rate;
|
|
44
|
+
const k0 = h0 % 8, n = st.length; const lock = ((st[k0 % n] - st[i % n]) * h / 300 - WORLD.V * tau);
|
|
45
|
+
const L = U.ss(0, 220, S.hx) * U.ss(0, 220, seg.w - S.hx);
|
|
46
|
+
return clamp(lock, -70, 40) * L;
|
|
47
|
+
};
|
|
48
|
+
XJ.texInside = (g, w, h, draw, op = 'multiply', alpha = 1, skipWhite = false) => {
|
|
49
|
+
const T = P.canvas(w, h), tg = T.getContext('2d');
|
|
50
|
+
draw(tg, w, h);
|
|
51
|
+
tg.globalCompositeOperation = 'destination-in'; tg.drawImage(g.canvas, 0, 0);
|
|
52
|
+
if (skipWhite) { tg.globalCompositeOperation = 'destination-out'; tg.drawImage(XJ.whiteMask(g, w, h), 0, 0); } // 白帽白T不上做旧(角色设定:帽子纯白)
|
|
53
|
+
g.save(); g.globalCompositeOperation = op; g.globalAlpha = alpha; g.drawImage(T, 0, 0); g.restore();
|
|
54
|
+
};
|
|
55
|
+
// 逐像素改色(白帽护白等):fn(r,g,b,a) → [r,g,b] 或 null
|
|
56
|
+
XJ.remap = (g, w, h, fn) => { const d = g.getImageData(0, 0, w, h), q = d.data;
|
|
57
|
+
for (let k = 0; k < q.length; k += 4) { if (q[k + 3] < 8) continue; const r = fn(q[k], q[k + 1], q[k + 2], q[k + 3], (k >> 2) % w, (k >> 2) / w | 0); if (r) { q[k] = r[0]; q[k + 1] = r[1]; q[k + 2] = r[2]; if (r.length > 3) q[k + 3] = r[3]; } }
|
|
58
|
+
g.putImageData(d, 0, 0); };
|
|
59
|
+
// 白色区域蒙版(亮且低饱和),给 texInside 挖掉
|
|
60
|
+
XJ.whiteMask = (g, w, h) => { const d = g.getImageData(0, 0, w, h).data, M = P.canvas(w, h), mg = M.getContext('2d'), o = mg.createImageData(w, h);
|
|
61
|
+
for (let k = 0; k < w * h; k++) { const r = d[k * 4], gg = d[k * 4 + 1], b = d[k * 4 + 2], mx = Math.max(r, gg, b), mn = Math.min(r, gg, b); o.data[k * 4 + 3] = d[k * 4 + 3] > 100 && mx > 200 && mx - mn < 50 ? 255 : 0; }
|
|
62
|
+
mg.putImageData(o, 0, 0); return M; };
|
|
63
|
+
// 帽区(上 frac)里的做旧斑点抹白:周围 5×5 有六成以上是白的、自身又不暗的像素=白帽上的斑点 → 换成纯白(帽檐的勾线周围白不足六成,保留)
|
|
64
|
+
XJ.cleanHat = (g, w, h, frac = 0.34, white = [250, 248, 242], R = 2, wmin = 205) => { const D = g.getImageData(0, 0, w, h), d = D.data, Y = Math.floor(h * frac), wt = new Uint8Array(w * Y);
|
|
65
|
+
for (let y = 0; y < Y; y++) for (let x = 0; x < w; x++) { const k = (y * w + x) * 4, mx = Math.max(d[k], d[k + 1], d[k + 2]), mn = Math.min(d[k], d[k + 1], d[k + 2]); wt[y * w + x] = d[k + 3] > 100 && mx > wmin && mx - mn < 45 ? 1 : 0; }
|
|
66
|
+
const need = Math.ceil((2 * R + 1) ** 2 * 0.55);
|
|
67
|
+
for (let y = R; y < Y - R; y++) for (let x = R; x < w - R; x++) { const k = (y * w + x) * 4; if (d[k + 3] < 100 || wt[y * w + x]) continue; const lum = d[k] * 0.3 + d[k + 1] * 0.59 + d[k + 2] * 0.11; if (lum < 95) continue;
|
|
68
|
+
let n = 0; for (let dy = -R; dy <= R; dy++) for (let dx = -R; dx <= R; dx++) n += wt[(y + dy) * w + x + dx]; if (n >= need) { d[k] = white[0]; d[k + 1] = white[1]; d[k + 2] = white[2]; } }
|
|
69
|
+
g.putImageData(D, 0, 0); };
|
|
70
|
+
XJ.ta = (seg, s, k = 0) => s.t - WORLD.actT(seg, k);
|
|
71
|
+
XJ.hatNow = (seg, s) => {
|
|
72
|
+
const a = s.act; if (!a) return null;
|
|
73
|
+
const key = a.a.key, i = a.a.frame ? a.a.frame(a.u) : 0, h = (seg.hero && seg.hero.h) || WORLD.HERO_H;
|
|
74
|
+
const x = s.hx - s.camX, y = a.a.y ? a.a.y(s.hx, a.u) : (seg.ground ? seg.ground(s.hx) : WORLD.GROUND);
|
|
75
|
+
return XING.hatTop(key, i, { x, y, h });
|
|
76
|
+
};
|
|
77
|
+
XJ.arc = (p0, p1, q, lift = 120) => [lerp(p0[0], p1[0], q), lerp(p0[1], p1[1], q) - 4 * lift * q * (1 - q)];
|
|
78
|
+
// 停下/起步不再一帧刹死、一帧满速:互动开头 Tb 秒从步速匀减到 0、末尾 Tb 秒从 0 匀加回步速(镜头跟着他,所以镜头也是缓停缓起)。
|
|
79
|
+
// extra(u) 可叠加互动自己的位移(如被甩飞),extraDx 是它的总位移。返回 { dx, move } 直接展开进 acts 项(并配 lead: 0,免得镜头先冲再倒带)。
|
|
80
|
+
XJ.glide = (dur, Tb = 0.22, extra = null, extraDx = 0) => { const V = WORLD.V, d1 = V * Tb / 2, dx = 2 * d1 + extraDx;
|
|
81
|
+
const pos = u => (u < Tb ? V * (u - u * u / (2 * Tb)) : d1) + (u > dur - Tb ? V * (u - (dur - Tb)) ** 2 / (2 * Tb) : 0) + (extra ? extra(u) : 0);
|
|
82
|
+
return { dx, move: f => dx ? pos(f * dur) / dx : 0 }; };
|
|
83
|
+
// 缓停/缓起窗口里不播互动帧,接着播走路帧:步频随速度降到 0 再升回来,与互动前后的走路相位首尾相接
|
|
84
|
+
XJ.walkedAt = t => WORLD.plan.filter(q => q.kind === 'walk' && q.t0 <= t).reduce((a, q) => a + Math.min(t, q.t1) - q.t0, 0);
|
|
85
|
+
XJ.glideWalk = (S, dur, fps, n = 8, Tb = 0.22, head = true, tail = true) => {
|
|
86
|
+
if (!S.act) return null; const u = S.act.u, V = WORLD.V, rate = fps * V / 330, ph0 = XJ.walkedAt(S.t - u) * rate, d1 = V * Tb / 2;
|
|
87
|
+
let ph = null;
|
|
88
|
+
if (head && u < Tb) ph = ph0 + (V * (u - u * u / (2 * Tb))) / V * rate;
|
|
89
|
+
else if (tail && u > dur - Tb) { const w = u - (dur - Tb); ph = ph0 - (d1 - V * w * w / (2 * Tb)) / V * rate; }
|
|
90
|
+
return ph == null ? null : ((Math.floor(ph) % n) + n) % n;
|
|
91
|
+
};
|
|
92
|
+
XJ.actFrame = tab => u => { let f = tab[0][1]; for (const [t0, fr] of tab) if (u >= t0) f = fr; return f; };
|
|
93
|
+
// 只在本段可见范围内画:lx 世界坐标 → 屏幕
|
|
94
|
+
XJ.vis = (s, lx, pad = 300) => lx - s.camX > -pad && lx - s.camX < s.W + pad;
|
|
95
|
+
// 跨边界的颜料迸散:crossing.js 只有 monet / vg 两套颜色。这里只给本组的 burst 名('jia_*')换色,其余照旧交回原函数(不改 crossing.js)
|
|
96
|
+
XJ.BURST = {
|
|
97
|
+
jia_cave: ['#9c3520', '#c9772f', '#21150d', '#d49a5a', '#74261a'],
|
|
98
|
+
jia_egypt: ['#1f4fa0', '#c0392b', '#2f8a5a', '#e0b030', '#1a1a1a'],
|
|
99
|
+
jia_greek: ['#1a1410', '#c8602a', '#e8d2b0', '#7a2a1a'],
|
|
100
|
+
jia_dunhuang: ['#2f5f8f', '#3f8f6e', '#a5452f', '#ecdfc4', '#d8b05a'],
|
|
101
|
+
jia_ink: ['#1a1a1a', '#3a3a3a', '#6a6a6a', '#9a9a9a', '#b8322a'],
|
|
102
|
+
jia_gothic: ['#e8c45a', '#1f3f9f', '#b8202a', '#f6e6a8', '#2f7a3a'],
|
|
103
|
+
};
|
|
104
|
+
if (XING.burst && !XING.burst.__jia) {
|
|
105
|
+
const orig = XING.burst;
|
|
106
|
+
XING.burst = (c, x, y, a, R, style, seed = 1) => {
|
|
107
|
+
const cols = XJ.BURST[style]; if (!cols) return orig(c, x, y, a, R, style, seed); if (a < 0 || a > 1) return;
|
|
108
|
+
const r = U.rng(seed); c.save(); c.lineCap = 'round';
|
|
109
|
+
for (let i = 0; i < 70; i++) { const ang = r() * TAU, sp = R * (0.55 + r() * 0.8), d = 150 + sp * U.ease.out(a), x1 = x + Math.cos(ang) * d, y1 = y + Math.sin(ang) * d * 0.8 + 120 * a * a; // 从人身外一圈迸开,不压在他身上(审片:彩条留在白T上)
|
|
110
|
+
c.strokeStyle = cols[i % cols.length]; c.globalAlpha = 1 - a; c.lineWidth = 7 + r() * 6; c.beginPath(); c.moveTo(x1, y1); c.lineTo(x1 + Math.cos(ang) * 16, y1 + Math.sin(ang) * 10); c.stroke(); }
|
|
111
|
+
c.restore(); };
|
|
112
|
+
XING.burst.__jia = true;
|
|
113
|
+
}
|
|
114
|
+
})();
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// 一组段共用的小工具(完整片子里 06_renaissance / 32_rembrandt / 07_ukiyoe / 28_monet / 29_seurat / 09_postimp 共用;本示范只用到 28_monet)。
|
|
2
|
+
// 不进 index.html:各段文件开头同步加载(if (!window.YI) …),哪一段先加载都行。
|
|
3
|
+
// YI.S(key) 帧库(确保 XING 已算好 imgs/hat)
|
|
4
|
+
// YI.actor(c, key, i, o, mat) 画第 i 帧,mat = { name, fn(img)->canvas(可带 pad), atop(g, o, box) }:
|
|
5
|
+
// fn 是逐帧缓存的材质(滤镜、点彩重画…);atop 每帧在精灵上 source-atop 叠(烛光、光斑)
|
|
6
|
+
// YI.toScreen(key, i, o, [px,py]) 帧内像素坐标 → 屏幕坐标(手的位置、道具对齐)
|
|
7
|
+
// YI.hand(key, i, o, tbl) tbl[i] = [px,py](帧内像素)→ 屏幕坐标
|
|
8
|
+
// YI.fly(c, from, to, q, draw, o) 纪念品飞上帽子:抛物线+放大+旋转(落帽那一下由 world.js 的 pop 接住)
|
|
9
|
+
(() => {
|
|
10
|
+
const W = 1920, H = 1080, TAU = Math.PI * 2;
|
|
11
|
+
const { clamp, lerp, ss } = U;
|
|
12
|
+
const P = window.PAINT, X = window.XING;
|
|
13
|
+
const Y = window.YI = {};
|
|
14
|
+
|
|
15
|
+
// 帧库在不在(读目录列表,不发 404 请求——render.py 把 404 记成页面报错)
|
|
16
|
+
let LIST = null;
|
|
17
|
+
Y.has = k => { if (LIST == null) { const x = new XMLHttpRequest(); x.open('GET', 'demos/long_scroll/frames/', false); x.send(); LIST = x.status === 200 ? x.responseText : ''; } return LIST.includes(`"${k}/"`); };
|
|
18
|
+
// 帽顶修正:举过头顶的手会骗过 XING 的「最上一行不透明像素」,用逐帧量出的白帽顶覆盖(帧内像素 [x,y])
|
|
19
|
+
Y.HATFIX = {};
|
|
20
|
+
Y.fixHat = (key, arr) => { Y.HATFIX[key] = arr; };
|
|
21
|
+
Y.S = key => { X.hatTop(key, 0, { x: 0, y: 0, h: 1 }); const S = X.SPR[key]; if (Y.HATFIX[key] && !S._yiFixed) { Y.HATFIX[key].forEach((p, i) => { if (p) S.hat[i] = p; }); S._yiFixed = true; } return S; };
|
|
22
|
+
Y.hatTop = (key, i, o) => { Y.S(key); return X.hatTop(key, i, o); };
|
|
23
|
+
const PROC = {};
|
|
24
|
+
Y.proc = (key, i, mat) => {
|
|
25
|
+
const k = key + '|' + mat.name + '|' + i; if (PROC[k]) return PROC[k];
|
|
26
|
+
const S = Y.S(key), im = S.imgs[i]; const out = mat.fn(im); if (out.pad == null) out.pad = 0; return PROC[k] = out;
|
|
27
|
+
};
|
|
28
|
+
Y.geom = (key, i, o) => { const S = Y.S(key), f = S.meta.frames[i], s = o.h / S.meta.ref_h * (o.scale || 1); return { S, f, s }; };
|
|
29
|
+
Y.toScreen = (key, i, o, p) => { const { f, s } = Y.geom(key, i, o), fx = o.flip ? -1 : 1; return [o.x + (p[0] - f.ax) * s * fx, o.y + (p[1] - f.ay) * s]; };
|
|
30
|
+
Y.hand = (key, i, o, tbl) => Y.toScreen(key, i, o, tbl[i] || tbl[0]);
|
|
31
|
+
|
|
32
|
+
Y.actor = (c, key, i, o, mat) => {
|
|
33
|
+
const { S, f, s } = Y.geom(key, i, o);
|
|
34
|
+
const im = mat && mat.fn ? Y.proc(key, i, mat) : S.imgs[i], pad = im.pad || 0, fx = o.flip ? -1 : 1;
|
|
35
|
+
const dw = im.width * s, dh = im.height * s, x0 = o.x - (f.ax + pad) * s * fx, y0 = o.y - (f.ay + pad) * s;
|
|
36
|
+
const left = o.flip ? x0 - dw : x0;
|
|
37
|
+
c.save(); if (o.alpha != null) c.globalAlpha *= o.alpha;
|
|
38
|
+
if (o.shadow) { c.fillStyle = o.shadow; c.beginPath(); c.ellipse(o.x, o.y + 2, o.h * 0.2, o.h * 0.035, 0, 0, TAU); c.fill(); }
|
|
39
|
+
if (mat && mat.atop) {
|
|
40
|
+
const Sc = P.scratch('yiActor'), g = Sc.getContext('2d'); g.setTransform(1, 0, 0, 1, 0, 0);
|
|
41
|
+
const bw = Math.ceil(dw) + 4, bh = Math.ceil(dh) + 4; g.clearRect(0, 0, bw + 4, bh + 4);
|
|
42
|
+
g.save(); g.translate(2, 2); if (o.flip) { g.translate(dw, 0); g.scale(-1, 1); } g.drawImage(im, 0, 0, dw, dh); g.restore();
|
|
43
|
+
g.save(); g.globalCompositeOperation = 'source-atop'; g.translate(2 - left, 2 - y0); mat.atop(g, o, { x: left, y: y0, w: dw, h: dh }); g.restore();
|
|
44
|
+
c.drawImage(Sc, 0, 0, bw, bh, left - 2, y0 - 2, bw, bh);
|
|
45
|
+
} else {
|
|
46
|
+
if (o.flip) { c.translate(left + dw, y0); c.scale(-1, 1); c.drawImage(im, 0, 0, dw, dh); }
|
|
47
|
+
else c.drawImage(im, left, y0, dw, dh);
|
|
48
|
+
}
|
|
49
|
+
c.restore();
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// 生图爱把白帽白T画成米黄浅灰:低饱和的亮色拉回白(帽子定规是纯白)。lo = 起拉的亮度
|
|
53
|
+
Y.whiten = (g, w, h, lo = 185, r = 251, gg = 249, b = 242, chroma = 52) => { const d = g.getImageData(0, 0, w, h), q = d.data;
|
|
54
|
+
for (let k = 0; k < q.length; k += 4) { const mx = Math.max(q[k], q[k + 1], q[k + 2]), mn = Math.min(q[k], q[k + 1], q[k + 2]); if (q[k + 3] > 0 && mx > lo && mx - mn < chroma) { const f = Math.min(1, (mx - lo) / (255 - lo) * 1.15); q[k] = lerp(q[k], r, f); q[k + 1] = lerp(q[k + 1], gg, f); q[k + 2] = lerp(q[k + 2], b, f); } }
|
|
55
|
+
g.putImageData(d, 0, 0); };
|
|
56
|
+
// 段两端补边:画风边界是波浪线(±30px),会伸出段的 [0,w] 之外——把世界画布最边上 2px 拉宽铺到外面 80px,免得露黑
|
|
57
|
+
Y.edge = (c, cv, camX, w) => { c.drawImage(cv, 0, 0, 2, cv.height, -camX - 80, 0, 82, cv.height); c.drawImage(cv, w - 2, 0, 2, cv.height, w - camX, 0, 80, cv.height); };
|
|
58
|
+
// 纪念品飞上帽子时的落点=当前那摞的顶(不是帽顶):在草稿画布上空画一遍 drawHatStack 拿顶端高度
|
|
59
|
+
Y.stackTop = (key, i, o, t) => { const [hx, hy] = Y.hatTop(key, i, o), g = P.scratch('yiStackProbe').getContext('2d'); g.setTransform(1, 0, 0, 1, 0, 0); g.globalAlpha = 0; const top = WORLD.drawHatStack(g, hx, hy + 4, o.h || 300, t); g.globalAlpha = 1; return [hx, hy + 4 + top]; };
|
|
60
|
+
// 帽子拉回纯白:直接改帧库里的原帧(只改上部帽子那几成高度的低饱和亮色),之后 XING 的材质都从白帽子算起
|
|
61
|
+
Y.whitenHat = (key, { frac = 0.42, lo = 150, chroma = 110 } = {}) => { const S = Y.S(key); if (S._yiWhite) return; S.imgs = S.imgs.map(im => { const cv = P.canvas(im.width, im.height), g = cv.getContext('2d', { willReadFrequently: true }); g.drawImage(im, 0, 0);
|
|
62
|
+
const hh = Math.round(im.height * frac); const d = g.getImageData(0, 0, im.width, hh), q = d.data;
|
|
63
|
+
for (let k = 0; k < q.length; k += 4) { const mx = Math.max(q[k], q[k + 1], q[k + 2]), mn = Math.min(q[k], q[k + 1], q[k + 2]); if (q[k + 3] > 0 && mx > lo && mx - mn < chroma && q[k + 2] >= q[k] - 20) { const f = Math.min(1, (mx - lo) / (255 - lo) * 1.3); q[k] = lerp(q[k], 252, f); q[k + 1] = lerp(q[k + 1], 251, f); q[k + 2] = lerp(q[k + 2], 248, f); } }
|
|
64
|
+
g.putImageData(d, 0, 0); return cv; }); S._yiWhite = true; };
|
|
65
|
+
// 纪念品飞上帽子:from/to 屏幕坐标;q 0..1;draw(c, x, y, scale, rot)
|
|
66
|
+
Y.fly = (c, from, to, q, draw, { arc = 160, grow = 0.8, spin = 0, s0 = 1, s1 = 1 } = {}) => {
|
|
67
|
+
const e = U.ease ? q : q; const x = lerp(from[0], to[0], e), y = lerp(from[1], to[1], e) - Math.sin(e * Math.PI) * arc;
|
|
68
|
+
const sc = lerp(s0, s1, e) * (1 + grow * Math.sin(e * Math.PI)); draw(c, x, y, sc, spin * e);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// 简单的抠精灵像素工具:返回精灵的 ImageData(缓存)
|
|
72
|
+
const DATA = {};
|
|
73
|
+
Y.data = (key, i) => { const k = key + i; if (DATA[k]) return DATA[k]; const im = Y.S(key).imgs[i], cv = P.canvas(im.width, im.height), g = cv.getContext('2d', { willReadFrequently: true }); g.drawImage(im, 0, 0); return DATA[k] = { w: im.width, h: im.height, d: g.getImageData(0, 0, im.width, im.height).data }; };
|
|
74
|
+
|
|
75
|
+
// 烛火(伦勃朗段纪念品与段内道具共用):x,y = 烛芯底;s 缩放;t 时间。只画火苗+内焰+芯
|
|
76
|
+
Y.flame = (c, x, y, s, t, seed = 0) => {
|
|
77
|
+
const fl = 0.5 * P.noise(t * 2.2 + seed, 3.1) + 0.25 * P.noise(t * 6.5 + seed, 7.7), lean = fl * 0.5, hgt = (30 + 8 * P.noise(t * 5 + seed, 1.3)) * s;
|
|
78
|
+
c.save(); c.translate(x, y);
|
|
79
|
+
const outer = new Path2D(); outer.moveTo(-7 * s, 0); outer.bezierCurveTo(-9 * s, -hgt * 0.45, lean * hgt * 0.6 - 2 * s, -hgt * 0.75, lean * hgt, -hgt); outer.bezierCurveTo(lean * hgt * 0.6 + 2 * s, -hgt * 0.75, 9 * s, -hgt * 0.45, 7 * s, 0); outer.closePath();
|
|
80
|
+
const g1 = c.createRadialGradient(0, -hgt * 0.3, 1, 0, -hgt * 0.3, hgt * 0.9); g1.addColorStop(0, 'rgba(255,250,215,1)'); g1.addColorStop(0.5, 'rgba(255,200,90,.95)'); g1.addColorStop(1, 'rgba(230,110,30,.7)');
|
|
81
|
+
c.fillStyle = g1; c.fill(outer);
|
|
82
|
+
c.fillStyle = 'rgba(255,255,240,.95)'; c.beginPath(); c.ellipse(lean * hgt * 0.25, -hgt * 0.3, 2.6 * s, hgt * 0.22, lean * 0.3, 0, TAU); c.fill();
|
|
83
|
+
c.fillStyle = 'rgba(90,110,200,.55)'; c.beginPath(); c.ellipse(0, -2 * s, 3.5 * s, 3 * s, 0, 0, TAU); c.fill();
|
|
84
|
+
c.strokeStyle = '#2a1a10'; c.lineWidth = 1.6 * s; c.beginPath(); c.moveTo(0, 2 * s); c.lineTo(lean * 3 * s, -5 * s); c.stroke();
|
|
85
|
+
c.restore();
|
|
86
|
+
};
|
|
87
|
+
// 烛光晕(lighter)
|
|
88
|
+
Y.glow = (c, x, y, r, a, col = '255,190,100') => { c.save(); c.globalCompositeOperation = 'lighter'; const g = c.createRadialGradient(x, y, 0, x, y, r); g.addColorStop(0, `rgba(${col},${a})`); g.addColorStop(0.4, `rgba(${col},${a * 0.4})`); g.addColorStop(1, `rgba(${col},0)`); c.fillStyle = g; c.fillRect(x - r, y - r, 2 * r, 2 * r); c.restore(); };
|
|
89
|
+
Y.flick = (t, seed = 0) => 0.5 + 0.5 * (0.6 * P.noise(t * 2.2 + seed, 5.5) + 0.4 * P.noise(t * 7 + seed, 9.1));
|
|
90
|
+
|
|
91
|
+
// ---------- 点彩(修拉):P.pointillism 的「世界坐标版」——网格锚在世界上(相机横移时点不游动),任意尺寸画布,可只点不透明处 ----------
|
|
92
|
+
// src 的 x=0 对应世界 x = gx0;alphaMin>0 时只在 src 不透明处下点(动态物件层);rect=[x0,y0,x1,y1] 限定范围(src 坐标)
|
|
93
|
+
const PICK = new Map();
|
|
94
|
+
Y.dots = (c, src, { pal, comp = {}, pitch = 10, rad = 4.5, posJitter = 2.4, colorJitter = 40, compRate = 0.08, flicker = 0.07, fps = 6, t = 0, shimmer, white = 12, dark = 0, bleed = 45, gx0 = 0, alphaMin = 0, rect, palKey = 'p' } = {}) => {
|
|
95
|
+
const PAL = pal.map(v => typeof v === 'string' ? P.hex(v) : v);
|
|
96
|
+
let cache = PICK.get(palKey); if (!cache) { cache = new Map(); PICK.set(palKey, cache); }
|
|
97
|
+
const pick = (r, g, b) => { const key = (r >> 3) << 10 | (g >> 3) << 5 | (b >> 3); let v = cache.get(key); if (v) return v;
|
|
98
|
+
let best = 1e18, bi = 0, bj = 0, bw = 1;
|
|
99
|
+
for (let i = 0; i < PAL.length; i++) for (let j = i + 1; j < PAL.length; j++) { const pi = PAL[i], pj = PAL[j], ex = pi[0] - pj[0], ey = pi[1] - pj[1], ez = pi[2] - pj[2];
|
|
100
|
+
const w = clamp(((r - pj[0]) * ex + (g - pj[1]) * ey + (b - pj[2]) * ez) / (ex * ex + ey * ey + ez * ez + 1e-6), 0.2, 0.8);
|
|
101
|
+
const dr = pj[0] + w * ex - r, dg = pj[1] + w * ey - g, db = pj[2] + w * ez - b, d = dr * dr * 0.9 + dg * dg * 1.2 + db * db * 0.8 + (ex * ex + ey * ey + ez * ez) * 0.004;
|
|
102
|
+
if (d < best) { best = d; bi = i; bj = j; bw = w; } }
|
|
103
|
+
v = [bi, bj, bw]; cache.set(key, v); return v; };
|
|
104
|
+
const sw = src.width, sh = src.height, [rx0, ry0, rx1, ry1] = rect || [0, 0, sw, sh];
|
|
105
|
+
const sd = src.getContext('2d', { willReadFrequently: true }).getImageData(0, 0, sw, sh).data;
|
|
106
|
+
const at = (x, y) => (clamp(y | 0, 0, sh - 1) * sw + clamp(x | 0, 0, sw - 1)) * 4;
|
|
107
|
+
const L = (x, y) => { const k = at(x, y); return sd[k] * 0.3 + sd[k + 1] * 0.59 + sd[k + 2] * 0.11; };
|
|
108
|
+
const paths = PAL.map(() => new Path2D()), st = Math.floor(t * fps), ROWH = pitch * 0.866, hj = posJitter / 2;
|
|
109
|
+
for (let j = Math.max(0, Math.floor(ry0 / ROWH) - 1), y = j * ROWH; y < ry1 + ROWH; j++, y += ROWH) {
|
|
110
|
+
const off = (j % 2) * pitch / 2, i0 = Math.floor((gx0 + rx0 - off) / pitch) - 1, i1 = Math.ceil((gx0 + rx1 - off) / pitch) + 1;
|
|
111
|
+
for (let i = i0; i <= i1; i++) {
|
|
112
|
+
const jx = (U.hash(i, j * 7 + 1) - 0.5) * 2 * hj, jy = (U.hash(i * 3, j + 5) - 0.5) * 2 * hj, px = i * pitch + off + jx - gx0, py = y + jy;
|
|
113
|
+
if (px < rx0 - pitch || px > rx1 + pitch) continue;
|
|
114
|
+
const k = at(px, py); if (alphaMin && sd[k + 3] < alphaMin) continue;
|
|
115
|
+
const dj = (U.hash(i * 7 + 3, j * 11) - 0.5) * colorJitter;
|
|
116
|
+
const [a, bb, w] = pick(clamp(sd[k] + dj, 0, 255), clamp(sd[k + 1] + dj * 0.8, 0, 255), clamp(sd[k + 2] - dj * 0.6, 0, 255));
|
|
117
|
+
const hseed = shimmer && shimmer(px, py) ? U.hash(i + st * 977, j) : U.hash(i, j);
|
|
118
|
+
let idx = hseed < w ? a : bb; const h2 = U.hash(i * 5 + 2, j * 3 + 9);
|
|
119
|
+
if (h2 < compRate && comp[idx] != null) idx = comp[idx];
|
|
120
|
+
else if (U.hash(i * 13 + st * 31, j * 17) < flicker) idx = (U.hash(i, j + st) < w) ? bb : a;
|
|
121
|
+
else if (!alphaMin) { const dl = L(px, py) - L(px + 14, py + 6); if (Math.abs(dl) > bleed && h2 < 0.45) idx = dl > 0 ? white : dark; }
|
|
122
|
+
paths[idx].moveTo(px + rad, py); paths[idx].arc(px, py, rad, 0, TAU);
|
|
123
|
+
} }
|
|
124
|
+
paths.forEach((p, k) => { c.fillStyle = P.rgb(PAL[k]); c.fill(p); });
|
|
125
|
+
};
|
|
126
|
+
// 精灵 → 点阵(散开/聚回用):按 pitch 采样不透明像素,返回 [{x,y,col,r1,r2,r3}](帧内像素坐标)
|
|
127
|
+
const DOTS = {};
|
|
128
|
+
Y.spriteDots = (key, i, pitch, procCanvas) => { const k = key + i + '|' + pitch; if (DOTS[k]) return DOTS[k];
|
|
129
|
+
const im = procCanvas || Y.S(key).imgs[i], cv = P.canvas(im.width, im.height), g = cv.getContext('2d', { willReadFrequently: true }); g.drawImage(im, 0, 0); const d = g.getImageData(0, 0, im.width, im.height).data, out = [];
|
|
130
|
+
for (let y = 0, j = 0; y < im.height; y += pitch * 0.866, j++) for (let x = (j % 2) * pitch / 2; x < im.width; x += pitch) { const q = ((y | 0) * im.width + (x | 0)) * 4; if (d[q + 3] < 140) continue;
|
|
131
|
+
out.push({ x, y, col: `rgb(${d[q]},${d[q + 1]},${d[q + 2]})`, r1: U.hash(x | 0, y | 0), r2: U.hash(y | 0, (x | 0) + 7), r3: U.hash((x | 0) + 3, (y | 0) * 3) }); }
|
|
132
|
+
return DOTS[k] = out; };
|
|
133
|
+
})();
|