@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,269 @@
|
|
|
1
|
+
// 1962 波普(利希滕斯坦漫画格+沃霍尔四宫格)——纯代码。
|
|
2
|
+
// 管线:①缓存底版(米白漫画格+45°本戴网点蓝墙+白踢脚线+黄地+红带+MEANWHILE 框)
|
|
3
|
+
// → ②会动的母题(沃霍尔四格猫按拍换配色、思考泡泡三点依次闪、WIGGLE 爆炸框抖、猫扭屁股)
|
|
4
|
+
// → ③角色:粗黑勾线平涂+皮肤红色本戴点(createPattern,零成本)+高光白条 + 斜线阴影
|
|
5
|
+
SCENES['13_pop'] = (() => {
|
|
6
|
+
const W = 1920, H = 1080, { clamp, lerp } = U, P = PAINT, TAU = Math.PI * 2;
|
|
7
|
+
const C = { ink: '#141414', paper: '#f3eedb', blue: '#2f5cc8', yellow: '#f7d52c', red: '#d8232a', redD: '#a8161c', white: '#fbf8ee',
|
|
8
|
+
skin: '#fae0cc', dotRed: '#e2483c', hair: '#f8d62c', hairD: '#d9a514', orange: '#f39a2c', orangeD: '#c4561a' };
|
|
9
|
+
const hash = (i, j = 0) => U.hash(i, j);
|
|
10
|
+
|
|
11
|
+
// 本戴点图案:方格 + 中心点 = 45° 网格。pitch 是方格边长
|
|
12
|
+
const dotPattern = (c, key, pitch, r, col, bg) => {
|
|
13
|
+
const tile = P.cached('pop_tile_' + key, pitch, pitch, (g) => { if (bg) { g.fillStyle = bg; g.fillRect(0, 0, pitch, pitch); } g.fillStyle = col;
|
|
14
|
+
[[0, 0], [pitch, 0], [0, pitch], [pitch, pitch], [pitch / 2, pitch / 2]].forEach(([x, y]) => { g.beginPath(); g.arc(x, y, r, 0, TAU); g.fill(); }); });
|
|
15
|
+
return c.createPattern(tile, 'repeat');
|
|
16
|
+
};
|
|
17
|
+
// 斜线阴影(漫画的投影)
|
|
18
|
+
const hatch = (c, cx, cy, rx, ry, gap = 10, w = 3.5) => { c.save(); c.beginPath(); c.ellipse(cx, cy, rx, ry, 0, 0, TAU); c.clip();
|
|
19
|
+
c.strokeStyle = C.ink; c.lineWidth = w; c.beginPath(); for (let x = cx - rx - ry * 2; x < cx + rx + ry; x += gap) { c.moveTo(x, cy + ry); c.lineTo(x + ry * 2.2, cy - ry); } c.stroke(); c.restore(); };
|
|
20
|
+
const ink = (c, p, fill, lw = 5) => { if (fill) { c.fillStyle = fill; c.fill(p); } c.strokeStyle = C.ink; c.lineWidth = lw; c.lineJoin = 'round'; c.lineCap = 'round'; c.stroke(p); };
|
|
21
|
+
const poly = (pts, closed = true) => { const p = new Path2D(); pts.forEach((q, i) => i ? p.lineTo(q[0], q[1]) : p.moveTo(q[0], q[1])); if (closed) p.closePath(); return p; };
|
|
22
|
+
|
|
23
|
+
// ---------- ① 底版 ----------
|
|
24
|
+
const bg = () => P.cached('pop_bg', W, H, (g) => {
|
|
25
|
+
g.fillStyle = C.paper; g.fillRect(0, 0, W, H);
|
|
26
|
+
// 蓝网点墙
|
|
27
|
+
g.fillStyle = dotPattern(g, 'wall', 24, 5.6, C.blue, C.white); g.fillRect(20, 16, W - 40, 738);
|
|
28
|
+
// 踢脚线、黄地、红带
|
|
29
|
+
g.fillStyle = C.white; g.fillRect(20, 754, W - 40, 38);
|
|
30
|
+
g.fillStyle = C.ink; g.fillRect(20, 750, W - 40, 6); g.fillRect(20, 790, W - 40, 6);
|
|
31
|
+
g.fillStyle = C.yellow; g.beginPath(); g.moveTo(20, 796); g.lineTo(W - 20, 796); g.lineTo(W - 20, 990); g.lineTo(20, 1003); g.closePath(); g.fill();
|
|
32
|
+
// 黄地上浅浅的网点(印刷感)
|
|
33
|
+
g.save(); g.clip(); g.globalAlpha = 0.18; g.fillStyle = dotPattern(g, 'floor', 14, 2.2, '#c99a10'); g.fillRect(20, 796, W - 40, 210); g.restore();
|
|
34
|
+
g.fillStyle = C.red; g.beginPath(); g.moveTo(20, 1003); g.lineTo(W - 20, 990); g.lineTo(W - 20, 1064); g.lineTo(20, 1064); g.closePath(); g.fill();
|
|
35
|
+
g.strokeStyle = C.ink; g.lineWidth = 6; g.beginPath(); g.moveTo(20, 1003); g.lineTo(W - 20, 990); g.stroke();
|
|
36
|
+
// 漫画格外框
|
|
37
|
+
g.strokeStyle = C.ink; g.lineWidth = 6; g.strokeRect(17, 14, W - 34, H - 28);
|
|
38
|
+
// MEANWHILE...
|
|
39
|
+
g.fillStyle = C.yellow; g.fillRect(32, 32, 272, 70); g.lineWidth = 5; g.strokeRect(32, 32, 272, 70);
|
|
40
|
+
g.font = '46px "Bangers-400"'; g.fillStyle = C.ink; g.textBaseline = 'middle'; g.letterSpacing = '1px'; g.fillText('MEANWHILE...', 50, 70); g.letterSpacing = '0px';
|
|
41
|
+
// 沃霍尔画框(白框)+窗台
|
|
42
|
+
g.fillStyle = C.white; g.fillRect(350, 110, 462, 472); g.lineWidth = 5; g.strokeRect(350, 110, 462, 472);
|
|
43
|
+
g.fillStyle = C.white; g.fillRect(334, 574, 496, 22); g.strokeRect(334, 574, 496, 22); g.fillStyle = C.blue; g.fillRect(338, 592, 488, 4);
|
|
44
|
+
// 椅子(蓝)
|
|
45
|
+
const blue = '#2448b0';
|
|
46
|
+
[[1452, 478, 24, 420], [1300, 760, 18, 140], [1460, 760, 18, 140]].forEach(([x, y, w, h]) => { g.fillStyle = blue; g.fillRect(x, y, w, h); g.lineWidth = 4; g.strokeRect(x, y, w, h); });
|
|
47
|
+
g.fillStyle = blue; g.fillRect(1282, 740, 210, 24); g.strokeRect(1282, 740, 210, 24);
|
|
48
|
+
[[1440, 560], [1440, 640]].forEach(([x, y]) => { g.fillRect(x, y, 30, 12); });
|
|
49
|
+
g.fillStyle = '#6f8ee8'; g.fillRect(1458, 486, 6, 400);
|
|
50
|
+
// 纸面颗粒
|
|
51
|
+
g.drawImage(P.grain('pop', 0.035, [60, 50, 30], 0.12), 0, 0);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// ---------- ② 沃霍尔四格猫 ----------
|
|
55
|
+
const SCHEMES = [
|
|
56
|
+
{ bg: '#9ccf3a', face: '#e0302a', low: '#9a6ad0', eye: '#3a7ad8', lip: '#f06a3a' },
|
|
57
|
+
{ bg: '#e0359a', face: '#f0902a', low: '#78d6e6', eye: '#2a9a5a', lip: '#e0302a' },
|
|
58
|
+
{ bg: '#2fc0b0', face: '#f27fb2', low: '#f8e040', eye: '#7a3ac8', lip: '#e0302a' },
|
|
59
|
+
{ bg: '#f08a2a', face: '#f8d830', low: '#f6a2c4', eye: '#2ab0d8', lip: '#e0302a' },
|
|
60
|
+
];
|
|
61
|
+
function warholCat(c, cx, cy, s) {
|
|
62
|
+
c.save(); c.translate(cx, cy);
|
|
63
|
+
const head = poly([[-70, -14], [-64, -78], [-30, -44], [30, -44], [64, -78], [70, -14], [62, 32], [30, 58], [-30, 58], [-62, 32]]);
|
|
64
|
+
const hd = RIG.smooth([[-70, -10], [-66, -76], [-28, -46], [28, -46], [66, -76], [70, -10], [60, 34], [0, 62], [-60, 34]], true, 0.35);
|
|
65
|
+
c.fillStyle = s.face; c.fill(hd);
|
|
66
|
+
c.save(); c.clip(hd); c.fillStyle = s.low; c.beginPath(); c.ellipse(4, 34, 58, 30, 0, 0, TAU); c.fill();
|
|
67
|
+
c.fillStyle = 'rgba(20,20,20,.88)'; c.beginPath(); c.moveTo(40, -40); c.quadraticCurveTo(76, 0, 52, 60); c.lineTo(90, 60); c.lineTo(90, -40); c.fill(); c.restore(); // 丝网错版的黑块
|
|
68
|
+
c.strokeStyle = C.ink; c.lineWidth = 5; c.lineJoin = 'round'; c.stroke(hd);
|
|
69
|
+
// 眼影+眼
|
|
70
|
+
[-26, 26].forEach(x => { c.fillStyle = s.eye; c.beginPath(); c.ellipse(x, -10, 19, 13, 0, Math.PI, TAU); c.fill();
|
|
71
|
+
c.fillStyle = '#fff'; c.beginPath(); c.ellipse(x, -4, 14, 9, 0, 0, TAU); c.fill(); c.lineWidth = 3; c.stroke();
|
|
72
|
+
c.fillStyle = C.ink; c.beginPath(); c.arc(x + 2, -4, 6, 0, TAU); c.fill(); c.lineWidth = 3.5; c.beginPath(); c.moveTo(x - 16, -10); c.lineTo(x - 22, -18); c.moveTo(x + 14, -12); c.lineTo(x + 20, -19); c.stroke(); });
|
|
73
|
+
// 嘴(口红)、鼻、胡须
|
|
74
|
+
c.fillStyle = s.lip; c.beginPath(); c.moveTo(-14, 30); c.quadraticCurveTo(0, 22, 14, 30); c.quadraticCurveTo(0, 44, -14, 30); c.fill(); c.lineWidth = 2.5; c.stroke();
|
|
75
|
+
c.fillStyle = C.ink; c.beginPath(); c.moveTo(-6, 14); c.lineTo(6, 14); c.lineTo(0, 21); c.fill();
|
|
76
|
+
c.lineWidth = 2; c.beginPath(); [[-1, 18], [1, 22], [-1, 26]].forEach(([d, y]) => { c.moveTo(-36, y); c.lineTo(-74, y - 4 + d * 2); c.moveTo(36, y); c.lineTo(74, y - 4 + d * 2); }); c.stroke();
|
|
77
|
+
// 耳内
|
|
78
|
+
c.fillStyle = C.ink; c.beginPath(); c.moveTo(-58, -64); c.lineTo(-56, -38); c.lineTo(-38, -44); c.fill();
|
|
79
|
+
c.restore();
|
|
80
|
+
}
|
|
81
|
+
function warhol(c, lt) {
|
|
82
|
+
const step = Math.floor(clamp(lt, 0, 9) * 11); // ~每 5–6 帧换一次配色
|
|
83
|
+
const cells = [[372, 132], [585, 132], [372, 350], [585, 350]];
|
|
84
|
+
cells.forEach(([x, y], i) => { const s = SCHEMES[(i + step) % 4];
|
|
85
|
+
c.fillStyle = s.bg; c.fillRect(x, y, 206, 212); warholCat(c, x + 103, y + 116, s);
|
|
86
|
+
c.strokeStyle = C.ink; c.lineWidth = 4; c.strokeRect(x, y, 206, 212); });
|
|
87
|
+
// 玻璃反光
|
|
88
|
+
c.strokeStyle = 'rgba(255,255,255,.85)'; c.lineWidth = 6; c.beginPath(); c.moveTo(712, 140); c.lineTo(660, 222); c.moveTo(736, 140); c.lineTo(684, 222); c.stroke();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---------- 思考泡泡(三点依次闪) ----------
|
|
92
|
+
const cloud = [[975, 220, 62], [1040, 180, 64], [1110, 170, 62], [1180, 200, 58], [1210, 250, 50], [1160, 292, 48], [1090, 300, 52], [1010, 292, 50], [960, 262, 46], [1080, 240, 90]];
|
|
93
|
+
function bubble(c, lt) {
|
|
94
|
+
c.lineWidth = 11; c.strokeStyle = C.ink; cloud.forEach(([x, y, r]) => { c.beginPath(); c.arc(x, y, r, 0, TAU); c.stroke(); });
|
|
95
|
+
c.fillStyle = C.white; cloud.forEach(([x, y, r]) => { c.beginPath(); c.arc(x, y, r, 0, TAU); c.fill(); });
|
|
96
|
+
[[1196, 334, 18], [1236, 338, 12]].forEach(([x, y, r]) => { c.beginPath(); c.arc(x, y, r, 0, TAU); c.fillStyle = C.white; c.fill(); c.lineWidth = 5; c.stroke(); });
|
|
97
|
+
const f = lt * 60;
|
|
98
|
+
[1025, 1082, 1139].forEach((x, k) => { const ph = ((f - 2 * k) % 12 + 12) % 12, on = ph < 4;
|
|
99
|
+
c.fillStyle = C.ink; c.beginPath(); c.arc(x, 242 - (on ? 6 : 0), on ? 20 : 12, 0, TAU); c.fill(); });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ---------- WIGGLE WIGGLE 爆炸框 ----------
|
|
103
|
+
const burst = (cx, cy, r0, r1, n, seed) => { const pts = []; for (let i = 0; i < n * 2; i++) { const a = i / (n * 2) * TAU + 0.1, r = i % 2 ? r0 * (0.92 + hash(i, seed) * 0.12) : r1 * (0.85 + hash(i, seed + 1) * 0.3); pts.push([cx + Math.cos(a) * r * 1.12, cy + Math.sin(a) * r * 0.8]); } return poly(pts); };
|
|
104
|
+
function wiggleBox(c, lt) {
|
|
105
|
+
const st = Math.floor(lt * 30), jx = (hash(st, 3) - 0.5) * 16, jy = (hash(st, 7) - 0.5) * 12, rot = (hash(st, 11) - 0.5) * 0.06;
|
|
106
|
+
const cx = 222, cy = 690;
|
|
107
|
+
// 放射动作线(黑三角)
|
|
108
|
+
c.fillStyle = C.ink;
|
|
109
|
+
[[-2.45, 1], [-2.0, 0.9], [-1.62, 1.1], [-1.25, 0.8], [2.2, 1], [1.85, 0.9], [1.5, 1.1], [1.15, 0.8], [2.7, 0.7], [-2.85, 0.8]].forEach(([a, k], i) => {
|
|
110
|
+
const pl = 0.85 + 0.25 * Math.sin(lt * 40 + i * 2), r0 = 230 * k, r1 = r0 + 90 * pl, w = 0.06;
|
|
111
|
+
c.beginPath(); c.moveTo(cx + Math.cos(a - w) * r0 * 1.12, cy + Math.sin(a - w) * r0 * 0.9); c.lineTo(cx + Math.cos(a) * r1 * 1.12, cy + Math.sin(a) * r1 * 0.9); c.lineTo(cx + Math.cos(a + w) * r0 * 1.12, cy + Math.sin(a + w) * r0 * 0.9); c.fill(); });
|
|
112
|
+
c.save(); c.translate(cx + jx, cy + jy); c.rotate(rot); c.translate(-cx, -cy);
|
|
113
|
+
const outer = burst(cx, cy, 128, 178, 14, 1); c.fillStyle = C.white; c.fill(outer); c.lineWidth = 6; c.strokeStyle = C.ink; c.stroke(outer);
|
|
114
|
+
const inner = burst(cx, cy, 104, 140, 12, 5); c.fillStyle = C.yellow; c.fill(inner);
|
|
115
|
+
c.font = '82px "Bangers-400"'; c.textAlign = 'center'; c.textBaseline = 'middle'; c.lineJoin = 'round';
|
|
116
|
+
c.save(); c.translate(cx, cy); c.rotate(-0.08); c.letterSpacing = '2px';
|
|
117
|
+
[['WIGGLE', -34], ['WIGGLE', 40]].forEach(([s, y], i) => { c.lineWidth = 12; c.strokeStyle = C.ink; c.strokeText(s, i * 14 - 4, y + 5); c.fillStyle = C.ink; c.fillText(s, i * 14 - 4 + 4, y + 9); c.fillStyle = C.red; c.fillText(s, i * 14 - 4, y + 5); });
|
|
118
|
+
c.restore(); c.restore();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ---------- 桌、杯 ----------
|
|
122
|
+
function table(c) {
|
|
123
|
+
hatch(c, 1010, 902, 150, 18);
|
|
124
|
+
c.fillStyle = C.ink; c.beginPath(); c.moveTo(985, 668); c.lineTo(1015, 668); c.lineTo(1024, 860); c.lineTo(976, 860); c.closePath(); c.fill();
|
|
125
|
+
c.beginPath(); c.moveTo(920, 900); c.quadraticCurveTo(930, 852, 1000, 848); c.quadraticCurveTo(1070, 852, 1080, 900); c.closePath(); c.fill();
|
|
126
|
+
c.strokeStyle = '#4a7ae8'; c.lineWidth = 5; c.lineCap = 'round'; c.beginPath(); c.moveTo(1030, 862); c.quadraticCurveTo(1062, 870, 1068, 892); c.moveTo(1010, 690); c.lineTo(1014, 830); c.stroke();
|
|
127
|
+
const top = poly([[822, 640], [1200, 640], [1196, 664], [826, 668]]); ink(c, top, C.red, 5);
|
|
128
|
+
c.strokeStyle = C.white; c.lineWidth = 5; c.beginPath(); c.moveTo(850, 650); c.lineTo(960, 650); c.moveTo(985, 650); c.lineTo(1010, 650); c.stroke();
|
|
129
|
+
}
|
|
130
|
+
function cup(c, G, t) {
|
|
131
|
+
const k = G.cup; c.save(); c.translate(k.x, k.y); c.rotate(k.tilt); const w = 50, h = 48;
|
|
132
|
+
const body = poly([[-w / 2, 0], [-w * 0.46, h], [w * 0.46, h], [w / 2, 0]]);
|
|
133
|
+
c.fillStyle = C.white; c.fill(body);
|
|
134
|
+
c.save(); c.clip(body); c.strokeStyle = '#2f5cc8'; c.lineWidth = 5; for (let x = 4; x < 50; x += 11) { c.beginPath(); c.moveTo(x, 0); c.lineTo(x - 18, h); c.stroke(); } c.restore();
|
|
135
|
+
c.strokeStyle = C.ink; c.lineWidth = 4; c.stroke(body); c.beginPath(); c.arc(w * 0.6, h * 0.45, 14, -1.3, 1.4); c.lineWidth = 6; c.stroke();
|
|
136
|
+
c.fillStyle = '#5a3016'; c.beginPath(); c.ellipse(0, 1, w / 2 - 2, 6, 0, 0, TAU); c.fill(); c.lineWidth = 3.5; c.stroke();
|
|
137
|
+
c.restore();
|
|
138
|
+
// 漫画热气:两条交叉的黑色波浪
|
|
139
|
+
c.save(); c.strokeStyle = C.ink; c.lineWidth = 4; c.lineCap = 'round';
|
|
140
|
+
[[-8, 0], [10, 2]].forEach(([dx, ph]) => { c.beginPath(); for (let i = 0; i <= 14; i++) { const q = i / 14, x = k.x + dx + Math.sin(q * 7 + ph + t * 14) * 9 * (dx < 0 ? 1 : -1), y = k.y - 14 - q * 62; i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); });
|
|
141
|
+
c.restore();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ---------- 少女(利希滕斯坦) ----------
|
|
145
|
+
function girl(c, G, lt, t, ch) {
|
|
146
|
+
const skinDots = dotPattern(c, 'skin', 13, 3.3, C.dotRed, C.skin);
|
|
147
|
+
const fillSkin = (p) => { c.fillStyle = skinDots; c.fill(p); c.strokeStyle = C.ink; c.lineWidth = 5; c.stroke(p); };
|
|
148
|
+
const a = G.A, h = a.headC, sh = [h[0] - 1318, h[1] - 360];
|
|
149
|
+
// 大波浪发团(背后)
|
|
150
|
+
const sway = Math.sin(t * 5) * 6;
|
|
151
|
+
// 外缘做成一串大波浪(利希滕斯坦的金发):沿后缘按弧长加 sin 鼓包
|
|
152
|
+
const spine = [[1262, 300], [1290, 262], [1346, 250], [1404, 276], [1440, 330], [1462, 396], [1492, 452], [1522, 510], [1540, 570], [1520, 624]];
|
|
153
|
+
const inner = [[1488, 640], [1452, 610], [1420, 628], [1392, 596], [1372, 548], [1356, 496], [1342, 452], [1330, 418], [1320, 392]];
|
|
154
|
+
const wav = spine.map(([x, y], i) => i < 4 ? [x, y] : [x + Math.sin(i * 2.1 + t * 4) * 14 + 10, y + Math.cos(i * 2.1) * 6]);
|
|
155
|
+
const hairPts = wav.concat(inner).map(([x, y]) => [x + sh[0] * (y < 430 ? 1 : 0.4) + (y > 430 ? sway * (y - 430) / 180 : 0), y + sh[1] * (y < 430 ? 1 : 0.4)]);
|
|
156
|
+
const hair = RIG.smooth(hairPts, true, 0.75);
|
|
157
|
+
// 大波浪:发团+沿后缘的一串卷团(先统一描粗黑边再统一填黄 = 并集轮廓)
|
|
158
|
+
const lobes = [[1440, 360, 44], [1478, 430, 46], [1500, 505, 48], [1520, 578, 44], [1478, 622, 38], [1430, 606, 34], [1400, 560, 30]]
|
|
159
|
+
.map(([x, y, r], i) => [x + sh[0] * (y < 430 ? 1 : 0.4) + sway * Math.max(0, y - 430) / 180 + Math.sin(t * 6 + i) * 3, y + sh[1] * (y < 430 ? 1 : 0.4), r]);
|
|
160
|
+
c.strokeStyle = C.ink; c.lineWidth = 10; c.stroke(hair); lobes.forEach(([x, y, r]) => { c.beginPath(); c.arc(x, y, r, 0, TAU); c.stroke(); });
|
|
161
|
+
c.fillStyle = C.hair; c.fill(hair); lobes.forEach(([x, y, r]) => { c.beginPath(); c.arc(x, y, r, 0, TAU); c.fill(); });
|
|
162
|
+
c.lineWidth = 4.5; c.lineCap = 'round'; lobes.forEach(([x, y, r], i) => { c.beginPath(); c.arc(x - 4, y + 2, r * 0.58, i % 2 ? -2.6 : -0.6, i % 2 ? 0.9 : 2.9); c.stroke(); });
|
|
163
|
+
c.strokeStyle = C.hairD; c.lineWidth = 7; lobes.forEach(([x, y, r]) => { c.beginPath(); c.arc(x, y, r - 6, 0.2, 1.2); c.stroke(); });
|
|
164
|
+
// 远侧手臂(桌上)
|
|
165
|
+
ink(c, G.farSleeve, C.red); fillSkin(G.farHand);
|
|
166
|
+
// 腿+红高跟
|
|
167
|
+
const knee = [1222, 760], ank = [1196, 878];
|
|
168
|
+
const leg = RIG.taper(knee, ank, 46, 30, 4); fillSkin(leg);
|
|
169
|
+
const leg2 = RIG.taper([1262, 772], [1236, 878], 44, 28, 4); fillSkin(leg2);
|
|
170
|
+
const shoe = (x, y) => { const p = poly([[x - 40, y + 2], [x - 30, y - 16], [x + 6, y - 14], [x + 16, y + 20], [x + 8, y + 22], [x - 2, y + 4], [x - 26, y + 12]]); ink(c, p, C.red, 4.5); c.strokeStyle = C.white; c.lineWidth = 3; c.beginPath(); c.moveTo(x - 26, y - 8); c.lineTo(x - 6, y - 8); c.stroke(); };
|
|
171
|
+
shoe(1236, 884); shoe(1196, 884);
|
|
172
|
+
// 裙(截短到膝)
|
|
173
|
+
c.save(); c.beginPath(); c.moveTo(0, 0); c.lineTo(W, 0); c.lineTo(W, 786); c.lineTo(1400, 786); c.quadraticCurveTo(1280, 806, 1150, 790); c.lineTo(0, 790); c.closePath(); c.clip();
|
|
174
|
+
ink(c, G.skirt, C.red, 5); c.restore();
|
|
175
|
+
c.strokeStyle = C.ink; c.lineWidth = 5; c.beginPath(); c.moveTo(1150, 790); c.quadraticCurveTo(1280, 806, 1400, 786); c.stroke();
|
|
176
|
+
ink(c, G.torso, C.red, 5);
|
|
177
|
+
// 高光白条
|
|
178
|
+
c.strokeStyle = C.white; c.lineWidth = 7; c.lineCap = 'round';
|
|
179
|
+
c.beginPath(); c.moveTo(a.chest[0] + 20, a.chest[1] - 10); c.quadraticCurveTo(a.chest[0] + 34, a.chest[1] + 40, a.chest[0] + 26, a.chest[1] + 80); c.moveTo(1200, 700); c.quadraticCurveTo(1250, 690, 1300, 700); c.stroke();
|
|
180
|
+
c.strokeStyle = C.redD; c.lineWidth = 4; c.beginPath(); c.moveTo(1380, 640); c.quadraticCurveTo(1400, 700, 1390, 760); c.stroke();
|
|
181
|
+
fillSkin(G.neck);
|
|
182
|
+
// 头
|
|
183
|
+
fillSkin(G.face);
|
|
184
|
+
c.fillStyle = C.skin; c.beginPath(); c.ellipse(G.ear[0], G.ear[1], 9, 13, 0.2, 0, TAU); c.fill(); c.lineWidth = 4; c.strokeStyle = C.ink; c.stroke();
|
|
185
|
+
// 刘海+头顶(大波浪)
|
|
186
|
+
const bangs = RIG.smooth([[1246, 330], [1250, 296], [1282, 268], [1336, 262], [1384, 284], [1404, 330], [1384, 312], [1350, 300], [1312, 302], [1280, 314], [1262, 340]].map(([x, y]) => [x + sh[0], y + sh[1]]), true, 0.5);
|
|
187
|
+
ink(c, bangs, C.hair, 5);
|
|
188
|
+
// 发丝黑线与白高光
|
|
189
|
+
c.strokeStyle = C.ink; c.lineWidth = 4;
|
|
190
|
+
[[1300, 272, 1418, 420, 0], [1350, 300, 1460, 520, 1], [1420, 360, 1520, 600, 2], [1380, 440, 1470, 630, 3], [1336, 420, 1410, 600, 4]].forEach(([x0, y0, x1, y1, k]) => {
|
|
191
|
+
c.beginPath(); for (let i = 0; i <= 24; i++) { const q = i / 24, x = lerp(x0, x1, q) + Math.sin(q * 9 + k) * 16 * q, y = lerp(y0, y1, q);
|
|
192
|
+
const X = x + sh[0] * 0.7 + (y > 430 ? sway * (y - 430) / 180 : 0), Y = y + sh[1] * 0.7; i ? c.lineTo(X, Y) : c.moveTo(X, Y); } c.stroke(); });
|
|
193
|
+
c.strokeStyle = C.white; c.lineWidth = 6; c.beginPath(); c.moveTo(1300 + sh[0], 280 + sh[1]); c.quadraticCurveTo(1340 + sh[0], 276 + sh[1], 1370 + sh[0], 292 + sh[1]); c.moveTo(1440 + sway * 0.3, 470); c.lineTo(1456 + sway * 0.4, 500); c.stroke();
|
|
194
|
+
// 五官:大眼、浓睫毛、红唇
|
|
195
|
+
const e = G.eye;
|
|
196
|
+
if (ch.blink) { c.strokeStyle = C.ink; c.lineWidth = 4; c.beginPath(); c.arc(e.x, e.y - 2, 9, 0.2, Math.PI - 0.2); c.stroke(); }
|
|
197
|
+
else { c.fillStyle = C.white; c.beginPath(); c.ellipse(e.x - 1, e.y, 12, 8.5, 0, 0, TAU); c.fill(); c.fillStyle = '#3a8ae0'; c.beginPath(); c.arc(e.x - 3, e.y + 1, 7, 0, TAU); c.fill(); c.fillStyle = C.ink; c.beginPath(); c.arc(e.x - 3, e.y + 1, 3.2, 0, TAU); c.fill(); c.fillStyle = '#fff'; c.beginPath(); c.arc(e.x - 6, e.y - 2, 2, 0, TAU); c.fill();
|
|
198
|
+
c.strokeStyle = C.ink; c.lineWidth = 4.5; c.stroke(G.lid); c.lineWidth = 3.5; G.lashes.forEach(l => { c.beginPath(); c.moveTo(...l[0]); c.lineTo(l[1][0] - 5, l[1][1] - 5); c.stroke(); }); c.beginPath(); c.moveTo(e.x + 10, e.y - 6); c.lineTo(e.x + 16, e.y - 12); c.stroke(); }
|
|
199
|
+
c.save(); c.translate(0, -5); c.strokeStyle = C.ink; c.lineWidth = 4; c.stroke(G.brow); c.restore();
|
|
200
|
+
c.fillStyle = C.red; c.beginPath(); c.moveTo(G.lips[0][0] - 3, G.lips[0][1] - 1); c.quadraticCurveTo(G.lips[1][0] + 4, G.lips[1][1] - 4, G.lips[1][0] + 3, G.lips[1][1] + 2); c.quadraticCurveTo(G.lips[2][0] + 3, G.lips[2][1] + 3, G.lips[2][0] - 3, G.lips[2][1]); c.closePath(); c.fill(); c.lineWidth = 2.5; c.stroke();
|
|
201
|
+
// 近侧手臂(无袖:皮肤点)+杯+手
|
|
202
|
+
fillSkin(G.upperArm); fillSkin(G.foreArm);
|
|
203
|
+
cup(c, G, t);
|
|
204
|
+
fillSkin(G.hand); c.fillStyle = C.skin; c.beginPath(); c.arc(G.thumb[0], G.thumb[1], 7, 0, TAU); c.fill(); c.lineWidth = 3.5; c.stroke();
|
|
205
|
+
c.fillStyle = C.red; c.beginPath(); c.arc(G.thumb[0] - 4, G.thumb[1] - 1, 3, 0, TAU); c.fill(); // 红指甲
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ---------- 猫:趴低、准备扑,屁股左右扭 ----------
|
|
209
|
+
function cat(c, K, lt, t, ch) {
|
|
210
|
+
const wig = Math.sin(lt * TAU * 6) * 16; // 6Hz 扭屁股
|
|
211
|
+
const bx = K.box[0][0] - 390, by = K.box[1][1] - 905; // 骨架原点偏移
|
|
212
|
+
const def = ([x, y]) => { const w = clamp((560 - x) / 140); return [x + bx + wig * w, y + by - Math.abs(wig) * 0.6 * w]; };
|
|
213
|
+
hatch(c, 580, 912, 230, 22);
|
|
214
|
+
// 尾巴
|
|
215
|
+
const tailBase = def([440, 870]), tip = [318 + bx + wig * 1.4 + Math.sin(ch.tail) * 20, 846 + by - Math.cos(ch.tail) * 18];
|
|
216
|
+
const tailPts = P.bez(tailBase, [400 + bx + wig, 900 + by], [350 + bx + wig * 1.2, 900 + by], tip, 16);
|
|
217
|
+
c.lineCap = 'round'; c.strokeStyle = C.ink; c.lineWidth = 34; c.beginPath(); tailPts.forEach((p, i) => i ? c.lineTo(p[0], p[1]) : c.moveTo(p[0], p[1])); c.stroke();
|
|
218
|
+
c.strokeStyle = C.orange; c.lineWidth = 24; c.stroke();
|
|
219
|
+
c.strokeStyle = C.orangeD; c.lineWidth = 6; [4, 8, 12].forEach(i => { const p = tailPts[i], q = tailPts[i + 1], a = Math.atan2(q[1] - p[1], q[0] - p[0]) + Math.PI / 2; c.beginPath(); c.moveTo(p[0] + Math.cos(a) * 11, p[1] + Math.sin(a) * 11); c.lineTo(p[0] - Math.cos(a) * 11, p[1] - Math.sin(a) * 11); c.stroke(); });
|
|
220
|
+
c.fillStyle = C.white; c.beginPath(); c.arc(tip[0], tip[1], 12, 0, TAU); c.fill(); c.lineWidth = 4; c.strokeStyle = C.ink; c.stroke();
|
|
221
|
+
// 身体(翘起的屁股在左)
|
|
222
|
+
const body = RIG.smooth([[452, 900], [420, 872], [418, 820], [440, 780], [482, 756], [540, 756], [600, 770], [652, 800], [690, 850], [700, 900]].map(def), true, 0.55);
|
|
223
|
+
ink(c, body, C.orange, 5);
|
|
224
|
+
c.save(); c.clip(body);
|
|
225
|
+
c.fillStyle = C.white; c.beginPath(); c.ellipse(610 + bx, 880 + by, 120, 46, 0, 0, TAU); c.fill();
|
|
226
|
+
c.strokeStyle = C.orangeD; c.lineWidth = 9; c.lineCap = 'round';
|
|
227
|
+
[[450, 790, 486, 830], [478, 772, 512, 816], [512, 764, 540, 806], [546, 766, 566, 806], [430, 830, 470, 858]].forEach(([x0, y0, x1, y1]) => { const p0 = def([x0, y0]), p1 = def([x1, y1]); c.beginPath(); c.moveTo(...p0); c.quadraticCurveTo(p0[0] + 18, (p0[1] + p1[1]) / 2, p1[0], p1[1]); c.stroke(); });
|
|
228
|
+
c.restore(); c.strokeStyle = C.ink; c.lineWidth = 5; c.stroke(body);
|
|
229
|
+
// 前爪(向前伸)
|
|
230
|
+
[[650, 886], [704, 888]].forEach(([x, y]) => { const p = new Path2D(); p.ellipse(x + bx, y + by, 30, 15, 0, 0, TAU); ink(c, p, C.white, 4.5); c.lineWidth = 3; c.beginPath(); c.moveTo(x + bx + 6, y + by - 8); c.lineTo(x + bx + 6, y + by + 6); c.moveTo(x + bx + 16, y + by - 7); c.lineTo(x + bx + 16, y + by + 5); c.stroke(); });
|
|
231
|
+
// 头(压低,盯着前方)
|
|
232
|
+
const hc = [K.headC[0] + 22, K.headC[1] + 104], r = 58;
|
|
233
|
+
const head = new Path2D(); head.ellipse(hc[0], hc[1], r * 1.08, r * 0.92, 0, 0, TAU);
|
|
234
|
+
const ears = poly([[hc[0] - 50, hc[1] - 30], [hc[0] - 46, hc[1] - 94], [hc[0] - 8, hc[1] - 52], [hc[0] + 14, hc[1] - 54], [hc[0] + 52, hc[1] - 96], [hc[0] + 56, hc[1] - 30]]);
|
|
235
|
+
ink(c, ears, C.orange, 5); ink(c, head, C.orange, 5);
|
|
236
|
+
c.fillStyle = '#f39ab8'; c.beginPath(); c.moveTo(hc[0] - 40, hc[1] - 50); c.lineTo(hc[0] - 40, hc[1] - 80); c.lineTo(hc[0] - 18, hc[1] - 56); c.fill(); c.beginPath(); c.moveTo(hc[0] + 44, hc[1] - 52); c.lineTo(hc[0] + 46, hc[1] - 82); c.lineTo(hc[0] + 22, hc[1] - 58); c.fill();
|
|
237
|
+
c.save(); c.clip(head); c.fillStyle = C.white; c.beginPath(); c.ellipse(hc[0] + 4, hc[1] + 36, 44, 34, 0, 0, TAU); c.fill();
|
|
238
|
+
c.strokeStyle = C.orangeD; c.lineWidth = 7; [[-14, -60, -10, -38], [4, -62, 4, -40], [22, -60, 18, -38]].forEach(([a0, b0, a1, b1]) => { c.beginPath(); c.moveTo(hc[0] + a0, hc[1] + b0); c.lineTo(hc[0] + a1, hc[1] + b1); c.stroke(); });
|
|
239
|
+
c.restore(); c.strokeStyle = C.ink; c.lineWidth = 5; c.stroke(head);
|
|
240
|
+
[[-22, -2], [24, -2]].forEach(([dx, dy]) => { const x = hc[0] + dx, y = hc[1] + dy;
|
|
241
|
+
if (ch.blink) { c.lineWidth = 4; c.beginPath(); c.arc(x, y, 13, 0.2, Math.PI - 0.2); c.stroke(); return; }
|
|
242
|
+
c.fillStyle = '#f8d020'; c.beginPath(); c.arc(x, y, 16, 0, TAU); c.fill(); c.lineWidth = 4; c.stroke();
|
|
243
|
+
c.fillStyle = C.ink; c.beginPath(); c.ellipse(x + 4, y + 1, 7, 12, 0, 0, TAU); c.fill(); c.fillStyle = '#fff'; c.beginPath(); c.arc(x + 7, y - 5, 4, 0, TAU); c.fill(); });
|
|
244
|
+
c.fillStyle = '#f07aa0'; c.beginPath(); c.moveTo(hc[0] - 7, hc[1] + 18); c.lineTo(hc[0] + 7, hc[1] + 18); c.lineTo(hc[0], hc[1] + 27); c.closePath(); c.fill(); c.lineWidth = 2.5; c.stroke();
|
|
245
|
+
c.lineWidth = 3.5; c.beginPath(); c.moveTo(hc[0] - 14, hc[1] + 32); c.quadraticCurveTo(hc[0] - 7, hc[1] + 40, hc[0], hc[1] + 30); c.quadraticCurveTo(hc[0] + 7, hc[1] + 40, hc[0] + 14, hc[1] + 32); c.stroke();
|
|
246
|
+
c.lineWidth = 2.5; c.beginPath(); [[-40, 22, -96, 12], [-40, 30, -98, 34], [40, 22, 98, 12], [40, 30, 100, 34]].forEach(([a0, b0, a1, b1]) => { c.moveTo(hc[0] + a0, hc[1] + b0); c.lineTo(hc[0] + a1, hc[1] + b1); }); c.stroke();
|
|
247
|
+
// 动作线:屁股旁的弧线跟着扭
|
|
248
|
+
c.strokeStyle = C.ink; c.lineWidth = 5; c.lineCap = 'round';
|
|
249
|
+
const side = wig > 0 ? 1 : -1;
|
|
250
|
+
[[0, 0], [1, 12], [2, 24]].forEach(([k, off]) => { c.beginPath(); c.arc(476 + bx + wig * 0.6, 760 + by - off, 46 + off, -Math.PI * 0.86, -Math.PI * 0.62); c.stroke(); });
|
|
251
|
+
[[0, 0], [1, 14]].forEach(([k, off]) => { c.beginPath(); c.arc(430 + bx + wig, 830 + by, 40 + off, Math.PI * (side > 0 ? 0.8 : 0.9), Math.PI * (side > 0 ? 1.05 : 1.15)); c.stroke(); });
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
draw(c, lt, t) {
|
|
256
|
+
const ch = P.choreo(lt, t);
|
|
257
|
+
c.drawImage(bg(), 0, 0);
|
|
258
|
+
warhol(c, lt);
|
|
259
|
+
bubble(c, lt);
|
|
260
|
+
table(c);
|
|
261
|
+
hatch(c, 1340, 905, 170, 18);
|
|
262
|
+
const G = RIG.girl({ cup: ch.cup, sip: ch.sip, hair: 'long', breathe: ch.breathe });
|
|
263
|
+
girl(c, G, lt, t, ch);
|
|
264
|
+
const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe });
|
|
265
|
+
cat(c, K, lt, t, ch);
|
|
266
|
+
wiggleBox(c, lt);
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
})();
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
// 1985 8-bit 像素游戏(超级马里奥式)——纯代码。
|
|
2
|
+
// 管线:整幅在 240×135 低分辨率上逐像素画(1 低像素 = 8 屏幕像素)→ 角色用 RIG 路径缩到低分辨率填色
|
|
3
|
+
// → 调色板量化 + 阈值去抗锯齿 + 每个部件单独描 1px 黑边(NES 精灵的分层描边)→ 最近邻放大 8 倍
|
|
4
|
+
// → HUD 用 PressStart2P 64px(字体 1 像素 = 8 屏幕像素,坐标对齐 8 的倍数)直接画在全分辨率上。
|
|
5
|
+
// 片中只停约 0.23s(可见约 11 帧),所以所有小循环都是 15–30fps 的「步进」动画,幅度按整像素给。
|
|
6
|
+
SCENES['14_8bit'] = (() => {
|
|
7
|
+
const W = 1920, H = 1080, S = 8, LW = 240, LH = 135, P = PAINT;
|
|
8
|
+
const C = {
|
|
9
|
+
wall: '#064556', plus: '#0b988a', black: '#000000', white: '#fcfcfc', gray: '#bcbcbc',
|
|
10
|
+
wood: '#a77f06', woodHi: '#eeba18', brick: '#d85214', mortar: '#fbe2a6', base: '#0b988a',
|
|
11
|
+
sky0: '#0856ef', sky1: '#47c4f8', sky2: '#aae9fb', sun: '#f1bc08', hill: '#62ee58', hill2: '#0dc405',
|
|
12
|
+
red: '#e82704', redDk: '#7c0b02', blue: '#0856ef', blueDk: '#0838a8', skin: '#f9e0ab', skinDk: '#e8a878',
|
|
13
|
+
hair: '#f8d038', hairDk: '#c89010', orange: '#f39d48', orangeDk: '#d85214', pink: '#f8a0b0', leaf: '#0dc405', leafHi: '#62ee58',
|
|
14
|
+
};
|
|
15
|
+
const PAL_CHAR = ['#000000', '#fcfcfc', '#bcbcbc', '#e82704', '#7c0b02', '#0856ef', '#0838a8', '#f9e0ab', '#e8a878', '#f8d038', '#c89010', '#f39d48', '#d85214', '#f8a0b0'].map(P.hex);
|
|
16
|
+
|
|
17
|
+
// ---------- 低分辨率画布工具 ----------
|
|
18
|
+
const lc = (k) => P.cached('8b_' + k, LW, LH, () => {});
|
|
19
|
+
const px = (g, x, y, w = 1, h = 1, col) => { g.fillStyle = col; g.fillRect(x, y, w, h); };
|
|
20
|
+
// 把一层(在低分辨率上用抗锯齿画的路径)量化到调色板、alpha 阈值化,再在外轮廓描 1px 黑边
|
|
21
|
+
function spriteize(g, outline = true) {
|
|
22
|
+
const im = g.getImageData(0, 0, LW, LH), d = im.data, N = LW * LH, op = new Uint8Array(N);
|
|
23
|
+
for (let i = 0; i < N; i++) {
|
|
24
|
+
const a = d[i * 4 + 3];
|
|
25
|
+
if (a < 110) { d[i * 4 + 3] = 0; continue; }
|
|
26
|
+
// 去预乘后找最近色
|
|
27
|
+
const r = d[i * 4], gg = d[i * 4 + 1], b = d[i * 4 + 2];
|
|
28
|
+
let best = 0, bd = 1e9; for (let j = 0; j < PAL_CHAR.length; j++) { const q = PAL_CHAR[j], dd = (r - q[0]) ** 2 + (gg - q[1]) ** 2 + (b - q[2]) ** 2; if (dd < bd) { bd = dd; best = j; } }
|
|
29
|
+
const q = PAL_CHAR[best]; d[i * 4] = q[0]; d[i * 4 + 1] = q[1]; d[i * 4 + 2] = q[2]; d[i * 4 + 3] = 255; op[i] = 1;
|
|
30
|
+
}
|
|
31
|
+
if (outline) for (let y = 0; y < LH; y++) for (let x = 0; x < LW; x++) {
|
|
32
|
+
const i = y * LW + x; if (op[i]) continue;
|
|
33
|
+
if ((x > 0 && op[i - 1]) || (x < LW - 1 && op[i + 1]) || (y > 0 && op[i - LW]) || (y < LH - 1 && op[i + LW])) { d[i * 4] = 0; d[i * 4 + 1] = 0; d[i * 4 + 2] = 0; d[i * 4 + 3] = 255; }
|
|
34
|
+
}
|
|
35
|
+
g.putImageData(im, 0, 0);
|
|
36
|
+
}
|
|
37
|
+
// 一个精灵层:在低分辨率画布上以 1/8 缩放画全分辨率坐标的路径,再 spriteize
|
|
38
|
+
function layer(key, fn, outline = true) {
|
|
39
|
+
const cv = lc('L_' + key), g = cv.getContext('2d');
|
|
40
|
+
g.setTransform(1, 0, 0, 1, 0, 0); g.clearRect(0, 0, LW, LH);
|
|
41
|
+
g.save(); g.scale(1 / S, 1 / S); fn(g); g.restore();
|
|
42
|
+
spriteize(g, outline); return cv;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ---------- 静态背景(低分辨率,逐像素) ----------
|
|
46
|
+
function background(g) {
|
|
47
|
+
px(g, 0, 0, LW, LH, C.wall);
|
|
48
|
+
// 十字点纹墙:6px 网格、隔行错 3px
|
|
49
|
+
for (let j = 0; j * 6 < 100; j++) for (let i = -1; i * 6 < LW + 6; i++) {
|
|
50
|
+
const x = i * 6 + (j % 2 ? 3 : 0) + 1, y = j * 6 + 2;
|
|
51
|
+
if (y > 97) continue;
|
|
52
|
+
px(g, x, y - 1, 1, 3, C.plus); px(g, x - 1, y, 3, 1, C.plus);
|
|
53
|
+
}
|
|
54
|
+
px(g, 0, 100, LW, 1, C.base);
|
|
55
|
+
// 木地板条
|
|
56
|
+
px(g, 0, 106, LW, 1, C.black); px(g, 0, 107, LW, 6, C.wood); px(g, 0, 107, LW, 1, C.woodHi);
|
|
57
|
+
for (let x = 7; x < LW; x += 24) px(g, x, 107, 1, 6, C.black);
|
|
58
|
+
// 砖块地面:行高 7(含缝),砖宽 16,隔行错 8
|
|
59
|
+
px(g, 0, 113, LW, LH - 113, C.black);
|
|
60
|
+
for (let r = 0; 113 + r * 7 < LH; r++) { const y = 114 + r * 7; for (let x = -16 + (r % 2 ? 8 : 0); x < LW; x += 16) {
|
|
61
|
+
px(g, x, y, 15, 6, C.brick); px(g, x, y, 15, 1, C.mortar); px(g, x, y, 1, 6, C.mortar);
|
|
62
|
+
} }
|
|
63
|
+
// 窗(45,15)-(100,73)
|
|
64
|
+
px(g, 44, 14, 57, 60, C.black); px(g, 45, 15, 55, 58, C.white); px(g, 98, 15, 2, 58, C.gray); px(g, 45, 71, 55, 2, C.gray);
|
|
65
|
+
const pane = (x0, y0, w, h) => {
|
|
66
|
+
for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) {
|
|
67
|
+
const q = (y0 + y - 17) / 52, X = x0 + x, Y = y0 + y;
|
|
68
|
+
// 天空:深蓝→浅蓝→淡蓝,交界用 2×2 棋盘抖动(NES 渐变)
|
|
69
|
+
let col = q < 0.36 ? C.sky0 : q < 0.62 ? C.sky1 : C.sky2;
|
|
70
|
+
if (Math.abs(q - 0.36) < 0.05 && (X + Y) % 2) col = C.sky1;
|
|
71
|
+
if (Math.abs(q - 0.62) < 0.05 && (X + Y) % 2) col = q < 0.62 ? C.sky2 : C.sky1;
|
|
72
|
+
px(g, X, Y, 1, 1, col);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
pane(47, 17, 24, 25); pane(75, 17, 23, 25); pane(47, 45, 24, 25); pane(75, 45, 23, 25);
|
|
76
|
+
px(g, 71, 17, 4, 53, C.white); px(g, 73, 17, 2, 53, C.gray); px(g, 47, 42, 51, 3, C.white); px(g, 47, 44, 51, 1, C.gray);
|
|
77
|
+
// 太阳(像素圆)
|
|
78
|
+
for (let y = -4; y <= 4; y++) for (let x = -4; x <= 4; x++) if (x * x + y * y <= 17) px(g, 58 + x, 28 + y, 1, 1, (x + y < -3) ? '#f8e858' : C.sun);
|
|
79
|
+
// 绿山(下两格)
|
|
80
|
+
const hill = (cx, cy, r, x0, x1, col) => { for (let x = x0; x < x1; x++) { const h = Math.sqrt(Math.max(0, r * r - (x - cx) ** 2)); if (h <= 0) continue; const top = Math.round(cy - h); px(g, x, top, 1, 70 - top, col); px(g, x, top, 1, 1, C.black); } };
|
|
81
|
+
hill(56, 70, 9, 47, 71, C.hill); hill(66, 72, 8, 47, 71, C.hill2);
|
|
82
|
+
hill(90, 76, 18, 75, 98, C.hill2);
|
|
83
|
+
for (const [x, y] of [[52, 64], [55, 62], [86, 63], [92, 66], [80, 68]]) px(g, x, y, 2, 1, C.hill);
|
|
84
|
+
// 砖块+问号块(右下格上方)
|
|
85
|
+
for (let k = 0; k < 2; k++) { const x = 81 + k * 4; px(g, x, 47, 4, 4, C.black); px(g, x, 47, 3, 3, C.brick); px(g, x, 47, 3, 1, C.mortar); }
|
|
86
|
+
px(g, 89, 47, 4, 4, C.black); px(g, 89, 47, 3, 3, C.sun); px(g, 90, 48, 1, 1, C.black);
|
|
87
|
+
px(g, 43, 73, 59, 3, C.black); px(g, 43, 73, 59, 2, C.gray); px(g, 43, 73, 59, 1, C.white);
|
|
88
|
+
// 鱼画框
|
|
89
|
+
px(g, 114, 29, 20, 15, C.black); px(g, 115, 30, 18, 13, C.woodHi); px(g, 117, 32, 14, 9, C.sky2);
|
|
90
|
+
px(g, 120, 35, 7, 3, C.brick); px(g, 119, 36, 1, 1, C.brick); px(g, 127, 34, 2, 5, C.brick); px(g, 121, 35, 1, 1, C.black);
|
|
91
|
+
px(g, 123, 27, 2, 2, C.gray);
|
|
92
|
+
// 椅子(梯背,人后面)
|
|
93
|
+
px(g, 178, 57, 3, 57, C.black); px(g, 179, 58, 2, 55, C.woodHi);
|
|
94
|
+
px(g, 185, 59, 3, 55, C.black); px(g, 186, 60, 2, 53, C.woodHi);
|
|
95
|
+
for (let k = 0; k < 4; k++) { px(g, 180, 63 + k * 7, 6, 2, C.black); px(g, 180, 63 + k * 7, 6, 1, C.wood); }
|
|
96
|
+
px(g, 160, 91, 28, 3, C.black); px(g, 160, 91, 28, 2, C.woodHi);
|
|
97
|
+
px(g, 163, 94, 2, 19, C.woodHi); px(g, 162, 94, 1, 19, C.black);
|
|
98
|
+
// 桌子 (102,80)-(150,113)
|
|
99
|
+
px(g, 101, 79, 50, 4, C.black); px(g, 102, 79, 48, 2, C.woodHi); px(g, 102, 81, 48, 1, C.wood);
|
|
100
|
+
px(g, 104, 83, 44, 3, C.wood); px(g, 104, 85, 44, 1, C.black);
|
|
101
|
+
for (const x of [106, 144]) { px(g, x - 1, 86, 4, 27, C.black); px(g, x, 86, 2, 27, C.woodHi); }
|
|
102
|
+
px(g, 107, 102, 38, 2, C.black); px(g, 107, 102, 38, 1, C.wood);
|
|
103
|
+
}
|
|
104
|
+
// 盆栽(会摆):叶子用整像素偏移 sway
|
|
105
|
+
function plant(g, sway) {
|
|
106
|
+
const pot = (x, y, w, h) => { px(g, x - 1, y - 1, w + 2, h + 2, C.black); px(g, x, y, w, h, C.brick); px(g, x + 1, y, 2, h, '#f39d48'); };
|
|
107
|
+
pot(208, 99, 20, 13); px(g, 206, 97, 24, 3, C.black); px(g, 207, 97, 22, 2, C.brick);
|
|
108
|
+
px(g, 217, 82, 2, 16, C.black); px(g, 218, 82, 1, 16, C.leaf);
|
|
109
|
+
const leaf = (cx, cy, w, h, dx) => { // 圆角像素叶:先画黑色外轮廓(缺角),再填绿、上沿一行高光、中间一条叶脉
|
|
110
|
+
const x0 = cx + dx - w / 2;
|
|
111
|
+
for (let j = -1; j <= h; j++) { const cut = (j === -1 || j === h) ? 1 : 0; px(g, x0 - 1 + cut, cy + j, w + 2 - cut * 2, 1, C.black); }
|
|
112
|
+
for (let j = 0; j < h; j++) { const cut = (j === 0 || j === h - 1) ? 1 : 0; px(g, x0 + cut, cy + j, w - cut * 2, 1, C.leaf); }
|
|
113
|
+
px(g, x0 + 1, cy, w - 3, 1, C.leafHi); px(g, x0 + 1, cy + (h >> 1), w - 2, 1, '#0a8a04');
|
|
114
|
+
};
|
|
115
|
+
leaf(218, 70, 6, 6, sway); leaf(213, 76, 6, 4, sway); leaf(224, 76, 6, 4, sway);
|
|
116
|
+
leaf(209, 84, 8, 4, Math.round(sway * 0.6)); leaf(227, 84, 8, 4, Math.round(sway * 0.6)); leaf(212, 91, 7, 3, 0); leaf(225, 91, 7, 3, 0);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ---------- HUD(全分辨率,PressStart2P 64px,对齐 8px 网格) ----------
|
|
120
|
+
const BM = { // 位图:. 透明 o 橘 w 白 k 黑 r 红 p 粉
|
|
121
|
+
cat: ['o.....o.', 'oo...oo.', 'ooooooo.', 'okoooko.', 'ooopooo.', 'wwwwwww.', '.wwwww..'],
|
|
122
|
+
heart: ['.rr.rr.', 'rrrrrrr', 'rrrrrrr', '.rrrrr.', '..rrr..', '...r...'],
|
|
123
|
+
heartE: ['.ww.ww.', 'w..w..w', 'w.....w', '.w...w.', '..w.w..', '...w...'],
|
|
124
|
+
apple: ['...w....', '..w.....', '.wwwwww.', 'wwwwwwww', 'wwwwwwkw', 'wwwwwwww', '.wwwwww.', '..w..w..'],
|
|
125
|
+
};
|
|
126
|
+
const COL = { o: C.orange, w: C.white, k: C.black, r: C.red, p: C.pink, R: C.white };
|
|
127
|
+
const bitmap = (c, rows, x, y, map = COL, shadow = true) => {
|
|
128
|
+
if (shadow) rows.forEach((row, j) => [...row].forEach((ch, i) => { if (ch !== '.') { c.fillStyle = '#0b1d24'; c.fillRect(x + i * S + S, y + j * S + S, S, S); } }));
|
|
129
|
+
rows.forEach((row, j) => [...row].forEach((ch, i) => { if (ch !== '.') { c.fillStyle = map[ch]; c.fillRect(x + i * S, y + j * S, S, S); } }));
|
|
130
|
+
};
|
|
131
|
+
const text = (c, s, x, y) => { c.font = '48px "PressStart2P-400"'; c.textBaseline = 'top'; c.fillStyle = '#0b1d24'; c.fillText(s, x + S, y + S); c.fillStyle = C.white; c.fillText(s, x, y); };
|
|
132
|
+
function hud(c, lt) {
|
|
133
|
+
bitmap(c, BM.cat, 64, 40);
|
|
134
|
+
text(c, 'x9', 144, 48);
|
|
135
|
+
// 心:第 630 帧(本段 lt≈0.19)闪一下——两颗满心变白、空心变红,2 帧一换
|
|
136
|
+
const fl = lt > 0.16 && lt < 0.23 && Math.floor(lt * 30) % 2 === 0;
|
|
137
|
+
const red = fl ? { ...COL, r: C.white } : COL, emp = fl ? { ...COL, w: C.red } : COL;
|
|
138
|
+
bitmap(c, BM.heart, 312, 48, red); bitmap(c, BM.heart, 384, 48, red); bitmap(c, fl ? BM.heart.map(r => r) : BM.heartE, 456, 48, fl ? { ...COL, r: C.red } : emp);
|
|
139
|
+
text(c, 'WORLD 1-1', 624, 48);
|
|
140
|
+
bitmap(c, BM.apple, 1168, 40);
|
|
141
|
+
text(c, 'x03', 1248, 48);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ---------- 角色 ----------
|
|
145
|
+
const legs = (g, G) => { // 牛仔裤腿+白球鞋(替换 RIG 的裙子)
|
|
146
|
+
const hip = G.hipSeat, knee = [1222, 724], ank = [1214, 872];
|
|
147
|
+
g.fillStyle = C.blueDk; g.fill(RIG.taper([hip[0] + 10, hip[1] - 6], [knee[0] + 26, knee[1] - 8], 84, 70)); g.fill(RIG.taper([knee[0] + 26, knee[1] - 8], [ank[0] + 30, ank[1]], 64, 50));
|
|
148
|
+
g.fillStyle = C.blue; g.fill(RIG.taper(hip, knee, 92, 76)); g.fill(RIG.taper(knee, ank, 70, 56));
|
|
149
|
+
g.fillStyle = C.white; g.fill(RIG.smooth([[1150, 868], [1238, 862], [1250, 900], [1124, 904], [1118, 888]]));
|
|
150
|
+
g.fillStyle = C.red; g.fillRect(1176, 880, 30, 8);
|
|
151
|
+
};
|
|
152
|
+
function girlLayers(ch, lt) {
|
|
153
|
+
const G = RIG.girl({ cup: ch.cup, sip: ch.sip, hair: 'long', breathe: ch.breathe });
|
|
154
|
+
// 每个部件单独成层、单独描黑边:NES 精灵靠这些「内轮廓」分出头发/脸/手臂
|
|
155
|
+
const back = layer('gBack', g => { g.fillStyle = C.red; g.fill(G.farSleeve); g.fillStyle = C.skin; g.fill(G.farHand); });
|
|
156
|
+
const body = layer('gBody', g => {
|
|
157
|
+
legs(g, G);
|
|
158
|
+
g.fillStyle = C.red; g.fill(G.torso);
|
|
159
|
+
g.fillStyle = C.redDk; g.fillRect(G.A.waist[0] - 40, G.A.waist[1] - 18, 120, 16); // 毛衣下摆罗纹
|
|
160
|
+
g.fillStyle = C.red; g.fillRect(G.A.neck[0] - 28, G.A.neck[1] - 10, 56, 22); // 高领
|
|
161
|
+
});
|
|
162
|
+
const hair = layer('gHair', g => { g.fillStyle = C.hair; g.fill(G.hairBack); g.fill(G.hairDown); g.fillStyle = C.hairDk; g.fillRect(G.A.nape[0] + 10, G.A.nape[1] + 20, 14, 120); });
|
|
163
|
+
const face = layer('gFace', g => { g.fillStyle = C.skin; g.fill(G.face); g.beginPath(); g.ellipse(G.ear[0], G.ear[1], 9, 13, 0.2, 0, 7); g.fill(); });
|
|
164
|
+
const bangs = layer('gBangs', g => { g.fillStyle = C.hair; g.fill(G.bangs); });
|
|
165
|
+
const arm = layer('gArm', g => {
|
|
166
|
+
g.fillStyle = C.red; g.fill(G.upperArm); g.fill(G.foreArm); g.fillStyle = C.redDk; g.fill(G.cuff);
|
|
167
|
+
g.fillStyle = C.skin; g.fill(G.hand);
|
|
168
|
+
});
|
|
169
|
+
return { G, back, body, hair, face, bangs, arm };
|
|
170
|
+
}
|
|
171
|
+
// 白底红条马克杯:直接在低分辨率上逐像素画(6×5 + 把手),杯子跟着手走
|
|
172
|
+
function cupPx(g, G) {
|
|
173
|
+
const x = Math.round(G.cup.x / S) - 3, y = Math.round(G.cup.y / S);
|
|
174
|
+
px(g, x - 1, y - 1, 8, 7, C.black); px(g, x + 6, y, 3, 4, C.black); px(g, x + 6, y + 1, 2, 2, C.white); px(g, x + 6, y + 1, 1, 2, C.black);
|
|
175
|
+
px(g, x, y, 6, 5, C.white); px(g, x, y, 6, 1, C.gray); px(g, x, y + 2, 6, 1, C.red); px(g, x, y + 4, 6, 1, C.red); px(g, x + 5, y + 1, 1, 4, C.gray);
|
|
176
|
+
px(g, Math.round(G.thumb[0] / S), Math.round(G.thumb[1] / S), 2, 2, C.skin);
|
|
177
|
+
return [x + 3, y - 1];
|
|
178
|
+
}
|
|
179
|
+
function catLayers(ch, lt) {
|
|
180
|
+
// 尾巴:15fps 步进,4 个姿态循环(像素动画的「两三张图交替」)
|
|
181
|
+
const step = Math.floor(lt * 15) % 4, tail = [-0.55, 0, 0.55, 0][step];
|
|
182
|
+
const K = RIG.cat({ tail, blink: ch.blink, breathe: 0 });
|
|
183
|
+
const body = layer('catB', g => {
|
|
184
|
+
g.lineCap = 'round'; g.strokeStyle = C.orange; g.lineWidth = K.tailW; g.stroke(K.tail);
|
|
185
|
+
const tip = K.tailPts[K.tailPts.length - 1]; g.fillStyle = C.white; g.beginPath(); g.arc(tip[0], tip[1], 14, 0, 7); g.fill();
|
|
186
|
+
g.fillStyle = C.orange; g.fill(K.body);
|
|
187
|
+
g.fillStyle = C.white; g.fill(K.white); K.legs.forEach(l => g.fill(l));
|
|
188
|
+
g.strokeStyle = C.orangeDk; g.lineWidth = 9; K.stripes.slice(3).forEach(s => { g.beginPath(); g.moveTo(...s[0]); g.lineTo(...s[1]); g.stroke(); });
|
|
189
|
+
});
|
|
190
|
+
const head = layer('catH', g => {
|
|
191
|
+
g.fillStyle = C.orange; g.fill(K.head);
|
|
192
|
+
g.save(); g.clip(K.head); g.fillStyle = C.white; g.beginPath(); g.ellipse(K.nose[0] - 14, K.nose[1] + 20, 34, 22, 0, 0, 7); g.fill(); g.restore();
|
|
193
|
+
g.strokeStyle = C.orangeDk; g.lineWidth = 9; K.stripes.slice(0, 3).forEach(s => { g.beginPath(); g.moveTo(...s[0]); g.lineTo(...s[1]); g.stroke(); });
|
|
194
|
+
g.fillStyle = C.pink; K.earInner.forEach(e => { g.beginPath(); e.forEach((p, i) => i ? g.lineTo(...p) : g.moveTo(...p)); g.fill(); });
|
|
195
|
+
});
|
|
196
|
+
return { K, body, head };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
draw(c, lt, t) {
|
|
201
|
+
const ch = P.choreo(lt, t);
|
|
202
|
+
const L = lc('frame'), g = L.getContext('2d');
|
|
203
|
+
g.setTransform(1, 0, 0, 1, 0, 0);
|
|
204
|
+
g.drawImage(P.cached('8b_bg', LW, LH, background), 0, 0);
|
|
205
|
+
// 窗里的云:2 帧挪 1px 往左;问号块 lt≈0.08 起顶出一枚金币(上去再落回)
|
|
206
|
+
const cx = 92 - Math.floor(lt * 30);
|
|
207
|
+
for (const [x, y, w] of [[cx - 34, 35, 8], [cx, 22, 9]]) { if (x < 47 || x + w > 98 || (x + w > 70 && x < 75)) continue; px(g, x + 1, y - 1, w - 2, 1, C.white); px(g, x, y, w, 2, C.white); px(g, x, y + 2, w, 1, C.gray); }
|
|
208
|
+
const cq = (lt - 0.06) / 0.16;
|
|
209
|
+
if (cq > 0 && cq < 1) { const y = Math.round(46 - Math.sin(cq * Math.PI) * 9); px(g, 90, y - 3, 2, 3, C.sun); px(g, 90, y - 3, 1, 3, '#f8e858'); }
|
|
210
|
+
// 盆栽在 630 帧(lt≈0.19)抖一下
|
|
211
|
+
const sw = lt > 0.15 && lt < 0.24 ? [0, 1, 0, -1][Math.floor(lt * 30) % 4] : 0;
|
|
212
|
+
plant(g, sw);
|
|
213
|
+
// 角色
|
|
214
|
+
const CL = catLayers(ch, lt), K = CL.K;
|
|
215
|
+
g.drawImage(CL.body, 0, 0); g.drawImage(CL.head, 0, 0);
|
|
216
|
+
// 猫脸:像素眼(2×2 黑+1 白高光)、粉鼻、白胡须
|
|
217
|
+
K.eyes.forEach(e => { const x = Math.round(e.x / S), y = Math.round(e.y / S); if (ch.blink) px(g, x - 1, y, 2, 1, C.black); else { px(g, x - 1, y - 1, 2, 2, C.black); px(g, x - 1, y - 1, 1, 1, C.white); } });
|
|
218
|
+
px(g, Math.round(K.nose[0] / S), Math.round(K.nose[1] / S), 1, 1, C.pink);
|
|
219
|
+
const hx = Math.round((K.headC[0] + 70) / S), hy = Math.round(K.headC[1] / S) + 2;
|
|
220
|
+
px(g, hx, hy, 3, 1, C.white); px(g, hx, hy + 2, 3, 1, C.white); px(g, hx - 17, hy + 1, 2, 1, C.white); px(g, hx - 17, hy + 3, 2, 1, C.white);
|
|
221
|
+
const GL = girlLayers(ch, lt), G = GL.G;
|
|
222
|
+
g.drawImage(GL.back, 0, 0); g.drawImage(GL.body, 0, 0); g.drawImage(GL.hair, 0, 0); g.drawImage(GL.face, 0, 0); g.drawImage(GL.bangs, 0, 0);
|
|
223
|
+
// 少女五官(逐像素):1×2 黑眼+眉、腮红、红唇
|
|
224
|
+
const ex = Math.round(G.eye.x / S), ey = Math.round(G.eye.y / S);
|
|
225
|
+
if (ch.blink) px(g, ex - 1, ey, 2, 1, C.black); else { px(g, ex, ey - 1, 1, 2, C.black); px(g, ex - 1, ey - 3, 2, 1, C.hairDk); }
|
|
226
|
+
px(g, Math.round(G.cheek[0] / S) - 1, Math.round(G.cheek[1] / S), 2, 1, C.pink);
|
|
227
|
+
px(g, Math.round(G.lips[1][0] / S), Math.round(G.lips[1][1] / S), 1, 1, C.red);
|
|
228
|
+
g.drawImage(GL.arm, 0, 0);
|
|
229
|
+
const [sx, sy0] = cupPx(g, G);
|
|
230
|
+
// 像素热气:三列 1×2 的点,30fps 每帧上移 1px,循环 6px
|
|
231
|
+
const ph = Math.floor(lt * 30), sy = sy0 - 1;
|
|
232
|
+
for (let k = 0; k < 3; k++) for (let j = 0; j < 2; j++) { const y = sy - ((ph + k * 2 + j * 3) % 6) - j * 3; px(g, sx - 2 + k * 2 + ((ph + k) % 2), y, 1, 2, k === 1 ? C.white : C.gray); }
|
|
233
|
+
// 放大 8 倍(最近邻)
|
|
234
|
+
c.imageSmoothingEnabled = false; c.drawImage(L, 0, 0, LW, LH, 0, 0, W, H); c.imageSmoothingEnabled = true;
|
|
235
|
+
hud(c, lt);
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
})();
|