@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,47 @@
|
|
|
1
|
+
// K3 阶段二「对话」(第 8–11 拍,4.0–6.0s;推页 5.8s 起、6.0s 到位)
|
|
2
|
+
// 第 8 拍主词砸在画右+巨型「2」→ 8.5 拍你的气泡弹出 → 第 9 拍 AI 气泡长出、代码一行行流进来 → 9.5 拍辅句 → 10.5 拍「一整段」放大+荧光笔。
|
|
3
|
+
// 版式和 K2 镜像(主词左↔右),同一套拍位换位置,观众不会觉得重复。
|
|
4
|
+
// 返修:辅句原在第 10 拍、10 个字只停 0.55s,左端离 AI 面板只剩 14px;现在提前、压到 7 个字、右对齐留 250px 以上。
|
|
5
|
+
(() => {
|
|
6
|
+
const K = KT, C = K.C, b = K.b;
|
|
7
|
+
const COLS = ['#C792EA', '#82AAFF', '#C3E88D', '#F78C6C', '#89DDFF', '#FFCB6B'];
|
|
8
|
+
SCENES['k3_chat'] = {
|
|
9
|
+
draw(c, lt, t) {
|
|
10
|
+
c.fillStyle = C.navy; c.fillRect(0, 0, 1920, 1080);
|
|
11
|
+
K.dots(c, t, C.white, 0.07);
|
|
12
|
+
K.cam(c, t, c => {
|
|
13
|
+
K.bigNum(c, t, '2', b(8), 1880, 1150, 'rgba(255,255,255,0.07)');
|
|
14
|
+
TY.rise(c, '阶段 02', 1790, 250, MO.at(t, b(8), 0.3), { size: K.SZ.label, fam: 'PuHui-Bold', color: C.yellow, track: 6, align: 'right' });
|
|
15
|
+
TY.underline(c, 1620, 272, 170, MO.at(t, b(8.25), 0.3), C.yellow, 6);
|
|
16
|
+
const hw = TY.width(c, '对话', K.SZ.hero);
|
|
17
|
+
K.slam(c, '对话', 1790 - hw, 600, t, b(8), { color: C.yellow });
|
|
18
|
+
const s1 = '一句话换', key = '一整段', w1 = TY.width(c, s1, K.SZ.sub, 'PuHui-Heavy'), wk = TY.width(c, key, K.SZ.sub, 'PuHui-Heavy');
|
|
19
|
+
const ky = 775, x1 = 1790 - w1 - wk * 1.35 - 12, kx = x1 + w1 + 12;
|
|
20
|
+
TY.rise(c, s1, x1, ky, MO.at(t, b(9.5), 0.35), { size: K.SZ.sub, fam: 'PuHui-Heavy', color: C.white });
|
|
21
|
+
const ksc = t >= b(10.5) ? 1 + 0.35 * MO.springHz(t - b(10.5), 2.5, 9) : 1;
|
|
22
|
+
K.keyMarker(c, kx, ky, wk, K.SZ.sub, t, b(10.5), ksc, C.yellow);
|
|
23
|
+
K.key(c, key, kx, ky, t, b(9.75), b(10.5), { size: K.SZ.sub, fam: 'PuHui-Heavy', color: t >= b(10.5) ? C.navy : C.white });
|
|
24
|
+
// 你的气泡(右对齐到 x=820),8.5 拍弹出
|
|
25
|
+
const p1 = MO.at(t, b(8.5), 0.3);
|
|
26
|
+
if (p1 > 0) { const s = MO.backOut(p1, 2.6); c.save(); c.translate(820, 250); c.scale(s, s);
|
|
27
|
+
const w = TY.width(c, '帮我做个登录页', 48, 'PuHui-Bold') + 70;
|
|
28
|
+
c.fillStyle = C.blue; c.beginPath(); c.roundRect(-w, -50, w, 96, 30); c.fill();
|
|
29
|
+
c.beginPath(); c.moveTo(-26, 40); c.lineTo(10, 58); c.lineTo(-6, 26); c.fill();
|
|
30
|
+
TY.text(c, '帮我做个登录页', -w / 2, 16, { size: 48, fam: 'PuHui-Bold', color: C.white }); c.restore(); }
|
|
31
|
+
// AI 气泡:第 9 拍长出(高度 expoOut 0.35s),之后代码每 0.045s 流入一行
|
|
32
|
+
const p2 = MO.at(t, b(9), 0.35);
|
|
33
|
+
if (p2 > 0) {
|
|
34
|
+
const e = MO.expoOut(p2), x = 130, y = 340, w = 690, h = 620 * e;
|
|
35
|
+
c.fillStyle = C.white; c.beginPath(); c.roundRect(x, y, w, Math.max(40, h), 30); c.fill();
|
|
36
|
+
c.save(); c.beginPath(); c.roundRect(x, y, w, Math.max(40, h), 30); c.clip();
|
|
37
|
+
const n = Math.floor(Math.max(0, t - b(9) - 0.1) / 0.045), r = U.rng(5);
|
|
38
|
+
for (let i = 0; i < 22; i++) { const toks = 1 + (r() * 3 | 0), ind = (r() * 3 | 0) * 28; let xx = x + 40 + ind;
|
|
39
|
+
const yy = y + 40 + i * 26, vis = i < n;
|
|
40
|
+
for (let k = 0; k < toks; k++) { const ww = 40 + r() * 140; if (vis) { c.fillStyle = COLS[(r() * 6) | 0]; c.beginPath(); c.roundRect(xx, yy, ww, 14, 7); c.fill(); } else r(); xx += ww + 12; } }
|
|
41
|
+
c.restore();
|
|
42
|
+
c.fillStyle = C.white; c.beginPath(); c.moveTo(x + 30, y + 4); c.lineTo(x - 10, y - 26); c.lineTo(x + 70, y + 4); c.fill();
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// K4 阶段三「代理」(第 12 拍起,6.0–8.5s)
|
|
2
|
+
// 第 12 拍主词砸进+巨型「3」→ 12.5 拍「Agent」+任务卡升起 → 第 13 拍辅句「你只说」、13.25 拍「要什么」→
|
|
3
|
+
// 13.5 / 14 / 14.5 拍三条任务自己打勾 → 第 15 拍「要什么」放大+下划线 → 第 16 拍收尾重拍:镜头拉远 6%、整屏一冲,留 0.5s 静住。
|
|
4
|
+
// 返修:第一版高潮最弱——收尾无一击、打完勾全变灰、底部 28% 空着。现在内容整体下移 70px、勾完不变灰、加第 16 拍收尾。
|
|
5
|
+
(() => {
|
|
6
|
+
const K = KT, C = K.C, b = K.b;
|
|
7
|
+
const { clamp } = U;
|
|
8
|
+
const TASKS = [['写代码', 13.5], ['跑测试', 14], ['上线', 14.5]];
|
|
9
|
+
SCENES['k4_agent'] = {
|
|
10
|
+
draw(c, lt, t) {
|
|
11
|
+
c.fillStyle = C.paper; c.fillRect(0, 0, 1920, 1080);
|
|
12
|
+
K.dots(c, t, C.ink, 0.06);
|
|
13
|
+
const pull = MO.expoOut(MO.at(t, b(16), 0.45)), hit = t >= b(16) ? 0.035 * Math.exp(-(t - b(16)) * 9) : 0;
|
|
14
|
+
K.cam(c, t, c => {
|
|
15
|
+
K.bigNum(c, t, '3', b(12), 1880, 1150, 'rgba(17,17,17,0.05)');
|
|
16
|
+
TY.rise(c, '阶段 03', 140, 300, MO.at(t, b(12), 0.3), { size: K.SZ.label, fam: 'PuHui-Bold', color: C.coral, track: 6 });
|
|
17
|
+
TY.underline(c, 140, 322, 170, MO.at(t, b(12.25), 0.3), C.coral, 6);
|
|
18
|
+
K.slam(c, '代理', 130, 650, t, b(12), { color: C.ink });
|
|
19
|
+
const hw = TY.width(c, '代理', K.SZ.hero);
|
|
20
|
+
TY.rise(c, 'Agent', 150 + hw, 650, MO.at(t, b(12.5), 0.35), { size: 96, fam: 'PuHui-Black', color: C.coral });
|
|
21
|
+
const s1 = '你只说', key = '要什么', w1 = TY.width(c, s1, K.SZ.sub, 'PuHui-Heavy'), wk = TY.width(c, key, K.SZ.sub, 'PuHui-Heavy');
|
|
22
|
+
const ky = 825, kx = 152 + w1;
|
|
23
|
+
TY.rise(c, s1, 140, ky, MO.at(t, b(13), 0.35), { size: K.SZ.sub, fam: 'PuHui-Heavy', color: C.ink });
|
|
24
|
+
const ksc = K.key(c, key, kx, ky, t, b(13.25), b(15), { size: K.SZ.sub, fam: 'PuHui-Heavy', color: C.coral });
|
|
25
|
+
TY.underline(c, kx, ky + 30 * ksc, wk * ksc, MO.at(t, b(15), 0.3), C.coral, 12);
|
|
26
|
+
// 任务卡:12.5 拍从下方升起
|
|
27
|
+
const pc = MO.expoOut(MO.at(t, b(12.5), 0.4)); if (pc <= 0) return;
|
|
28
|
+
c.save(); c.translate(1220, 360 + 260 * (1 - pc)); c.globalAlpha = clamp(pc * 2);
|
|
29
|
+
c.fillStyle = 'rgba(0,0,0,0.08)'; c.beginPath(); c.roundRect(12, 16, 600, 470, 28); c.fill();
|
|
30
|
+
c.fillStyle = C.white; c.beginPath(); c.roundRect(0, 0, 600, 470, 28); c.fill();
|
|
31
|
+
TY.text(c, 'TODO', 50, 80, { size: 40, fam: 'PuHui-Black', color: '#B5AFA4', align: 'left', track: 4 });
|
|
32
|
+
TASKS.forEach(([s, beat], i) => {
|
|
33
|
+
const y = 170 + i * 115, ck = MO.at(t, b(beat), 0.22), done = t >= b(beat);
|
|
34
|
+
// 打勾那一拍框先放大 1.25 再回,勾按弧长画出
|
|
35
|
+
const bs = done ? 1 + 0.25 * Math.exp(-(t - b(beat)) * 10) * Math.sin(Math.min(Math.PI, (t - b(beat)) * 20)) : 1;
|
|
36
|
+
c.save(); c.translate(80, y); c.scale(bs, bs);
|
|
37
|
+
c.fillStyle = done ? C.green : C.paper; c.strokeStyle = done ? C.green : '#CFC7BA'; c.lineWidth = 6;
|
|
38
|
+
c.beginPath(); c.roundRect(-30, -30, 60, 60, 14); c.fill(); c.stroke();
|
|
39
|
+
if (ck > 0) { c.strokeStyle = C.white; c.lineWidth = 10; c.lineCap = 'round'; c.lineJoin = 'round';
|
|
40
|
+
const L1 = Math.hypot(11, 12), L2 = Math.hypot(22, 26), d = MO.expoOut(ck) * (L1 + L2);
|
|
41
|
+
c.beginPath(); c.moveTo(-15, 0); if (d <= L1) c.lineTo(-15 + 11 * d / L1, 12 * d / L1); else { c.lineTo(-4, 12); const q = (d - L1) / L2; c.lineTo(-4 + 22 * q, 12 - 26 * q); } c.stroke(); }
|
|
42
|
+
c.restore();
|
|
43
|
+
TY.text(c, s, 140, y + 24, { size: 66, fam: 'PuHui-Heavy', color: done ? C.green : C.ink, align: 'left' });
|
|
44
|
+
});
|
|
45
|
+
c.restore();
|
|
46
|
+
}, { zoom: (1 - 0.06 * pull) * (1 + hit) });
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
})();
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// y5 本片共用:节拍网格、配色、字号阶梯、拍点微冲、进度轨(全片唯一连续的叙事锚)、背景点阵。
|
|
2
|
+
// 一切「落点」都写成拍号:b(4) = 第 4 拍 = 2.0s。120 BPM:一拍 0.5s,八分 0.25s。
|
|
3
|
+
// 用到的库:TY(slam 砸入、rise 遮罩升起、keyword 关键词强调、marker 荧光笔、underline)、CAM(with、drift)、MO(springHz、拍号)。
|
|
4
|
+
(() => {
|
|
5
|
+
const { clamp, lerp } = U;
|
|
6
|
+
window.BPM = 120;
|
|
7
|
+
const K = window.KT = {};
|
|
8
|
+
K.b = n => MO.beat(n, 120); // 拍 → 秒
|
|
9
|
+
const C0 = K.C = { ink: '#111111', paper: '#F4EFE6', yellow: '#FFD23F', coral: '#FF5A36', navy: '#14213D', blue: '#3A86FF', green: '#2BB673', white: '#FFFFFF', code: '#1B1D29' };
|
|
10
|
+
// 字号阶梯:主词 : 辅句 : 标签 ≈ 3.4 : 1 : 0.4(主词必须一眼读完,辅句给耳朵对位)
|
|
11
|
+
K.SZ = { hero: 340, sub: 96, label: 40 };
|
|
12
|
+
// 拍点微冲:每个四分拍让画面放大 1.2%,0.12s 衰减(e^-25t)——画面跟着鼓「呼吸」
|
|
13
|
+
K.bump = t => { const ph = t % 0.5; return 1 + 0.012 * Math.exp(-ph * 25); };
|
|
14
|
+
// 背景点阵:斜向缓慢平移 36px/s(持续运动,低对比 8%,不和字抢)
|
|
15
|
+
K.dots = (c, t, col, alpha = 0.1) => {
|
|
16
|
+
const g = 64, ox = (t * 36) % g, oy = (t * 18) % g;
|
|
17
|
+
c.save(); c.globalAlpha = alpha; c.fillStyle = col;
|
|
18
|
+
for (let y = -g; y < 1080 + g; y += g) for (let x = -g; x < 1920 + g; x += g) { c.beginPath(); c.arc(x + ox, y + oy, 4, 0, Math.PI * 2); c.fill(); }
|
|
19
|
+
c.restore();
|
|
20
|
+
};
|
|
21
|
+
// 进度轨:右上三格,画在固定层(GLOBAL_OVERLAY),不跟页面一起被推走(返修:第一版画在各段里,推页时跟着跳)。
|
|
22
|
+
// 颜色在转场「揭开」那一帧换(2.0 / 4.0 / 6.0s),亮格同一帧点亮。
|
|
23
|
+
K.STAGES = [2.0, 4.0, 6.0];
|
|
24
|
+
K.trackCol = t => t < 4.0 ? [C0.white, 'rgba(255,255,255,0.35)'] : t < 6.0 ? [C0.yellow, 'rgba(255,255,255,0.25)'] : [C0.coral, 'rgba(17,17,17,0.18)'];
|
|
25
|
+
window.GLOBAL_OVERLAY = (c, t) => {
|
|
26
|
+
if (t < 2.0) return;
|
|
27
|
+
const [col, dim] = K.trackCol(t), a = MO.expoOut(clamp((t - 2.0) / 0.3));
|
|
28
|
+
c.save(); c.globalAlpha = a;
|
|
29
|
+
for (let i = 0; i < 3; i++) {
|
|
30
|
+
const x = 1520 + i * 120, y = 92, on = t >= K.STAGES[i], fill = on ? MO.expoOut(clamp((t - K.STAGES[i]) / 0.3)) : 0;
|
|
31
|
+
c.fillStyle = dim; c.fillRect(x, y, 100, 10); c.fillStyle = col; c.fillRect(x, y, 100 * fill, 10);
|
|
32
|
+
TY.text(c, '0' + (i + 1), x, y - 18, { size: 30, fam: 'PuHui-Bold', color: on ? col : dim, align: 'left', track: 2 });
|
|
33
|
+
}
|
|
34
|
+
c.restore();
|
|
35
|
+
};
|
|
36
|
+
// 背景巨型阶段数字(900px、低对比、斜向慢漂):数字是章节的主角之一,但不和主词抢(返修:审片说 01/02/03 太小,退成了装饰)
|
|
37
|
+
K.bigNum = (c, t, s, t0, x, y, col) => {
|
|
38
|
+
const lt = t - t0; if (lt < 0) return;
|
|
39
|
+
const k = MO.springHz(lt, 2.0, 8), sc = lerp(1.35, 1, k);
|
|
40
|
+
TY.text(c, s, x + lt * 14, y - lt * 10, { size: 900, fam: 'PuHui-Black', align: 'right', color: col, scale: sc, alpha: clamp(lt / 0.08), ox: x - 250, oy: y - 320 });
|
|
41
|
+
};
|
|
42
|
+
// 关键词:先从槽里升起,到「强调拍」再放大 1.35 倍并钉住,荧光笔跟着放大(TY.keyword / TY.keywordMarker)
|
|
43
|
+
K.key = TY.keyword;
|
|
44
|
+
K.keyMarker = TY.keywordMarker;
|
|
45
|
+
// 主词「砸」进来:从 1.7 倍缩到 1(弹簧 2.2Hz/10,过冲 ≈8%),前 0.06s 透明→不透明(TY.slam)
|
|
46
|
+
K.slam = (c, s, x, y, t, t0, o = {}) => TY.slam(c, s, x, y, t - t0, { size: K.SZ.hero, ...o });
|
|
47
|
+
// 镜头:微漂+极慢旋转(±0.5°)+拍点微冲
|
|
48
|
+
K.cam = (c, t, fn, extra = {}) => {
|
|
49
|
+
const [dx, dy] = CAM.drift(t, 5, 2);
|
|
50
|
+
CAM.with(c, { x: 960 + dx + (extra.x || 0), y: 540 + dy + (extra.y || 0), z: K.bump(t) * (extra.zoom || 1), r: 0.009 * Math.sin(t * 0.8) + (extra.rot || 0) }, fn);
|
|
51
|
+
};
|
|
52
|
+
})();
|
package/engine/engine.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
// 引擎:时间 t(秒)→ 一帧画面。确定性:同一个 t 永远画出同一帧(随机数全部用种子)。
|
|
2
|
+
// 时间轴由节拍网格驱动:128 BPM,每段占若干八分音符(见 eras.js 的 eighths)。
|
|
3
|
+
(() => {
|
|
4
|
+
// 画布尺寸:?w=1080&h=1920(render.py --width/--height 传)> eras.js 里的 window.FILM_SIZE = [w, h] > 1920×1080。
|
|
5
|
+
// 一次性 U.setStage:转场、相机和各库都跟着换;竖屏整片的场景代码要按 window.STAGE(或 CAM/UI 等库)排版,35 种艺术风格场景只按横屏画过。
|
|
6
|
+
const qs = new URLSearchParams(location.search), qw = +qs.get('w'), qh = +qs.get('h');
|
|
7
|
+
const [W, H] = qw > 0 && qh > 0 ? [qw, qh] : Array.isArray(window.FILM_SIZE) ? window.FILM_SIZE.map(Number) : [1920, 1080];
|
|
8
|
+
window.U.setStage(W, H);
|
|
9
|
+
const cv = document.getElementById('c');
|
|
10
|
+
cv.width = W; cv.height = H; window.__size = [W, H];
|
|
11
|
+
const ctx = cv.getContext('2d');
|
|
12
|
+
window.__canvas = cv;
|
|
13
|
+
|
|
14
|
+
const EIGHTH = 60 / (window.BPM || 128) / 2; // 0.234375s @128;片子可设 window.BPM
|
|
15
|
+
// 段长:eighths(八分音符数,卡节拍)或 dur(秒,按口播切的解说片)。写了 dur 就以 dur 为准。
|
|
16
|
+
let acc = 0;
|
|
17
|
+
for (const e of ERAS) { e.t0 = acc; acc += e.dur != null ? e.dur : e.eighths * EIGHTH; e.t1 = acc; }
|
|
18
|
+
// 片长 = 最后一段的 t1;window.FILM_DURATION 优先。render.py / 预览都读 window.__total。
|
|
19
|
+
window.__total = window.FILM_DURATION || ERAS[ERAS.length - 1].t1;
|
|
20
|
+
ERAS[ERAS.length - 1].t1 = 1e9;
|
|
21
|
+
|
|
22
|
+
// 离屏缓冲:A=上一段,B=当前段
|
|
23
|
+
const mk = () => { const c = document.createElement('canvas'); c.width = W; c.height = H; return c; };
|
|
24
|
+
const bufA = mk(), bufB = mk(), bufT = mk();
|
|
25
|
+
window.__mk = mk;
|
|
26
|
+
|
|
27
|
+
// ---------- 资源 ----------
|
|
28
|
+
const IMG = {};
|
|
29
|
+
function load(src) {
|
|
30
|
+
return new Promise((res, rej) => { const im = new Image(); im.onload = () => res(im); im.onerror = () => rej(src); im.src = src; });
|
|
31
|
+
}
|
|
32
|
+
async function boot() {
|
|
33
|
+
if (window.__bootErrors && __bootErrors.length) { window.__bootFailed = __bootErrors.join('\n'); console.error('BOOT FAILED\n' + window.__bootFailed); return; }
|
|
34
|
+
const list = new Set();
|
|
35
|
+
for (const e of ERAS) (e.assets || []).concat(e.plate ? [e.plate] : []).forEach(s => list.add(s));
|
|
36
|
+
(window.EXTRA_ASSETS || []).forEach(s => list.add(s));
|
|
37
|
+
try { await Promise.all([...list].map(async s => { IMG[s] = await load(s); })); }
|
|
38
|
+
catch (src) { window.__bootFailed = `图片加载失败:${src}(段的 assets / plate / window.EXTRA_ASSETS 里点名的文件不存在或不是图片)`; console.error('BOOT FAILED\n' + window.__bootFailed); return; }
|
|
39
|
+
for (const f of (window.FONT_FACES || [])) { const ff = new FontFace(f.family, `url(${f.url})`, f.desc || {}); await ff.load(); document.fonts.add(ff); }
|
|
40
|
+
await document.fonts.ready;
|
|
41
|
+
await U.loadCmaps(); // 字形检查用(U.assertGlyphs)
|
|
42
|
+
for (const e of ERAS) if (e.init) e.init(IMG);
|
|
43
|
+
checkCounterGlyphs();
|
|
44
|
+
await window.prepare(0); // 第 0 帧的视频帧先加载(缺帧在这里就报错)
|
|
45
|
+
window.__ready = true;
|
|
46
|
+
renderFrame(0);
|
|
47
|
+
}
|
|
48
|
+
window.IMG = IMG;
|
|
49
|
+
|
|
50
|
+
const U = window.U;
|
|
51
|
+
|
|
52
|
+
// ---------- 角标(右上角):可插拔 ----------
|
|
53
|
+
// 每段 era.counter 三种写法:
|
|
54
|
+
// 1) 默认:年份计数器 U.yearCounter——数值只由全局时间决定(U.counterAt),跨段连续滚动;label 写风格名。
|
|
55
|
+
// 2) counter.draw(c, lt, t)(别名 counter.custom(c, lt)):自绘。年代不单调、要印章/榜题/漫画旁白框时用。
|
|
56
|
+
// 3) counter.mode = 'tag':U.styleTag 画「年份+风格名」两行静态牌(年代乱序时不滚动)。
|
|
57
|
+
// 转场期间:默认(window.COUNTER_IN_TRANSITION = 'cut')新旧两层都不画角标,转场画完后在最上层按 p<0.5 硬切旧/新角标——
|
|
58
|
+
// 各段角标版式不同时,局部揭示的转场(点灯、扫光、水波)会让两套字叠成一团(迁移测试 C、D 的独立审片都抓到)。
|
|
59
|
+
// 'layers':角标留在各自层里随转场一起变(原片复刻:年份牌位置固定、数字连续滚动,混在一起看不出来)。单个转场可用 transition.counter 覆盖。
|
|
60
|
+
function drawCounter(c, e, t) {
|
|
61
|
+
const ct = e.counter; if (!ct) return;
|
|
62
|
+
const lt = t - e.t0;
|
|
63
|
+
if (ct.draw) ct.draw(c, lt, t);
|
|
64
|
+
else if (ct.custom) ct.custom(c, lt);
|
|
65
|
+
else if (ct.mode === 'tag') U.styleTag(c, ct, lt);
|
|
66
|
+
else { const cv = U.counterAt(t); U.yearCounter(c, ct, cv.v, cv.rolling, lt); }
|
|
67
|
+
}
|
|
68
|
+
// 启动时检查角标用到的字形(缺字静默回退系统字体是迁移测试 B、D 都踩过的坑)。自绘角标的字请在 scene.init 里自己 U.assertGlyphs。
|
|
69
|
+
function checkCounterGlyphs() {
|
|
70
|
+
for (const e of ERAS) { const ct = e.counter; if (!ct || ct.draw || ct.custom) continue;
|
|
71
|
+
if (ct.mode === 'tag') { if (ct.name) U.assertGlyphs(ct.nameFont || ct.font, ct.name, e.id + ' 角标'); if (ct.sub) U.assertGlyphs(ct.subFont || ct.nameFont || ct.font, ct.sub, e.id + ' 角标'); continue; }
|
|
72
|
+
U.assertGlyphs(ct.font, '0123456789' + (Math.abs(ct.year) >= 10000 ? ',' : ''), e.id + ' 年份');
|
|
73
|
+
if (ct.label) U.assertGlyphs(ct.label.font, ct.label.text, e.id + ' 角标');
|
|
74
|
+
if (ct.suffix) U.assertGlyphs(ct.suffix.font || ct.font, 'BCAD', e.id + ' 角标后缀');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// 「年份+风格名」静态牌:{ year, name, sub?, x, y, font, size, color, nameFont, nameColor, subFont, subColor, box(c,lt), shadow, align }
|
|
78
|
+
U.styleTag = function (c, ct, lt) {
|
|
79
|
+
if (ct.box) ct.box(c, lt);
|
|
80
|
+
c.save(); c.textAlign = ct.align || 'right'; c.textBaseline = 'alphabetic';
|
|
81
|
+
if (ct.shadow) { c.shadowColor = ct.shadow.color; c.shadowBlur = ct.shadow.blur || 0; c.shadowOffsetX = ct.shadow.x || 0; c.shadowOffsetY = ct.shadow.y || 0; }
|
|
82
|
+
const x = ct.x ?? 1866, y = ct.y ?? 128;
|
|
83
|
+
if (ct.year != null) { c.font = `${ct.size || 96}px "${U.fontFamily(ct.font)}"`; c.fillStyle = ct.color || '#fff'; c.fillText(ct.yearText || (ct.year < 0 ? U.fmtYear(ct.year) + ' BC' : String(ct.year)), x, y); }
|
|
84
|
+
if (ct.name) { c.font = ct.nameFont || `34px "${U.fontFamily(ct.font)}"`; c.fillStyle = ct.nameColor || ct.color || '#fff'; c.fillText(ct.name, x, ct.ny ?? y + 52); }
|
|
85
|
+
if (ct.sub) { c.font = ct.subFont || ct.nameFont; c.fillStyle = ct.subColor || ct.nameColor || ct.color; c.fillText(ct.sub, x, ct.sy ?? y + 92); }
|
|
86
|
+
c.restore();
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// ---------- 年份计数器(里程表逐位滚动) ----------
|
|
90
|
+
// from/to 是数值;fmt 把数值格式化成字符串;滚动时每一位独立从旧字符滚到新字符,低位先动、错开 stagger。
|
|
91
|
+
U.odometer = function (c, opt, p) {
|
|
92
|
+
const { x, y, size, font, color, fromStr, toStr, align = 'right', stagger = 0.12, spacing = 0, shadow } = opt;
|
|
93
|
+
c.save();
|
|
94
|
+
c.font = `${size}px ${font}`;
|
|
95
|
+
c.textBaseline = 'alphabetic';
|
|
96
|
+
c.fillStyle = color;
|
|
97
|
+
const n = Math.max(fromStr.length, toStr.length);
|
|
98
|
+
const a = fromStr.padStart(n, ' '), b = toStr.padStart(n, ' ');
|
|
99
|
+
const widths = [...b].map(ch => c.measureText(ch === ' ' ? '0' : ch).width + spacing);
|
|
100
|
+
const total = widths.reduce((s, w) => s + w, 0) - spacing;
|
|
101
|
+
let cx = align === 'right' ? x - total : align === 'center' ? x - total / 2 : x;
|
|
102
|
+
for (let i = 0; i < n; i++) {
|
|
103
|
+
const order = n - 1 - i; // 低位先滚
|
|
104
|
+
const pi = U.clamp((p - order * stagger) / Math.max(0.0001, 1 - (n - 1) * stagger));
|
|
105
|
+
const e = U.ease.inOut(pi);
|
|
106
|
+
const w = widths[i];
|
|
107
|
+
c.save();
|
|
108
|
+
c.beginPath(); c.rect(cx - 4, y - size * 1.05, w + 8, size * 1.35); c.clip();
|
|
109
|
+
if (shadow) { c.shadowColor = shadow.color; c.shadowBlur = shadow.blur || 0; c.shadowOffsetX = shadow.x || 0; c.shadowOffsetY = shadow.y || 0; }
|
|
110
|
+
if (a[i] === b[i]) c.fillText(b[i], cx, y);
|
|
111
|
+
else {
|
|
112
|
+
const dy = size * 1.1 * e;
|
|
113
|
+
c.globalAlpha = 1 - e * 0.6; if (a[i] !== ' ') c.fillText(a[i], cx, y - dy);
|
|
114
|
+
c.globalAlpha = 0.4 + e * 0.6; if (b[i] !== ' ') c.fillText(b[i], cx, y + size * 1.1 - dy);
|
|
115
|
+
}
|
|
116
|
+
c.restore();
|
|
117
|
+
cx += w;
|
|
118
|
+
}
|
|
119
|
+
c.restore();
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// ---------- 年份计数器:数值插值(ease-out)+个位数滚动残影 ----------
|
|
123
|
+
// ct: {year, x, y, size, font, color, suffix:{font,size,color,dy,gap}, label:{text,font,color,x,y,spacing}, box(c), shadow, spacing}
|
|
124
|
+
U.fmtYear = (v) => { const a = Math.abs(Math.round(v)); return a >= 10000 ? a.toLocaleString('en-US') : String(a); };
|
|
125
|
+
// 计数器的值只由全局时间决定:转场中新旧两层显示同一个数字
|
|
126
|
+
U.counterAt = function (t) {
|
|
127
|
+
let k = 0; for (let i = 0; i < ERAS.length; i++) if (t >= ERAS[i].t0) k = i;
|
|
128
|
+
const cur = ERAS[k].counter, prv = k ? ERAS[k - 1].counter : cur;
|
|
129
|
+
const p = cur.roll ? U.clamp((t - ERAS[k].t0 - 0.03) / cur.roll) : 1;
|
|
130
|
+
return { v: prv.year + (cur.year - prv.year) * U.ease.inOut(p), rolling: p > 0 && p < 1 };
|
|
131
|
+
};
|
|
132
|
+
U.yearCounter = function (c, ct, v, rolling, lt) {
|
|
133
|
+
if (ct.box) ct.box(c, lt);
|
|
134
|
+
const vi = Math.round(v);
|
|
135
|
+
const str = U.fmtYear(vi);
|
|
136
|
+
const era = v < 0 ? 'BC' : 'AD';
|
|
137
|
+
c.save();
|
|
138
|
+
c.textBaseline = 'alphabetic';
|
|
139
|
+
if (ct.shadow) { c.shadowColor = ct.shadow.color; c.shadowBlur = ct.shadow.blur || 0; c.shadowOffsetX = ct.shadow.x || 0; c.shadowOffsetY = ct.shadow.y || 0; }
|
|
140
|
+
let x = ct.x;
|
|
141
|
+
// 后缀(BC/AD)
|
|
142
|
+
const sfx = ct.suffix && (ct.suffix.show === 'always' || (ct.suffix.show !== 'never')) ? ((ct.suffix.text === 'BC' || ct.suffix.text === 'AD') ? era : (ct.suffix.text || era)) : '';
|
|
143
|
+
if (sfx) {
|
|
144
|
+
c.font = `${ct.suffix.size}px "${ct.suffix.font || ct.font}"`; c.fillStyle = ct.suffix.color || ct.color; c.textAlign = 'right';
|
|
145
|
+
c.fillText(sfx, x, ct.y + (ct.suffix.dy || 0));
|
|
146
|
+
x -= c.measureText(sfx).width + (ct.suffix.gap || 12);
|
|
147
|
+
}
|
|
148
|
+
c.font = `${ct.size}px "${ct.font}"`; c.fillStyle = ct.color; c.textAlign = 'left';
|
|
149
|
+
if (ct.spacing) c.letterSpacing = ct.spacing + 'px';
|
|
150
|
+
const w = c.measureText(str).width - (ct.spacing || 0);
|
|
151
|
+
const x0 = x - w;
|
|
152
|
+
// 滚动中:除个位外照常画,个位画成上下滚动
|
|
153
|
+
if (rolling) {
|
|
154
|
+
const head = str.slice(0, -1), last = str.slice(-1);
|
|
155
|
+
c.fillText(head, x0, ct.y);
|
|
156
|
+
const hw = head ? c.measureText(head).width : 0;
|
|
157
|
+
const frac = (Math.abs(v) % 1);
|
|
158
|
+
const lw = c.measureText(last).width;
|
|
159
|
+
c.save(); c.beginPath(); c.rect(x0 + hw - 2, ct.y - ct.size * 1.0, lw + 30, ct.size * 1.25); c.clip();
|
|
160
|
+
c.fillText(last, x0 + hw, ct.y - frac * ct.size * 0.3);
|
|
161
|
+
c.globalAlpha = 0.35; c.fillText(String((+last + 1) % 10), x0 + hw, ct.y - ct.size * 0.85 - frac * ct.size * 0.3);
|
|
162
|
+
c.restore();
|
|
163
|
+
} else c.fillText(str, x0, ct.y);
|
|
164
|
+
c.restore();
|
|
165
|
+
if (ct.label) {
|
|
166
|
+
const L = ct.label; c.save(); c.font = L.font; c.fillStyle = L.color; c.textAlign = L.align || 'right'; c.textBaseline = 'alphabetic';
|
|
167
|
+
if (L.spacing) c.letterSpacing = L.spacing + 'px';
|
|
168
|
+
if (L.shadow) { c.shadowColor = L.shadow.color; c.shadowBlur = L.shadow.blur || 0; c.shadowOffsetX = L.shadow.x || 0; c.shadowOffsetY = L.shadow.y || 0; }
|
|
169
|
+
if (L.gradient) { const g = c.createLinearGradient(0, L.y - 30, 0, L.y); L.gradient.forEach((col, i) => g.addColorStop(i / (L.gradient.length - 1), col)); c.fillStyle = g; }
|
|
170
|
+
c.fillText(L.text, (L.x || ct.x) + (L.spacing && (L.align || 'right') === 'right' ? L.spacing : 0), L.y); c.restore();
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// ---------- 热气(通用:多条正弦卷曲的半透明笔触) ----------
|
|
175
|
+
U.steam = function (c, t, { x, y, h = 70, n = 3, w = 3, color = 'rgba(255,255,255,.8)', spread = 14, speed = 1, seed = 1, wobble = 8 }) {
|
|
176
|
+
c.save(); c.strokeStyle = color; c.lineCap = 'round'; c.lineWidth = w;
|
|
177
|
+
for (let k = 0; k < n; k++) {
|
|
178
|
+
const ox = (k - (n - 1) / 2) * spread;
|
|
179
|
+
const ph = t * 2.2 * speed + k * 1.7 + seed;
|
|
180
|
+
c.beginPath();
|
|
181
|
+
for (let s = 0; s <= 24; s++) {
|
|
182
|
+
const q = s / 24, yy = y - q * h, xx = x + ox + Math.sin(ph + q * 5) * wobble * q;
|
|
183
|
+
c.globalAlpha = 1;
|
|
184
|
+
s ? c.lineTo(xx, yy) : c.moveTo(xx, yy);
|
|
185
|
+
}
|
|
186
|
+
const g = c.createLinearGradient(0, y, 0, y - h); g.addColorStop(0, color); g.addColorStop(1, 'rgba(255,255,255,0)');
|
|
187
|
+
c.strokeStyle = g; c.stroke();
|
|
188
|
+
}
|
|
189
|
+
c.restore();
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// ---------- 视频层:每个时代可挂一段帧序列(clips/<id>/%04d.jpg),按需异步加载 ----------
|
|
193
|
+
// clip: {dir, fps, n, in, speed, loop} 本段局部时间 lt → 片内时间 in + lt*speed → 帧号
|
|
194
|
+
const FRAMES = new Map(); // url -> Image(已 decode)
|
|
195
|
+
function clipFrameUrl(e, lt) {
|
|
196
|
+
const c = e.clip; let ct = (c.in || 0) + Math.max(0, lt) * (c.speed || 1);
|
|
197
|
+
let i = Math.round(ct * c.fps);
|
|
198
|
+
if (c.loop === 'pingpong') { const m = 2 * (c.n - 1); i = i % m; if (i >= c.n) i = m - i; } else i = Math.min(c.n - 1, i);
|
|
199
|
+
return `${c.dir}/${String(i + 1).padStart(4, '0')}.jpg`;
|
|
200
|
+
}
|
|
201
|
+
async function need(url) {
|
|
202
|
+
if (FRAMES.has(url)) return;
|
|
203
|
+
const im = new Image(); im.src = url;
|
|
204
|
+
try { await im.decode(); } catch (e) { const msg = `视频帧缺失或解不开:${url}(clip.dir / n / in 对不上帧序列)`; console.error(msg); throw new Error(msg); }
|
|
205
|
+
FRAMES.set(url, im);
|
|
206
|
+
if (FRAMES.size > 240) { const k = FRAMES.keys().next().value; FRAMES.delete(k); }
|
|
207
|
+
}
|
|
208
|
+
function layersAt(t) {
|
|
209
|
+
let k = ERAS.findIndex(e => t >= e.t0 && t < e.t1); if (k < 0) k = ERAS.length - 1;
|
|
210
|
+
const out = [k]; const e = ERAS[k];
|
|
211
|
+
if (k > 0 && e.transition && t - e.t0 < e.transition.dur + (e.transition.delay || 0)) out.push(k - 1);
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
214
|
+
window.prepare = async function (t) {
|
|
215
|
+
await Promise.all(layersAt(t).map(k => ERAS[k].clip ? need(clipFrameUrl(ERAS[k], t - ERAS[k].t0)) : null));
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// ---------- 画一个时代(本段局部时间 lt) ----------
|
|
219
|
+
function drawEra(c, e, t, prev, punchFrom, noCounter) {
|
|
220
|
+
const lt = t - e.t0;
|
|
221
|
+
c.save();
|
|
222
|
+
c.clearRect(0, 0, W, H);
|
|
223
|
+
// 拍点镜头冲击(只作用于场景层,年份牌不缩放):s(k)=1+0.03·(1-k/20)^1.5,k=转场开始后的帧数
|
|
224
|
+
// 冲击跟「正在进来的那一段」的转场起点走:转场期间旧段也一起放大(原片拍点那一帧整屏都在冲)
|
|
225
|
+
const pe = punchFrom || e;
|
|
226
|
+
// 幅度:transition.punch(数字;false/0 关掉)> 片级 window.PUNCH > 0.03。解说/发布会/数据片不要这一下,写 window.PUNCH = 0。
|
|
227
|
+
const pa = pe.transition ? (pe.transition.punch ?? window.PUNCH ?? 0.03) : 0, amp = pa === true ? 0.03 : +pa || 0;
|
|
228
|
+
const k60 = amp ? (t - pe.t0) * 60 : 99;
|
|
229
|
+
const punch = k60 >= 0 && k60 < 20 ? 1 + amp * Math.pow(1 - k60 / 20, 1.5) : 1;
|
|
230
|
+
c.save(); if (punch !== 1) { c.translate(W / 2, H / 2); c.scale(punch, punch); c.translate(-W / 2, -H / 2); }
|
|
231
|
+
// 视频帧:等比铺满(MD.cover,不拉伸);段里写 clipDraw(c, frame, lt, t) 自己摆(推近、焦点、冲印件)。帧没准备好不许静默空白——直接报错
|
|
232
|
+
if (e.clip) { const url = clipFrameUrl(e, lt), im = FRAMES.get(url);
|
|
233
|
+
if (!im) throw new Error(`视频帧没加载:${url}(渲染前要先 await window.prepare(t);render.py 已经这么做,自己写的脚本也要)`);
|
|
234
|
+
if (e.clipDraw) e.clipDraw(c, im, lt, t); else MD.cover(c, im); }
|
|
235
|
+
else if (e.plate && !e.draw) c.drawImage(IMG[e.plate], 0, 0, W, H);
|
|
236
|
+
if (e.draw) e.draw(c, lt, t, IMG, prev);
|
|
237
|
+
c.restore();
|
|
238
|
+
if (!noCounter) drawCounter(c, e, t);
|
|
239
|
+
c.restore();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ---------- 主渲染 ----------
|
|
243
|
+
function renderFrame(t) {
|
|
244
|
+
let k = ERAS.findIndex(e => t >= e.t0 && t < e.t1); if (k < 0) k = ERAS.length - 1;
|
|
245
|
+
const e = ERAS[k], prev = ERAS[k - 1];
|
|
246
|
+
const tr = e.transition;
|
|
247
|
+
const trDur = tr ? tr.dur + (tr.delay || 0) : 0;
|
|
248
|
+
const lt = t - e.t0;
|
|
249
|
+
if (prev && tr && lt < trDur) {
|
|
250
|
+
const cut = (tr.counter || window.COUNTER_IN_TRANSITION || 'cut') === 'cut';
|
|
251
|
+
drawEra(bufA.getContext('2d'), prev, t, ERAS[k - 2], e, cut);
|
|
252
|
+
drawEra(bufB.getContext('2d'), e, t, prev, undefined, cut);
|
|
253
|
+
const p = U.clamp((lt - (tr.delay || 0)) / tr.dur);
|
|
254
|
+
ctx.save(); ctx.clearRect(0, 0, W, H);
|
|
255
|
+
// o.id / o.from = 进入段 / 离开段的 id:依赖画面内容的转场缓存要带上它(transitions.js 的 once(k, f, o))
|
|
256
|
+
TRANSITIONS[tr.type](ctx, bufA, bufB, p, { ...tr, id: e.id, from: prev.id, lt, t, W, H, tmp: bufT, IMG });
|
|
257
|
+
ctx.restore();
|
|
258
|
+
if (cut) drawCounter(ctx, p < 0.5 ? prev : e, t);
|
|
259
|
+
} else {
|
|
260
|
+
drawEra(ctx, e, t, prev);
|
|
261
|
+
}
|
|
262
|
+
if (window.GLOBAL_OVERLAY) window.GLOBAL_OVERLAY(ctx, t);
|
|
263
|
+
return k;
|
|
264
|
+
}
|
|
265
|
+
window.renderFrame = renderFrame;
|
|
266
|
+
// 单段预览:opt.counter = false 不画角标(自检量帧差时计数器滚动会抬高数字——迁移测试 B)
|
|
267
|
+
window.renderSolo = function (id, lt, opt = {}) { const e = ERAS.find(x => x.id === id); if (!e) throw new Error('renderSolo: 没有这一段 ' + id); const k = ERAS.indexOf(e); drawEra(ctx, e, e.t0 + lt, ERAS[k - 1], undefined, opt.counter === false); };
|
|
268
|
+
|
|
269
|
+
// ---------- 预览 UI ----------
|
|
270
|
+
if (location.search.includes('render=1')) document.body.classList.add('render');
|
|
271
|
+
const scrub = document.getElementById('scrub'), tt = document.getElementById('tt'); scrub.max = window.__total;
|
|
272
|
+
let playing = false, t0 = 0, base = 0;
|
|
273
|
+
scrub.oninput = () => { playing = false; renderFrame(+scrub.value); tt.textContent = (+scrub.value).toFixed(3); };
|
|
274
|
+
document.getElementById('play').onclick = () => { playing = !playing; t0 = performance.now(); base = +scrub.value; if (playing) loop(); };
|
|
275
|
+
function loop() { if (!playing) return; let t = base + (performance.now() - t0) / 1000; if (t > window.__total) { t = 0; base = 0; t0 = performance.now(); } scrub.value = t; tt.textContent = t.toFixed(3); renderFrame(t); requestAnimationFrame(loop); }
|
|
276
|
+
boot().catch(err => { window.__bootFailed = String(err && err.stack || err); console.error('BOOT FAILED\n' + window.__bootFailed); });
|
|
277
|
+
})();
|
package/engine/eras.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// 纯代码版:每个时代的画面由 scenes/<id>.js 的 draw(c, lt, t) 程序化绘制。
|
|
2
|
+
// 15 个时代 + 2026 结尾。eighths = 本段占几个八分音符(128 BPM,见 references/风格配方/_音轨_艺术史速通.md)。
|
|
3
|
+
const R = 1866; // 右对齐基准
|
|
4
|
+
const box = (x0, y0, x1, y1, fill, stroke, lw = 4, inset = 0) => c => {
|
|
5
|
+
c.save(); c.fillStyle = fill; c.fillRect(x0, y0, x1 - x0, y1 - y0);
|
|
6
|
+
if (stroke) { c.strokeStyle = stroke; c.lineWidth = lw; c.strokeRect(x0 + inset, y0 + inset, x1 - x0 - 2 * inset, y1 - y0 - 2 * inset); }
|
|
7
|
+
c.restore();
|
|
8
|
+
};
|
|
9
|
+
// 原片复刻:年份牌位置固定、数字连续滚动,转场时角标留在新旧两层里一起变(engine 默认是转场中点硬切,见 drawCounter)
|
|
10
|
+
window.COUNTER_IN_TRANSITION = 'layers';
|
|
11
|
+
window.ERAS = [
|
|
12
|
+
{ id: '01_cave', eighths: 6,
|
|
13
|
+
counter: { year: -40000, roll: 0, x: R, y: 114, size: 84, font: 'GochiHand-400', color: '#f3ead8', spacing: 14,
|
|
14
|
+
suffix: { text: 'BC', size: 44, gap: 18, dy: 0 }, label: { text: 'Cave painting', font: '38px "Kalam-700"', color: '#efe4cf', y: 178 } } },
|
|
15
|
+
{ id: '02_egypt', eighths: 4, transition: { type: 'tiles', dur: 0.42, delay: 0 },
|
|
16
|
+
counter: { year: -1350, roll: 0.45, x: R, y: 129, size: 100, font: 'IMFellEnglish-400', color: '#6b2318', spacing: 6,
|
|
17
|
+
suffix: { text: 'BC', size: 50, gap: 14 }, label: { text: 'Egyptian wall painting', font: '34px "EBGaramond-400"', color: '#4a3424', y: 186 } } },
|
|
18
|
+
{ id: '03_greek', eighths: 4, transition: { type: 'cube', dur: 0.32, delay: 0 },
|
|
19
|
+
counter: { year: -530, roll: 0.47, x: 1855, y: 136, size: 98, font: 'Cinzel-700', color: '#e3a066',
|
|
20
|
+
box: box(1544, 42, 1888, 208, '#1b1310', '#c8743c', 3, 4),
|
|
21
|
+
suffix: { text: 'BC', size: 48, gap: 14 }, label: { text: 'ATTIC BLACK-FIGURE', font: '29px "Cinzel-700"', color: '#e8b27c', x: 1855, y: 184, spacing: 2 } } },
|
|
22
|
+
{ id: '04_roman', eighths: 4, transition: { type: 'mosaic', dur: 0.38, delay: 0 },
|
|
23
|
+
counter: { year: 79, roll: 0.43, x: 1852, y: 132, size: 96, font: 'Cinzel-400', color: '#1d1a17',
|
|
24
|
+
box: box(1607, 40, 1888, 208, '#f4ede0', '#2b2724', 4, 0),
|
|
25
|
+
suffix: { text: 'AD', size: 46, gap: 18 }, label: { text: 'ROMAN MOSAIC', font: '26px "Cinzel-400"', color: '#2b2724', x: 1860, y: 182, spacing: 2 } } },
|
|
26
|
+
{ id: '05_gothic', eighths: 4, transition: { type: 'pageTurn', dur: 0.32, delay: 0 },
|
|
27
|
+
counter: { year: 1290, roll: 0.45, x: R, y: 134, size: 100, font: 'LibreBaskerville-700', color: '#b3222b',
|
|
28
|
+
label: { text: 'Gothic illumination', font: '36px "IMFellEnglish-400"', color: '#2b211a', y: 184 } } },
|
|
29
|
+
{ id: '06_renaissance', eighths: 4, transition: { type: 'godRays', dur: 0.4, delay: 0, cx: 560, cy: 290 },
|
|
30
|
+
counter: { year: 1503, roll: 0.42, x: R, y: 142, size: 104, font: 'CormorantGaramond-500', color: '#ece0bf', spacing: 22,
|
|
31
|
+
label: { text: 'HIGH RENAISSANCE', font: '30px "Cinzel-700"', color: '#e7d9b4', y: 184, spacing: 2 } } },
|
|
32
|
+
{ id: '08_impressionism', eighths: 3, transition: { type: 'dabs', dur: 0.28, delay: 0 },
|
|
33
|
+
counter: { year: 1874, roll: 0.37, x: R, y: 139, size: 104, font: 'PlayfairDisplay-400i', color: '#5a4f9a',
|
|
34
|
+
label: { text: 'Impressionism', font: '30px "EBGaramond-600"', color: '#6a5a9a', y: 182 } } },
|
|
35
|
+
{ id: '09_postimp', eighths: 3, transition: { type: 'swirl', dur: 0.28, delay: 0, cx: 560, cy: 340 },
|
|
36
|
+
counter: { year: 1889, roll: 0.34, x: R, y: 134, size: 104, font: 'LilitaOne-400', color: '#f5d03b', shadow: { color: '#2a2a6a', x: 3, y: 4 },
|
|
37
|
+
label: { text: 'Post-Impressionism', font: '34px "LilitaOne-400"', color: '#f5d03b', y: 182, shadow: { color: '#2a2a6a', x: 2, y: 3 } } } },
|
|
38
|
+
{ id: '10_nouveau', eighths: 3, transition: { type: 'organic', dur: 0.24, delay: 0, cx: 1330, cy: 320 },
|
|
39
|
+
counter: { year: 1896, roll: 0.37, x: R, y: 134, size: 96, font: 'LibreBaskerville-700', color: '#3a2a1c',
|
|
40
|
+
label: { text: 'ART NOUVEAU POSTER', font: '29px "EBGaramond-600"', color: '#3a2a1c', y: 182, spacing: 3 } } },
|
|
41
|
+
{ id: '11_cubism', eighths: 2, transition: { type: 'shards', dur: 0.24, delay: 0 },
|
|
42
|
+
counter: { year: 1912, roll: 0.23, x: R, y: 134, size: 98, font: 'AlfaSlabOne-400', color: '#1e1b18', spacing: 4,
|
|
43
|
+
label: { text: 'CUBISM', font: '27px "AlfaSlabOne-400"', color: '#1e1b18', y: 182, spacing: 3 } } },
|
|
44
|
+
{ id: '12_bauhaus', eighths: 2, transition: { type: 'bauhaus', dur: 0.2, delay: 0, cx: 1330, cy: 590 },
|
|
45
|
+
counter: { year: 1923, roll: 0.2, x: R, y: 134, size: 104, font: 'ArchivoBlack-400', color: '#151515',
|
|
46
|
+
label: { text: 'bauhaus', font: '36px "Poppins-800"', color: '#c4202b', y: 184 } } },
|
|
47
|
+
{ id: '13_pop', eighths: 2, transition: { type: 'zigzag', dur: 0.16, delay: 0 },
|
|
48
|
+
counter: { year: 1962, roll: 0.22, x: 1852, y: 144, size: 104, font: 'Anton-400', color: '#111',
|
|
49
|
+
box: c => { c.save(); c.fillStyle = '#111'; c.fillRect(1637, 40, 253, 170); c.fillStyle = '#f7e23a'; c.fillRect(1643, 46, 241, 158); c.restore(); },
|
|
50
|
+
label: { text: 'POP ART', font: '30px "Anton-400"', color: '#111', x: 1852, y: 192, spacing: 1 } } },
|
|
51
|
+
{ id: '14_8bit', eighths: 1, transition: { type: 'pixelate', dur: 0.12, delay: 0 },
|
|
52
|
+
counter: { year: 1985, roll: 0.07, x: R + 4, y: 86, size: 60, font: 'PressStart2P-400', color: '#ffffff', shadow: { color: '#0b1d24', x: 4, y: 4 },
|
|
53
|
+
label: { text: '8-BIT', font: '30px "PressStart2P-400"', color: '#ffffff', y: 138, shadow: { color: '#0b1d24', x: 3, y: 3 } } } },
|
|
54
|
+
{ id: '15_raytrace', eighths: 1, transition: { type: 'wireframe', dur: 0.14, delay: 0 },
|
|
55
|
+
counter: { year: 1993, roll: 0.07, x: R, y: 129, size: 104, font: 'Exo2-800i', color: '#ffffff', shadow: { color: '#d23aa6', x: 5, y: 5 },
|
|
56
|
+
label: { text: 'RAY-TRACED CGI', font: '30px "Exo2-800i"', gradient: ['#ffd84a', '#f08a2a'], y: 182, shadow: { color: '#7a2a7a', x: 2, y: 2 } } } },
|
|
57
|
+
{ id: '16_2026', eighths: 1 + 9 + 8, dur: 4.28, finale: true, transition: { type: 'iris', dur: 0.183, delay: 0 },
|
|
58
|
+
counter: { year: 2026, roll: 0.4, x: R, y: 132, size: 100, font: 'Poppins-800', color: '#232a5c',
|
|
59
|
+
label: { text: 'Flat vector illustration', font: '30px "Poppins-600"', color: '#e2554e', y: 182 } } },
|
|
60
|
+
];
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// 全风格样片:skill 里 35 种风格(15 原段+迁移测试 A–D 的 20 段)各一段,按年代排,用作回归测试与风格目录。
|
|
2
|
+
// 打开:index.html?film=gallery 渲染:render.py --film gallery 验收:qa.py --project scripts/engine --film gallery
|
|
3
|
+
// 每段 5 个八分音符(128 BPM ≈ 1.17s),最后一段 6 个。相邻段用「进入段」的签名转场(原 16 段沿用原片转场,A–D 组用各自配方卡里的转场;
|
|
4
|
+
// 每组第一段原来在片头没有进入转场,样片补了 inkBloom / redWedge / comicPanels / chiaroscuro)。
|
|
5
|
+
// 角标:年份严格单调,所以全片一个年份计数器连续滚动;各段沿用自己风格的字体与版式,label 写风格名。转场中点硬切新旧角标(引擎默认)。
|
|
6
|
+
// name 字段只给总览图/文档用(「年份+风格名」),引擎不读。
|
|
7
|
+
const R = 1866;
|
|
8
|
+
const box = (x0, y0, x1, y1, fill, stroke, lw = 4, inset = 0) => c => {
|
|
9
|
+
c.save(); c.fillStyle = fill; c.fillRect(x0, y0, x1 - x0, y1 - y0);
|
|
10
|
+
if (stroke) { c.strokeStyle = stroke; c.lineWidth = lw; c.strokeRect(x0 + inset, y0 + inset, x1 - x0 - 2 * inset, y1 - y0 - 2 * inset); }
|
|
11
|
+
c.restore();
|
|
12
|
+
};
|
|
13
|
+
window.ERAS = [
|
|
14
|
+
{ id: '01_cave', name: '洞穴壁画', eighths: 5,
|
|
15
|
+
counter: { year: -40000, roll: 0, x: R, y: 114, size: 84, font: 'GochiHand-400', color: '#f3ead8', spacing: 14,
|
|
16
|
+
suffix: { text: 'BC', size: 44, gap: 18, dy: 0 }, label: { text: 'Cave painting', font: '38px "Kalam-700"', color: '#efe4cf', y: 178 } } },
|
|
17
|
+
{ id: '02_egypt', name: '古埃及壁画', eighths: 5, transition: { type: 'tiles', dur: 0.42 },
|
|
18
|
+
counter: { year: -1350, roll: 0.4, x: R, y: 129, size: 100, font: 'IMFellEnglish-400', color: '#6b2318', spacing: 6,
|
|
19
|
+
suffix: { text: 'BC', size: 50, gap: 14 }, label: { text: 'Egyptian wall painting', font: '34px "EBGaramond-400"', color: '#4a3424', y: 186 } } },
|
|
20
|
+
{ id: '03_greek', name: '古希腊黑绘陶', eighths: 5, transition: { type: 'cube', dur: 0.32 },
|
|
21
|
+
counter: { year: -530, roll: 0.4, x: 1855, y: 136, size: 98, font: 'Cinzel-700', color: '#e3a066',
|
|
22
|
+
box: box(1544, 42, 1888, 208, '#1b1310', '#c8743c', 3, 4),
|
|
23
|
+
suffix: { text: 'BC', size: 48, gap: 14 }, label: { text: 'ATTIC BLACK-FIGURE', font: '29px "Cinzel-700"', color: '#e8b27c', x: 1855, y: 184, spacing: 2 } } },
|
|
24
|
+
{ id: '04_roman', name: '罗马马赛克', eighths: 5, transition: { type: 'mosaic', dur: 0.38 },
|
|
25
|
+
counter: { year: 79, roll: 0.4, x: 1852, y: 132, size: 96, font: 'Cinzel-400', color: '#1d1a17',
|
|
26
|
+
box: box(1607, 40, 1888, 208, '#f4ede0', '#2b2724', 4, 0),
|
|
27
|
+
suffix: { text: 'AD', size: 46, gap: 18 }, label: { text: 'ROMAN MOSAIC', font: '26px "Cinzel-400"', color: '#2b2724', x: 1860, y: 182, spacing: 2 } } },
|
|
28
|
+
{ id: '20_dunhuang', name: '敦煌壁画', eighths: 5, transition: { type: 'apsara', dur: 0.5 },
|
|
29
|
+
counter: { year: 720, roll: 0.4, x: 1852, y: 132, size: 92, font: 'LXGWWenKai-500', color: '#2a1a14', box: box(1500, 44, 1880, 204, '#e6d6b2', '#5a2414', 4, 6), suffix: { text: 'AD', size: 40, gap: 14, font: 'Cinzel-700' }, label: { text: '敦煌壁画 · 盛唐', font: '34px "LXGWWenKai-500"', color: '#8a2a1c', x: 1852, y: 184, spacing: 4 } } },
|
|
30
|
+
{ id: '34_shadowpuppet', name: '皮影戏', eighths: 5, transition: { type: 'lampGlow', dur: 0.4, cx: 960, cy: 470 },
|
|
31
|
+
counter: { year: 1050, roll: 0.4, x: R, y: 132, size: 96, font: 'LXGWWenKai-500', color: '#7a1d12', spacing: 4, suffix: { text: 'AD', size: 40, gap: 14, font: 'Cinzel-700' }, label: { text: '皮影戏 · 北宋', font: '36px "LXGWWenKai-500"', color: '#5a2a14', y: 184, spacing: 4 } } },
|
|
32
|
+
{ id: '05_gothic', name: '哥特手抄本', eighths: 5, transition: { type: 'pageTurn', dur: 0.32 },
|
|
33
|
+
counter: { year: 1290, roll: 0.4, x: R, y: 134, size: 100, font: 'LibreBaskerville-700', color: '#b3222b',
|
|
34
|
+
label: { text: 'Gothic illumination', font: '36px "IMFellEnglish-400"', color: '#2b211a', y: 184 } } },
|
|
35
|
+
{ id: '06_renaissance', name: '文艺复兴', eighths: 5, transition: { type: 'godRays', dur: 0.4, cx: 560, cy: 290 },
|
|
36
|
+
counter: { year: 1503, roll: 0.4, x: R, y: 142, size: 104, font: 'CormorantGaramond-500', color: '#ece0bf', spacing: 22,
|
|
37
|
+
label: { text: 'HIGH RENAISSANCE', font: '30px "Cinzel-700"', color: '#e7d9b4', y: 184, spacing: 2 } } },
|
|
38
|
+
{ id: '32_rembrandt', name: '伦勃朗明暗法', eighths: 5, transition: { type: 'chiaroscuro', dur: 0.45, cx: 580, cy: 300 },
|
|
39
|
+
counter: { year: 1642, roll: 0.4, x: R, y: 134, size: 104, font: 'CormorantGaramond-500', color: '#e9c98a', spacing: 10, label: { text: 'Rembrandt · chiaroscuro', font: '34px "IMFellEnglish-400i"', color: '#c9a467', y: 184 } } },
|
|
40
|
+
{ id: '17_ink', name: '水墨写意', eighths: 5, transition: { type: 'inkBloom', dur: 0.45, cx: 560, cy: 740 },
|
|
41
|
+
counter: { year: 1690, roll: 0.4, x: R, y: 132, size: 100, font: 'LXGWWenKai-500', color: '#1a1612', spacing: 4, label: { text: '水墨写意 · 八大山人', font: '38px "LXGWWenKai-500"', color: '#3a2c22', y: 186, spacing: 4 } } },
|
|
42
|
+
{ id: '08_impressionism', name: '印象派', eighths: 5, transition: { type: 'dabs', dur: 0.28 },
|
|
43
|
+
counter: { year: 1874, roll: 0.4, x: R, y: 139, size: 104, font: 'PlayfairDisplay-400i', color: '#5a4f9a',
|
|
44
|
+
label: { text: 'Impressionism', font: '30px "EBGaramond-600"', color: '#6a5a9a', y: 182 } } },
|
|
45
|
+
{ id: '29_seurat', name: '修拉点彩', eighths: 5, transition: { type: 'pointill', dur: 0.34 },
|
|
46
|
+
counter: { year: 1884, roll: 0.4, x: R, y: 134, size: 100, font: 'Marcellus-400', color: '#20306a', spacing: 6,
|
|
47
|
+
label: { text: 'SEURAT · LA GRANDE JATTE', font: '27px "Marcellus-400"', color: '#7a3a2a', y: 182, spacing: 2 } } },
|
|
48
|
+
{ id: '09_postimp', name: '后印象派(梵高)', eighths: 5, transition: { type: 'swirl', dur: 0.28, cx: 560, cy: 340 },
|
|
49
|
+
counter: { year: 1889, roll: 0.4, x: R, y: 134, size: 104, font: 'LilitaOne-400', color: '#f5d03b', shadow: { color: '#2a2a6a', x: 3, y: 4 },
|
|
50
|
+
label: { text: 'Post-Impressionism', font: '34px "LilitaOne-400"', color: '#f5d03b', y: 182, shadow: { color: '#2a2a6a', x: 2, y: 3 } } } },
|
|
51
|
+
{ id: '19_munch', name: '蒙克表现主义', eighths: 5, transition: { type: 'screamWarp', dur: 0.5 },
|
|
52
|
+
counter: { year: 1893, roll: 0.4, x: R, y: 134, size: 100, font: 'IMFellEnglish-400', color: '#f6c040', shadow: { color: 'rgba(40,10,20,.6)', x: 4, y: 4 }, label: { text: 'Munch · The Scream', font: '38px "IMFellEnglish-400i"', color: '#f8d88a', y: 184, shadow: { color: 'rgba(40,10,20,.6)', x: 2, y: 3 } } } },
|
|
53
|
+
{ id: '10_nouveau', name: '新艺术运动海报', eighths: 5, transition: { type: 'organic', dur: 0.24, cx: 1330, cy: 320 },
|
|
54
|
+
counter: { year: 1896, roll: 0.4, x: R, y: 134, size: 96, font: 'LibreBaskerville-700', color: '#3a2a1c',
|
|
55
|
+
label: { text: 'ART NOUVEAU POSTER', font: '29px "EBGaramond-600"', color: '#3a2a1c', y: 182, spacing: 3 } } },
|
|
56
|
+
{ id: '28_monet', name: '莫奈睡莲', eighths: 5, transition: { type: 'ripple', dur: 0.34, cx: 1000, cy: 600 },
|
|
57
|
+
counter: { year: 1899, roll: 0.4, x: R, y: 136, size: 104, font: 'PlayfairDisplay-400i', color: '#3e5a86', spacing: 4, shadow: { color: 'rgba(252,248,238,1)', blur: 18, x: 0, y: 0 },
|
|
58
|
+
label: { text: 'Monet · Nymphéas', font: '36px "PlayfairDisplay-400i"', color: '#4a5f8e', y: 186, shadow: { color: 'rgba(252,248,238,1)', blur: 14, x: 0, y: 0 } } } },
|
|
59
|
+
{ id: '36_picasso_blue', name: '毕加索蓝色时期', eighths: 5, transition: { type: 'blueBrush', dur: 0.42 },
|
|
60
|
+
counter: { year: 1903, roll: 0.4, x: R, y: 136, size: 108, font: 'Caveat-600', color: '#c9d6e4', label: { text: 'Picasso · période bleue', font: '38px "Caveat-600"', color: '#9fb3cc', y: 188 } } },
|
|
61
|
+
{ id: '18_klimt', name: '克里姆特金色时期', eighths: 5, transition: { type: 'goldLeaf', dur: 0.5, cx: 1300, cy: 380 },
|
|
62
|
+
counter: { year: 1907, roll: 0.4, x: R, y: 134, size: 100, font: 'Marcellus-400', color: '#f6dc8a', shadow: { color: 'rgba(40,24,6,.7)', x: 3, y: 4, blur: 6 }, label: { text: '克里姆特 · 金色时期', font: '36px "LXGWWenKai-500"', color: '#f3d27a', y: 186, shadow: { color: 'rgba(40,24,6,.7)', x: 2, y: 3, blur: 4 } } } },
|
|
63
|
+
{ id: '11_cubism', name: '立体主义', eighths: 5, transition: { type: 'shards', dur: 0.24 },
|
|
64
|
+
counter: { year: 1912, roll: 0.4, x: R, y: 134, size: 98, font: 'AlfaSlabOne-400', color: '#1e1b18', spacing: 4,
|
|
65
|
+
label: { text: 'CUBISM', font: '27px "AlfaSlabOne-400"', color: '#1e1b18', y: 182, spacing: 3 } } },
|
|
66
|
+
{ id: '12_bauhaus', name: '包豪斯', eighths: 5, transition: { type: 'bauhaus', dur: 0.2, cx: 1330, cy: 590 },
|
|
67
|
+
counter: { year: 1923, roll: 0.4, x: R, y: 134, size: 104, font: 'ArchivoBlack-400', color: '#151515',
|
|
68
|
+
label: { text: 'bauhaus', font: '36px "Poppins-800"', color: '#c4202b', y: 184 } } },
|
|
69
|
+
{ id: '22_constructivism', name: '构成主义海报', eighths: 5, transition: { type: 'redWedge', dur: 0.4 },
|
|
70
|
+
counter: { year: 1924, roll: 0.4, x: R, y: 132, size: 104, font: 'Anton-400', color: '#151311',
|
|
71
|
+
box: c => { c.save(); c.fillStyle = '#c8231c'; c.beginPath(); c.moveTo(1560, 46); c.lineTo(1892, 30); c.lineTo(1892, 152); c.lineTo(1560, 160); c.closePath(); c.fill(); c.restore(); },
|
|
72
|
+
label: { text: 'CONSTRUCTIVISM', font: '30px "ArchivoBlack-400"', color: '#151311', y: 196, spacing: 4 } } },
|
|
73
|
+
{ id: '33_rubberhose', name: '橡皮管卡通', eighths: 5, transition: { type: 'irisFilm', dur: 0.4, cx: 1236, cy: 420 },
|
|
74
|
+
counter: { year: 1930, roll: 0.4, x: 1858, y: 134, size: 100, font: 'Rye-400', color: '#141210', spacing: 4, box: c => { c.save(); c.fillStyle = '#efe7d2'; c.strokeStyle = '#141210'; c.lineWidth = 6; c.beginPath(); c.roundRect(1560, 44, 330, 166, 26); c.fill(); c.stroke(); c.restore(); }, label: { text: 'RUBBER HOSE', font: '34px "LilitaOne-400"', color: '#141210', x: 1858, y: 186, spacing: 2 } } },
|
|
75
|
+
{ id: '23_dali', name: '达利超现实', eighths: 5, transition: { type: 'melt', dur: 0.36 },
|
|
76
|
+
counter: { year: 1931, roll: 0.4, x: R, y: 136, size: 104, font: 'PlayfairDisplay-700i', color: '#f6e8c8', shadow: { color: 'rgba(10,20,50,.55)', x: 5, y: 5, blur: 4 },
|
|
77
|
+
label: { text: 'Surrealism', font: '34px "PlayfairDisplay-400i"', color: '#f0dcae', y: 184 } } },
|
|
78
|
+
{ id: '24_hopper', name: '霍珀美国现实主义', eighths: 5, transition: { type: 'sunShaft', dur: 0.34 },
|
|
79
|
+
counter: { year: 1942, roll: 0.4, x: R, y: 134, size: 100, font: 'LibreBaskerville-700', color: '#f3e3b5', shadow: { color: 'rgba(20,30,25,.55)', x: 4, y: 4 },
|
|
80
|
+
label: { text: 'AMERICAN REALISM', font: '27px "LibreBaskerville-400"', color: '#f0dca8', y: 182, spacing: 3 } } },
|
|
81
|
+
{ id: '30_matisse', name: '马蒂斯剪纸', eighths: 5, transition: { type: 'cutouts', dur: 0.36 },
|
|
82
|
+
counter: { year: 1947, roll: 0.4, x: R, y: 136, size: 108, font: 'Caveat-600', color: '#111', spacing: 2,
|
|
83
|
+
label: { text: 'Matisse · Jazz', font: '46px "Caveat-600"', color: '#c8242a', y: 188 } } },
|
|
84
|
+
{ id: '13_pop', name: '波普艺术', eighths: 5, transition: { type: 'zigzag', dur: 0.16 },
|
|
85
|
+
counter: { year: 1962, roll: 0.4, x: 1852, y: 144, size: 104, font: 'Anton-400', color: '#111',
|
|
86
|
+
box: c => { c.save(); c.fillStyle = '#111'; c.fillRect(1637, 40, 253, 170); c.fillStyle = '#f7e23a'; c.fillRect(1643, 46, 241, 158); c.restore(); },
|
|
87
|
+
label: { text: 'POP ART', font: '30px "Anton-400"', color: '#111', x: 1852, y: 192, spacing: 1 } } },
|
|
88
|
+
{ id: '21_kusama', name: '草间弥生波点', eighths: 5, transition: { type: 'dotBloom', dur: 0.48, cx: 540, cy: 760 },
|
|
89
|
+
counter: { year: 1965, roll: 0.4, x: R, y: 134, size: 104, font: 'Poppins-800', color: '#ffffff', shadow: { color: '#141414', x: 5, y: 5 }, label: { text: '草间弥生 · 无限波点', font: '36px "LXGWWenKai-500"', color: '#ffffff', y: 186, shadow: { color: '#141414', x: 3, y: 3 } } } },
|
|
90
|
+
{ id: '27_kirby', name: 'Kirby 漫画', eighths: 5, transition: { type: 'comicPanels', dur: 0.42 },
|
|
91
|
+
counter: { year: 1966, roll: 0.4, x: 1858, y: 132, size: 100, font: 'Bangers-400', color: '#151313', spacing: 4,
|
|
92
|
+
box: c => { c.save(); c.translate(6, 7); c.fillStyle = '#151313'; c.fillRect(1560, 38, 330, 176); c.restore();
|
|
93
|
+
c.save(); c.fillStyle = '#ffe23a'; c.fillRect(1560, 38, 330, 176); c.strokeStyle = '#151313'; c.lineWidth = 6; c.strokeRect(1560, 38, 330, 176); c.restore(); },
|
|
94
|
+
label: { text: 'JACK KIRBY · MARVEL', font: '34px "Bangers-400"', color: '#d8202a', x: 1858, y: 190, spacing: 2 } } },
|
|
95
|
+
{ id: '31_haring', name: '哈林涂鸦', eighths: 5, transition: { type: 'radiant', dur: 0.32, cx: 1000, cy: 600 },
|
|
96
|
+
counter: { year: 1982, roll: 0.4, x: R, y: 136, size: 108, font: 'Kalam-700', color: '#111', spacing: 2,
|
|
97
|
+
label: { text: 'KEITH HARING', font: '40px "Kalam-700"', color: '#111', y: 188, spacing: 2 } } },
|
|
98
|
+
{ id: '14_8bit', name: '8-bit 像素', eighths: 5, transition: { type: 'pixelate', dur: 0.12 },
|
|
99
|
+
counter: { year: 1985, roll: 0.4, x: R + 4, y: 86, size: 60, font: 'PressStart2P-400', color: '#ffffff', shadow: { color: '#0b1d24', x: 4, y: 4 },
|
|
100
|
+
label: { text: '8-BIT', font: '30px "PressStart2P-400"', color: '#ffffff', y: 138, shadow: { color: '#0b1d24', x: 3, y: 3 } } } },
|
|
101
|
+
{ id: '25_ghibli', name: '吉卜力水彩', eighths: 5, transition: { type: 'bleed', dur: 0.38, cx: 580, cy: 330 },
|
|
102
|
+
counter: { year: 1988, roll: 0.4, x: R, y: 132, size: 104, font: 'Kalam-700', color: '#3f6a4a',
|
|
103
|
+
label: { text: 'Ghibli-style watercolor', font: '34px "Caveat-600"', color: '#4d7a58', y: 182 } } },
|
|
104
|
+
{ id: '15_raytrace', name: '光追 CGI', eighths: 5, transition: { type: 'wireframe', dur: 0.14 },
|
|
105
|
+
counter: { year: 1993, roll: 0.4, x: R, y: 129, size: 104, font: 'Exo2-800i', color: '#ffffff', shadow: { color: '#d23aa6', x: 5, y: 5 },
|
|
106
|
+
label: { text: 'RAY-TRACED CGI', font: '30px "Exo2-800i"', gradient: ['#ffd84a', '#f08a2a'], y: 182, shadow: { color: '#7a2a7a', x: 2, y: 2 } } } },
|
|
107
|
+
{ id: '26_vaporwave', name: '蒸汽波', eighths: 5, transition: { type: 'vhs', dur: 0.3 },
|
|
108
|
+
counter: { year: 2011, roll: 0.4, x: R, y: 130, size: 104, font: 'Exo2-800i', color: '#ffffff', shadow: { color: '#ff2fb4', x: 6, y: 5 },
|
|
109
|
+
label: { text: 'VAPORWAVE', font: '32px "Righteous-400"', gradient: ['#7ff6ff', '#ff71ce'], y: 182, spacing: 7, shadow: { color: '#3a0a6a', x: 2, y: 2 } } } },
|
|
110
|
+
{ id: '35_shinkai', name: '新海诚光影', eighths: 5, transition: { type: 'flareSweep', dur: 0.38 },
|
|
111
|
+
counter: { year: 2016, roll: 0.4, x: R, y: 134, size: 100, font: 'Poppins-600', color: '#ffffff', shadow: { color: 'rgba(40,90,150,.5)', x: 3, y: 4, blur: 4 }, label: { text: 'Makoto Shinkai light', font: '30px "Poppins-600"', color: '#eaf6ff', y: 182, shadow: { color: 'rgba(40,90,150,.5)', x: 2, y: 3 } } } },
|
|
112
|
+
{ id: '16_2026', name: '2026 扁平插画', eighths: 6, transition: { type: 'iris', dur: 0.183 },
|
|
113
|
+
counter: { year: 2026, roll: 0.4, x: R, y: 132, size: 100, font: 'Poppins-800', color: '#232a5c',
|
|
114
|
+
label: { text: 'Flat vector illustration', font: '30px "Poppins-600"', color: '#e2554e', y: 182 } } },
|
|
115
|
+
];
|