dsh-drawai 0.3.4 → 0.4.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/LICENSE +4142 -0
- package/README.md +43 -10
- package/lib/audio-media.js +147 -0
- package/lib/audio-service.js +66 -0
- package/lib/canvas-format.js +134 -0
- package/lib/client.js +5075 -181
- package/lib/comfyui-default-workflow.mjs +1825 -0
- package/lib/comfyui-workflow.mjs +426 -0
- package/lib/document-commands.js +12 -0
- package/lib/document-core.js +29 -0
- package/lib/drawing-tasks.js +26 -0
- package/lib/imagegen.mjs +596 -0
- package/lib/index.js +1758 -129
- package/lib/media.js +26 -13
- package/lib/mgstyle-core.js +75 -0
- package/lib/motion/apply.js +180 -0
- package/lib/motion/asset-pack-lock.js +10 -0
- package/lib/motion/asset-pack.js +335 -0
- package/lib/motion/assets.js +290 -0
- package/lib/motion/audio.js +103 -0
- package/lib/motion/audiolevel.js +63 -0
- package/lib/motion/audiomix.js +144 -0
- package/lib/motion/beats.js +337 -0
- package/lib/motion/builtin-assets.js +62 -0
- package/lib/motion/canvas-spec.js +442 -0
- package/lib/motion/capture.js +95 -0
- package/lib/motion/cards.js +80 -0
- package/lib/motion/collect.js +447 -0
- package/lib/motion/delivery.js +112 -0
- package/lib/motion/edge-speech.py +90 -0
- package/lib/motion/entry.jsx +73 -0
- package/lib/motion/export.js +200 -0
- package/lib/motion/ffmpeg.js +42 -0
- package/lib/motion/fonts.js +71 -0
- package/lib/motion/jobs.js +113 -0
- package/lib/motion/json.js +4 -0
- package/lib/motion/media-catalog.js +59 -0
- package/lib/motion/mgstyles/BRIEF-TEMPLATE.md +63 -0
- package/lib/motion/mgstyles/briefs/01-flat-vector.md +104 -0
- package/lib/motion/mgstyles/briefs/02-line-art.md +97 -0
- package/lib/motion/mgstyles/briefs/03-isometric.md +108 -0
- package/lib/motion/mgstyles/briefs/05-cel-boil.md +145 -0
- package/lib/motion/mgstyles/briefs/06-collage.md +149 -0
- package/lib/motion/mgstyles/briefs/07-liquid.md +106 -0
- package/lib/motion/mgstyles/briefs/08-morph.md +111 -0
- package/lib/motion/mgstyles/briefs/09-bauhaus.md +150 -0
- package/lib/motion/mgstyles/briefs/10-synthwave.md +151 -0
- package/lib/motion/mgstyles/briefs/12-aurora-glass.md +147 -0
- package/lib/motion/mgstyles/briefs/18-hanazi.md +140 -0
- package/lib/motion/mgstyles/briefs/19-paperclip.md +156 -0
- package/lib/motion/mgstyles/briefs/20-pixel.md +118 -0
- package/lib/motion/mgstyles/briefs/22-hud.md +118 -0
- package/lib/motion/mgstyles/catalog.d/01-flat-vector.json +34 -0
- package/lib/motion/mgstyles/catalog.d/02-line-art.json +34 -0
- package/lib/motion/mgstyles/catalog.d/03-isometric.json +35 -0
- package/lib/motion/mgstyles/catalog.d/05-cel-boil.json +28 -0
- package/lib/motion/mgstyles/catalog.d/06-collage.json +28 -0
- package/lib/motion/mgstyles/catalog.d/07-liquid.json +28 -0
- package/lib/motion/mgstyles/catalog.d/08-morph.json +28 -0
- package/lib/motion/mgstyles/catalog.d/09-bauhaus.json +28 -0
- package/lib/motion/mgstyles/catalog.d/10-synthwave.json +28 -0
- package/lib/motion/mgstyles/catalog.d/12-aurora-glass.json +28 -0
- package/lib/motion/mgstyles/catalog.d/18-hanazi.json +28 -0
- package/lib/motion/mgstyles/catalog.d/19-paperclip.json +28 -0
- package/lib/motion/mgstyles/catalog.d/20-pixel.json +28 -0
- package/lib/motion/mgstyles/catalog.d/22-hud.json +28 -0
- package/lib/motion/mgstyles/catalog.json +1356 -0
- package/lib/motion/mgstyles/upstream/README.md +147 -0
- package/lib/motion/mgstyles/upstream/README.zh-CN.md +147 -0
- package/lib/motion/mgstyles/upstream/demos/01-flat-vector/cues.json +1 -0
- package/lib/motion/mgstyles/upstream/demos/01-flat-vector/index.html +712 -0
- package/lib/motion/mgstyles/upstream/demos/02-line-art/cues.json +331 -0
- package/lib/motion/mgstyles/upstream/demos/02-line-art/film.js +676 -0
- package/lib/motion/mgstyles/upstream/demos/02-line-art/index.html +18 -0
- package/lib/motion/mgstyles/upstream/demos/02-line-art/tools/dump_timing.mjs +26 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/cues.json +109 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/index.html +25 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/anim.js +82 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/main.js +219 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/night5.js +180 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/render.js +202 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/title.js +167 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/title5.js +294 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/ui.js +141 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/world.js +1399 -0
- package/lib/motion/mgstyles/upstream/demos/05-cel-boil/cues.json +157 -0
- package/lib/motion/mgstyles/upstream/demos/05-cel-boil/index.html +34 -0
- package/lib/motion/mgstyles/upstream/demos/05-cel-boil/js/engine.js +326 -0
- package/lib/motion/mgstyles/upstream/demos/05-cel-boil/js/scene.js +2036 -0
- package/lib/motion/mgstyles/upstream/demos/06-collage/assets/gen/layout.json +157 -0
- package/lib/motion/mgstyles/upstream/demos/06-collage/cues.json +80 -0
- package/lib/motion/mgstyles/upstream/demos/06-collage/index.html +889 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/cues.json +100 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/index.html +108 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/engine.js +153 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/links.js +64 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/lookdev.js +46 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/scene.js +963 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/shaders.js +647 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/textsdf.js +114 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/type.js +50 -0
- package/lib/motion/mgstyles/upstream/demos/08-morph/cues.json +131 -0
- package/lib/motion/mgstyles/upstream/demos/08-morph/glyphs.json +1 -0
- package/lib/motion/mgstyles/upstream/demos/08-morph/index.html +901 -0
- package/lib/motion/mgstyles/upstream/demos/09-bauhaus/cues.json +270 -0
- package/lib/motion/mgstyles/upstream/demos/09-bauhaus/film.js +697 -0
- package/lib/motion/mgstyles/upstream/demos/09-bauhaus/index.html +12 -0
- package/lib/motion/mgstyles/upstream/demos/10-synthwave/cues.json +44 -0
- package/lib/motion/mgstyles/upstream/demos/10-synthwave/index.html +1028 -0
- package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/cues.json +67 -0
- package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/index.html +16 -0
- package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/main.js +967 -0
- package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/shaders.js +468 -0
- package/lib/motion/mgstyles/upstream/demos/18-hanazi/cues.json +117 -0
- package/lib/motion/mgstyles/upstream/demos/18-hanazi/index.html +541 -0
- package/lib/motion/mgstyles/upstream/demos/19-paperclip/assets/countries-110m.json +1 -0
- package/lib/motion/mgstyles/upstream/demos/19-paperclip/assets/stickers/meta.json +34 -0
- package/lib/motion/mgstyles/upstream/demos/19-paperclip/cues.json +285 -0
- package/lib/motion/mgstyles/upstream/demos/19-paperclip/index.html +599 -0
- package/lib/motion/mgstyles/upstream/demos/20-pixel/cues.json +42 -0
- package/lib/motion/mgstyles/upstream/demos/20-pixel/film.js +914 -0
- package/lib/motion/mgstyles/upstream/demos/20-pixel/index.html +18 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/cues.json +108 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/index.html +125 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/js/holo.js +275 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/js/hud.js +855 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/js/post.js +150 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/js/util.js +65 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/js/v5.js +142 -0
- package/lib/motion/mgstyles/upstream/harness/render.mjs +319 -0
- package/lib/motion/mgstyles/upstream/manifest.json +269 -0
- package/lib/motion/mgstyles/upstream/prompts/01-flat-vector.md +98 -0
- package/lib/motion/mgstyles/upstream/prompts/02-line-art.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/03-isometric.md +97 -0
- package/lib/motion/mgstyles/upstream/prompts/05-cel-boil.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/06-collage.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/07-liquid.md +98 -0
- package/lib/motion/mgstyles/upstream/prompts/08-morph.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/09-bauhaus.md +96 -0
- package/lib/motion/mgstyles/upstream/prompts/10-synthwave.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/12-aurora-glass.md +96 -0
- package/lib/motion/mgstyles/upstream/prompts/18-hanazi.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/19-paperclip.md +96 -0
- package/lib/motion/mgstyles/upstream/prompts/20-pixel.md +96 -0
- package/lib/motion/mgstyles/upstream/prompts/22-hud.md +95 -0
- package/lib/motion/mgstyles/upstream/rubric.md +51 -0
- package/lib/motion/mgstyles/upstream/template.md +36 -0
- package/lib/motion/pixelqa.js +198 -0
- package/lib/motion/preview.js +95 -0
- package/lib/motion/project.js +395 -0
- package/lib/motion/qa.js +290 -0
- package/lib/motion/reference-media.jsx +10 -0
- package/lib/motion/render-settings.js +112 -0
- package/lib/motion/render.js +159 -0
- package/lib/motion/scene-kit.js +183 -0
- package/lib/motion/sfx.js +232 -0
- package/lib/motion/shotcraft/ClipCard.tsx +204 -0
- package/lib/motion/shotcraft/catalog.json +2461 -0
- package/lib/motion/shotcraft/upstream/SKILL.md +286 -0
- package/lib/motion/shotcraft/upstream/assets/lib/Caption.tsx +54 -0
- package/lib/motion/shotcraft/upstream/assets/lib/ClipCard.tsx +200 -0
- package/lib/motion/shotcraft/upstream/assets/lib/DigitRoll.tsx +45 -0
- package/lib/motion/shotcraft/upstream/assets/lib/FlashCut.tsx +20 -0
- package/lib/motion/shotcraft/upstream/assets/lib/FlatPanel.tsx +65 -0
- package/lib/motion/shotcraft/upstream/assets/lib/PageCam.tsx +151 -0
- package/lib/motion/shotcraft/upstream/assets/lib/VerticalTicker.tsx +129 -0
- package/lib/motion/shotcraft/upstream/assets/lib/helpers/camera.tsx +75 -0
- package/lib/motion/shotcraft/upstream/assets/lib/helpers/motion.ts +40 -0
- package/lib/motion/shotcraft/upstream/assets/lib/helpers/rand.ts +11 -0
- package/lib/motion/shotcraft/upstream/assets/lib/helpers/shake.ts +10 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/__pycache__/sfx-source.cpython-313.pyc +0 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/build-readme-cover.mjs +49 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/capture-template.mjs +210 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/sfx-source.py +423 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/smoke-render-demos.py +170 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/test_sfx_source.py +157 -0
- package/lib/motion/shotcraft/upstream/demos/README.md +120 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/Brand.tsx +112 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/Fixtures.tsx +647 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/Look.tsx +316 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/Motion.tsx +82 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/PageCam2D.tsx +115 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/Polish.tsx +243 -0
- package/lib/motion/shotcraft/upstream/demos/_textures/live-layout.json +389 -0
- package/lib/motion/shotcraft/upstream/demos/camera/basic-3d-scene/Basic3DScene.tsx +250 -0
- package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashImpactReal.tsx +305 -0
- package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashZoomReal.tsx +246 -0
- package/lib/motion/shotcraft/upstream/demos/camera/cursor-flyover/CursorFlyover.tsx +390 -0
- package/lib/motion/shotcraft/upstream/demos/camera/depth-layer-moves/MultiplaneReal.tsx +138 -0
- package/lib/motion/shotcraft/upstream/demos/camera/graze-face-tour/GrazeFaceTour.tsx +542 -0
- package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/OverheadTabletopDrop.tsx +315 -0
- package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/TiltReveal.tsx +299 -0
- package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/DroneDiveLanding.tsx +173 -0
- package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/ExplodedView.tsx +348 -0
- package/lib/motion/shotcraft/upstream/demos/camera/steep-tilt-glide/SteepTiltGlide.tsx +366 -0
- package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/BulletTimeFreezeOrbit.tsx +279 -0
- package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/PullBackIsolation.tsx +184 -0
- package/lib/motion/shotcraft/upstream/demos/camera/terminal-3d/Terminal3D.tsx +390 -0
- package/lib/motion/shotcraft/upstream/demos/data/before-after-slider-scrub/BeforeAfterSliderScrub.tsx +155 -0
- package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/AxisRescaleShockV2.tsx +303 -0
- package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/OscilloscopeStreamV2.tsx +290 -0
- package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/UnitDotSwarmRegroupV2.tsx +278 -0
- package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/NeedleSweepSelftest.tsx +307 -0
- package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/TapeScrollFixedPointer.tsx +215 -0
- package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/BrakeReticleLock.tsx +299 -0
- package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/ChangelogScrollBrake.tsx +232 -0
- package/lib/motion/shotcraft/upstream/demos/data/timeline-travel/TimelineTravel.tsx +290 -0
- package/lib/motion/shotcraft/upstream/demos/effects/assemble-then-type-flyin/AssembleThenTypeFlyin.tsx +273 -0
- package/lib/motion/shotcraft/upstream/demos/effects/aurora-bloom-bg-flip/AuroraBloomBgFlip.tsx +167 -0
- package/lib/motion/shotcraft/upstream/demos/effects/brand-frame-snap/BrandFrameSnap.tsx +536 -0
- package/lib/motion/shotcraft/upstream/demos/effects/fui-hud-moves/ReticleLockOn.tsx +164 -0
- package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/FlylineArc.tsx +175 -0
- package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx +197 -0
- package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/OrbFlylineRelay.tsx +241 -0
- package/lib/motion/shotcraft/upstream/demos/effects/icon-performance-moves/AttentionBounce.tsx +279 -0
- package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/AnimeImpact.tsx +230 -0
- package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/HitCounter.tsx +345 -0
- package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SheenSweepRetry.tsx +275 -0
- package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SpotlightSweepReveal.tsx +229 -0
- package/lib/motion/shotcraft/upstream/demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.tsx +312 -0
- package/lib/motion/shotcraft/upstream/demos/effects/riso-print-hits/RisoMisregistrationHit.tsx +222 -0
- package/lib/motion/shotcraft/upstream/demos/effects/scan-bracket-sweep/ScanBracketSweep.tsx +295 -0
- package/lib/motion/shotcraft/upstream/demos/effects/scanline-annotate-focus/ScanlineAnnotateFocus.tsx +329 -0
- package/lib/motion/shotcraft/upstream/demos/effects/scanline-assemble-flyin/ScanlineAssembleFlyin.tsx +315 -0
- package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ImpactBurstKit.tsx +264 -0
- package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx +260 -0
- package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ScoreSlam.tsx +263 -0
- package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/CornerSpotlightReveal.tsx +123 -0
- package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/GlowWakeSleepPanel.tsx +187 -0
- package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/SlideSpotlightPan.tsx +175 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/autolayout-gap-dial/AutolayoutGapDial.tsx +305 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/DiagramCascadeBuild.tsx +350 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/PanelToCanvasMaterialize.tsx +352 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/chip-grid-single-select-blackout/ChipGridSingleSelectBlackout.tsx +218 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/collab-cursor-moves/CursorCastEnsemble.tsx +297 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx +157 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/hashtag-to-pill-materialize/HashtagToPillMaterialize.tsx +218 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/picker-carousel-feature-cycle/PickerCarouselFeatureCycle.tsx +202 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/segmented-thumb-hero/SegmentedThumbHero.tsx +265 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/PaletteThemeRipple.tsx +457 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/ThemeSweepToggle.tsx +446 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/type-and-filter/TypeAndFilter.tsx +249 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/voice-waveform-live/VoiceWaveformLive.tsx +278 -0
- package/lib/motion/shotcraft/upstream/demos/opening/crane-rise-reveal/CraneRiseReveal.tsx +332 -0
- package/lib/motion/shotcraft/upstream/demos/opening/dataviz-landscape-open/DatavizLandscapeOpen.tsx +358 -0
- package/lib/motion/shotcraft/upstream/demos/opening/fracture/Fracture.tsx +201 -0
- package/lib/motion/shotcraft/upstream/demos/opening/icon-field-colorize/IconFieldColorize.tsx +200 -0
- package/lib/motion/shotcraft/upstream/demos/opening/letterspace-materialize/LetterspaceMaterialize.tsx +208 -0
- package/lib/motion/shotcraft/upstream/demos/opening/magician-card-flourish/MagicianCardFlourish.tsx +341 -0
- package/lib/motion/shotcraft/upstream/demos/opening/orbit-ring-title-open/OrbitRingTitleOpen.tsx +389 -0
- package/lib/motion/shotcraft/upstream/demos/opening/spotlight-hero-card/SpotlightHeroCard.tsx +293 -0
- package/lib/motion/shotcraft/upstream/demos/opening/stroke-segment-build/StrokeSegmentBuild.tsx +183 -0
- package/lib/motion/shotcraft/upstream/demos/opening/text-as-mask/TextAsMask.tsx +245 -0
- package/lib/motion/shotcraft/upstream/demos/outro/grain-dissolve/GrainDissolve.tsx +222 -0
- package/lib/motion/shotcraft/upstream/demos/outro/logo-shrink-wordmark-lockup/LogoShrinkWordmarkLockup.tsx +223 -0
- package/lib/motion/shotcraft/upstream/demos/outro/neon-triple-marquee/NeonTripleMarquee.tsx +217 -0
- package/lib/motion/shotcraft/upstream/demos/outro/outro-group-photo-launch/OutroGroupPhotoLaunch.tsx +343 -0
- package/lib/motion/shotcraft/upstream/demos/outro/ui-strip-away-outro/UiStripAwayOutro.tsx +357 -0
- package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/IconFlipBloomLogo.tsx +187 -0
- package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/InputMorphsIntoLogo.tsx +333 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/beat-cut-moves/PaparazziFlash.tsx +274 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.tsx +270 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/panel-grid-moves/ComicPanelSplit.tsx +430 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/quad-split-parallel-scenes/QuadSplitParallelScenes.tsx +252 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx +106 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/sakuga-timing-shift/SakugaTimingShift.tsx +288 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/speed-ramp-freeze/FreezeAnnotateReal.tsx +362 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/trailer-grammar-moves/TrailerBumper.tsx +168 -0
- package/lib/motion/shotcraft/upstream/demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.tsx +310 -0
- package/lib/motion/shotcraft/upstream/demos/transition/card-flip-reveal/CardFlipReveal.tsx +261 -0
- package/lib/motion/shotcraft/upstream/demos/transition/card-flock-tumble/CardFlockTumble.tsx +459 -0
- package/lib/motion/shotcraft/upstream/demos/transition/circle-match-iris/CircleMatchIris.tsx +294 -0
- package/lib/motion/shotcraft/upstream/demos/transition/gradient-transition/GradientTransition.tsx +161 -0
- package/lib/motion/shotcraft/upstream/demos/transition/line-carry-transition/LineCarryTransition.tsx +248 -0
- package/lib/motion/shotcraft/upstream/demos/transition/mosaic-reframe/MosaicReframe.tsx +359 -0
- package/lib/motion/shotcraft/upstream/demos/transition/page-turn-transitions/BarnDoorSplit.tsx +227 -0
- package/lib/motion/shotcraft/upstream/demos/transition/print-texture-transitions/InkBleedReveal.tsx +162 -0
- package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/PortalWipeV2.tsx +248 -0
- package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/WhipBrakeReal.tsx +186 -0
- package/lib/motion/shotcraft/upstream/demos/transition/tear-streak-transitions/GlitchDisplace.tsx +355 -0
- package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/InvisibleCut.tsx +272 -0
- package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/LightLeakBurn.tsx +262 -0
- package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/VersusSlam.tsx +217 -0
- package/lib/motion/shotcraft/upstream/demos/transition/transition-travel/LetterformZoom.tsx +285 -0
- package/lib/motion/shotcraft/upstream/demos/transition/white-flash-logo-simplify-cut/WhiteFlashLogoSimplifyCut.tsx +205 -0
- package/lib/motion/shotcraft/upstream/demos/transition/wipe-transitions/BlindsSlice.tsx +178 -0
- package/lib/motion/shotcraft/upstream/demos/typography/blur-slide/BlurSlide.tsx +147 -0
- package/lib/motion/shotcraft/upstream/demos/typography/brace-expand/BraceExpand.tsx +143 -0
- package/lib/motion/shotcraft/upstream/demos/typography/brand-ink-open/BrandInkOpen.tsx +134 -0
- package/lib/motion/shotcraft/upstream/demos/typography/countdown-arc-scatter/CountdownArcScatter.tsx +217 -0
- package/lib/motion/shotcraft/upstream/demos/typography/flying-words/FlyingWords.tsx +198 -0
- package/lib/motion/shotcraft/upstream/demos/typography/glitch-cycle/GlitchCycle.tsx +221 -0
- package/lib/motion/shotcraft/upstream/demos/typography/gradient-word-sweep/GradientWordSweep.tsx +312 -0
- package/lib/motion/shotcraft/upstream/demos/typography/lead-word-zoom-assemble/LeadWordZoomAssemble.tsx +261 -0
- package/lib/motion/shotcraft/upstream/demos/typography/marker-underline-title/MarkerUnderlineTitle.tsx +121 -0
- package/lib/motion/shotcraft/upstream/demos/typography/outline-word-fill/OutlineWordFill.tsx +153 -0
- package/lib/motion/shotcraft/upstream/demos/typography/paper-title-card/PaperTitleCard.tsx +179 -0
- package/lib/motion/shotcraft/upstream/demos/typography/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.tsx +140 -0
- package/lib/motion/shotcraft/upstream/demos/typography/pill-slot-cycle/PillSlotCycle.tsx +309 -0
- package/lib/motion/shotcraft/upstream/demos/typography/text-column-converge/TextColumnConverge.tsx +191 -0
- package/lib/motion/shotcraft/upstream/demos/typography/title-demote-to-label/TitleDemoteToLabel.tsx +292 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/LetterformDriftAssembly.tsx +135 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/SplitTextStagger.tsx +124 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/TextOnPath.tsx +255 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-entrance-moves/LetterDropPhysics.tsx +173 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/FontWeightPump.tsx +225 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/KaraokeFillSync.tsx +141 -0
- package/lib/motion/shotcraft/upstream/demos/typography/typewriter-moves/TypewriterErrorRetype.tsx +137 -0
- package/lib/motion/shotcraft/upstream/demos/typography/vertical-word-roll-blur-cycle/VerticalWordRollBlurCycle.tsx +147 -0
- package/lib/motion/shotcraft/upstream/demos/typography/word-relay-filmstrip/WordRelayFilmstrip.tsx +409 -0
- package/lib/motion/shotcraft/upstream/demos/typography/word-relay-geometry/WordRelayGeometry.tsx +280 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/avatar-bracket-carousel/AvatarBracketCarousel.tsx +271 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/bezier-source-converge-merge/BezierSourceConvergeMerge.tsx +385 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/card-stack/CardStack.tsx +312 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/carousel-3d/Carousel3D.tsx +303 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/clipcard-looping/ClipCardLooping.tsx +77 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/cloner-depth-echo/ClonerDepthEcho.tsx +311 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/deck-deal-flyin/DeckDealFlyin.tsx +307 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/doc-park-left-pill-deal/DocParkLeftPillDeal.tsx +349 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/document-typewriter-reveal/DocumentTypewriterReveal.tsx +255 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/draw-svg-trace/DrawSvgTrace.tsx +244 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/element-body-moves/ContactShadowLift.tsx +217 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx +652 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-reveal/ListReveal.tsx +153 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-stack-press/ListStackPress.tsx +238 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-forerun/NeonFrameForerun.tsx +324 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-orbit-drop/NeonFrameForerunOrbit.tsx +354 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/PageWaterfallWall.tsx +101 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/VerticalTicker.tsx +129 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/product-card-progressive-assemble/ProductCardProgressiveAssemble.tsx +297 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/radial-wave/RadialWave.tsx +57 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/research-card-stack-scroll/ResearchCardStackScroll.tsx +155 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/row-embed/RowEmbed.tsx +175 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/value-stagger-gradient/ValueStaggerGradient.tsx +153 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/BentoLightUp.tsx +146 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.tsx +226 -0
- package/lib/motion/shotcraft/upstream/gallery/api/library.json +1 -0
- package/lib/motion/shotcraft/upstream/manifest.json +2715 -0
- package/lib/motion/shotcraft/upstream/references/aesthetic-rules.md +193 -0
- package/lib/motion/shotcraft/upstream/references/final-review.md +121 -0
- package/lib/motion/shotcraft/upstream/references/guided-free-creation.md +199 -0
- package/lib/motion/shotcraft/upstream/references/jianying-export.md +183 -0
- package/lib/motion/shotcraft/upstream/references/music-beat-sync.md +257 -0
- package/lib/motion/shotcraft/upstream/references/pipeline.md +382 -0
- package/lib/motion/shotcraft/upstream/references/publish-upload-post.md +111 -0
- package/lib/motion/shotcraft/upstream/references/sequences/promo-energy-arc.md +50 -0
- package/lib/motion/shotcraft/upstream/references/sfx-sourcing.md +95 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/basic-3d-scene.md +41 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/crash-zoom-punch.md +52 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/cursor-flyover.md +39 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/depth-layer-moves.md +39 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/graze-face-tour.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/overhead-camera-moves.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/space-camera-moves.md +46 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/steep-tilt-glide.md +52 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/tension-camera-moves.md +43 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/terminal-3d.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/data/before-after-slider-scrub.md +48 -0
- package/lib/motion/shotcraft/upstream/references/shots/data/chart-live-moves.md +52 -0
- package/lib/motion/shotcraft/upstream/references/shots/data/gauge-readout-moves.md +45 -0
- package/lib/motion/shotcraft/upstream/references/shots/data/scroll-brake-moves.md +46 -0
- package/lib/motion/shotcraft/upstream/references/shots/data/timeline-travel.md +44 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/assemble-then-type-flyin.md +63 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/aurora-bloom-bg-flip.md +54 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/brand-frame-snap.md +58 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/fui-hud-moves.md +35 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/glow-flyline-moves.md +53 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/icon-performance-moves.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/impact-feedback.md +50 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/light-play-moves.md +46 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/radial-ripple-phone-chips.md +56 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/riso-print-hits.md +49 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/scan-bracket-sweep.md +50 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-annotate-focus.md +53 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-assemble-flyin.md +53 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/slam-entrance-moves.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/spotlight-sweep-moves.md +52 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/autolayout-gap-dial.md +60 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/canvas-materialize-moves.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/chip-grid-single-select-blackout.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/collab-cursor-moves.md +41 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/glass-pill-dictation-typing.md +53 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/hashtag-to-pill-materialize.md +60 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/picker-carousel-feature-cycle.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/segmented-thumb-hero.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/theme-switch-moves.md +47 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/type-and-filter.md +37 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/voice-waveform-live.md +54 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/brand-ink-open.md +36 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/crane-rise-reveal.md +56 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/dataviz-landscape-open.md +56 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/fracture.md +40 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/icon-field-colorize.md +60 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/letterspace-materialize.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/magician-card-flourish.md +73 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/orbit-ring-title-open.md +87 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/spotlight-hero-card.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/stroke-segment-build.md +50 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/text-as-mask.md +59 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/grain-dissolve.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/logo-shrink-wordmark-lockup.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/neon-triple-marquee.md +59 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/outro-group-photo-launch.md +39 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/ui-strip-away-outro.md +61 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/ui-to-brand-morph.md +52 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-cut-moves.md +50 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-step-list-theme-cycle.md +66 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/panel-grid-moves.md +43 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/quad-split-parallel-scenes.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/rhythm-interrupt-moves.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/sakuga-timing-shift.md +56 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/speed-ramp-freeze.md +44 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/trailer-grammar-moves.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/bottom-push-stack-wipe.md +63 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/card-flip-reveal.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/card-flock-tumble.md +63 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/circle-match-iris.md +58 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/gradient-transition.md +45 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/line-carry-transition.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/mosaic-reframe.md +49 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/page-turn-transitions.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/print-texture-transitions.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/shot-transitions.md +45 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/tear-streak-transitions.md +48 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/transition-hidden-cut.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/transition-travel.md +43 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/white-flash-logo-simplify-cut.md +41 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/wipe-transitions.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/blur-slide.md +59 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/brace-expand.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/countdown-arc-scatter.md +65 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/document-typewriter-reveal.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/flying-words.md +64 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/glitch-cycle.md +64 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/gradient-word-sweep.md +54 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/lead-word-zoom-assemble.md +91 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/marker-underline-title.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/outline-word-fill.md +66 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/paper-title-card.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/pill-chip-slot-cycle-handled.md +66 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/pill-slot-cycle.md +58 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/text-column-converge.md +61 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/title-demote-to-label.md +64 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/type-assembly-moves.md +45 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/type-entrance-moves.md +41 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/type-rhythm-sync.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/typewriter-moves.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/vertical-word-roll-blur-cycle.md +63 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-filmstrip.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-geometry.md +75 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/avatar-bracket-carousel.md +59 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/bezier-source-converge-merge.md +67 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/card-stack.md +60 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/carousel-3d.md +66 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/cloner-depth-echo.md +40 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/deck-deal-flyin.md +45 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/doc-park-left-pill-deal.md +62 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/draw-svg-trace.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/element-body-moves.md +37 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/integration-hub-map.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-reveal.md +50 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-stack-press.md +43 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-forerun.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-orbit-drop.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/page-waterfall-wall.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/product-card-progressive-assemble.md +56 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/radial-wave.md +49 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/research-card-stack-scroll.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/row-embed.md +36 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/value-stagger-gradient.md +64 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/wall-reveal-moves.md +47 -0
- package/lib/motion/shotcraft/upstream/references/sound-design.md +170 -0
- package/lib/motion/shotcraft/upstream/references/workbench.md +157 -0
- package/lib/motion/shotcraft/upstream/template/TEMPLATE.md +103 -0
- package/lib/motion/shotcraft/upstream/template/THEMES.md +44 -0
- package/lib/motion/shotcraft/upstream/template/package.json +20 -0
- package/lib/motion/shotcraft/upstream/template/remotion.config.ts +6 -0
- package/lib/motion/shotcraft/upstream/template/src/Root.tsx +17 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/Caption.tsx +65 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/DigitRoll.tsx +48 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/FlashCut.tsx +24 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/Main.tsx +192 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/PaperTitleCard.tsx +111 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/PageCam.tsx +150 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneDetail.tsx +158 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneFlyIn.tsx +468 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOpen.tsx +552 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOutroLive.tsx +411 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/ScenePapers.tsx +200 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneWbr.tsx +324 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live-layout.json +389 -0
- package/lib/motion/shotcraft/upstream/template/src/index.ts +4 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/ThemedFilm.tsx +7 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/manifest.tsx +68 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.json +1 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.ts +24 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/palettes.json +74 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/visual-theme.tsx +91 -0
- package/lib/motion/shotcraft/upstream/template/src/workbench.ts +213 -0
- package/lib/motion/shotcraft/upstream/template/tsconfig.json +15 -0
- package/lib/motion/spec.js +113 -0
- package/lib/motion/style-engine.js +153 -0
- package/lib/motion/style-frames.js +268 -0
- package/lib/motion/sync.js +214 -0
- package/lib/motion/theme.js +121 -0
- package/lib/motion/voice.js +417 -0
- package/lib/motion-agent-files.js +82 -0
- package/lib/motion-agent-protocol.js +66 -0
- package/lib/motion-agent-tools.js +196 -0
- package/lib/motion-agent.js +171 -0
- package/lib/motion-capture.js +92 -0
- package/lib/motion-core.js +238 -0
- package/lib/motion-film-review.js +71 -0
- package/lib/motion-library.js +210 -0
- package/lib/motion-planner.js +254 -0
- package/lib/motion-project.js +80 -0
- package/lib/motion-render-worker.js +145 -0
- package/lib/motion-session.js +32 -0
- package/lib/motion-source.js +49 -0
- package/lib/motion-tasks.js +452 -0
- package/lib/motion-vision.js +43 -0
- package/lib/mxfile.js +27 -7
- package/lib/routing/node-runtime.js +14 -0
- package/lib/routing/router.js +211 -0
- package/lib/routing/vendor/README.md +96 -0
- package/lib/routing/vendor/libavoid-routing.js +753 -0
- package/lib/routing/vendor/libavoid.min.js +59 -0
- package/lib/shotcard-core.js +39 -0
- package/lib/shotcard-reference.js +56 -0
- package/lib/style-kernel.js +37 -3
- package/lib/task-patches.js +26 -2
- package/lib/video-export.js +17 -2
- package/package.json +29 -4
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html><head><meta charset="utf-8"><title>popwise — One Dot</title>
|
|
3
|
+
<link rel="stylesheet" href="/assets/fonts/fonts.css">
|
|
4
|
+
<style>
|
|
5
|
+
html,body{margin:0;padding:0;background:#2B2BFF;overflow:hidden;width:1920px;height:1080px}
|
|
6
|
+
svg{display:block}
|
|
7
|
+
</style></head>
|
|
8
|
+
<body>
|
|
9
|
+
<svg id="S" xmlns="http://www.w3.org/2000/svg" width="1920" height="1080" viewBox="0 0 1920 1080"></svg>
|
|
10
|
+
<script>
|
|
11
|
+
window.DEMO = { width: 1920, height: 1080, fps: 30, duration: 10, motionBlur: { samples: 1, shutter: 0.5 } };
|
|
12
|
+
</script>
|
|
13
|
+
<script type="module">
|
|
14
|
+
// ============================================================ utilities
|
|
15
|
+
const NS = 'http://www.w3.org/2000/svg';
|
|
16
|
+
const S = document.getElementById('S');
|
|
17
|
+
// reel v5 (?v5=1): title 「扁平矢量」 replaces the wordmark, window.linkAt(t), ?nolink=dot,cap,rays,coin. Default path untouched.
|
|
18
|
+
const QS = new URLSearchParams(location.search), V5 = QS.get('v5') === '1';
|
|
19
|
+
const NOLINK = new Set(V5 ? (QS.get('nolink') || '').split(',').map(s => s.trim()).filter(Boolean) : []);
|
|
20
|
+
const C = { U:'#2B2BFF', UD:'#1F1FCC', UL:'#4D4DFF', UT:'#3B3BFF', UP:'#A3A3FF', CO:'#FF5A4E', COD:'#E0433A', COL:'#FF8C82',
|
|
21
|
+
SU:'#FFC62B', SUD:'#F0A91A', SUE:'#D98F00', MI:'#2EE6A8', MID:'#1FC28C', CR:'#FFF6E9', CRD:'#F1E3CC', CRS:'#E6D3B8',
|
|
22
|
+
INK:'#151433', INKW:'#2A2962', SK:'#FFB48C', SKD:'#F2976E' };
|
|
23
|
+
function el(tag, attrs, parent) { const e = document.createElementNS(NS, tag); if (attrs) for (const k in attrs) e.setAttribute(k, attrs[k]); (parent || S).appendChild(e); return e; }
|
|
24
|
+
const G = (p, a) => el('g', a, p);
|
|
25
|
+
const f3 = v => Math.round(v * 1000) / 1000;
|
|
26
|
+
function tf(e, x, y, sx = 1, sy = sx, r = 0) {
|
|
27
|
+
if (Math.abs(sx) < 1e-4 || Math.abs(sy) < 1e-4) { sx = 0; sy = 0; }
|
|
28
|
+
e.setAttribute('transform', `translate(${f3(x)} ${f3(y)}) rotate(${f3(r)}) scale(${sx.toFixed(5)} ${sy.toFixed(5)})`);
|
|
29
|
+
}
|
|
30
|
+
const vis = (e, v) => { e.style.display = v ? '' : 'none'; };
|
|
31
|
+
function rng(seed) { let a = seed >>> 0; return () => { a |= 0; a = a + 0x6D2B79F5 | 0; let t = Math.imul(a ^ a >>> 15, 1 | a); t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; return ((t ^ t >>> 14) >>> 0) / 4294967296; }; }
|
|
32
|
+
const clamp = (x, a = 0, b = 1) => x < a ? a : x > b ? b : x;
|
|
33
|
+
const lerp = (a, b, u) => a + (b - a) * u;
|
|
34
|
+
const inv = (a, b, x) => clamp((x - a) / (b - a));
|
|
35
|
+
const E = {
|
|
36
|
+
inQ: u => u * u, outQ: u => 1 - (1 - u) ** 2, ioQ: u => u < .5 ? 2 * u * u : 1 - (-2 * u + 2) ** 2 / 2,
|
|
37
|
+
inC: u => u ** 3, outC: u => 1 - (1 - u) ** 3, ioC: u => u < .5 ? 4 * u ** 3 : 1 - (-2 * u + 2) ** 3 / 2,
|
|
38
|
+
outQt: u => 1 - (1 - u) ** 4, ioQt: u => u < .5 ? 8 * u ** 4 : 1 - (-2 * u + 2) ** 4 / 2,
|
|
39
|
+
outX: u => u >= 1 ? 1 : 1 - 2 ** (-10 * u),
|
|
40
|
+
outB: (u, s = 1.70158) => 1 + (s + 1) * (u - 1) ** 3 + s * (u - 1) ** 2,
|
|
41
|
+
inB: (u, s = 1.70158) => (s + 1) * u ** 3 - s * u ** 2,
|
|
42
|
+
};
|
|
43
|
+
function bezier(x1, y1, x2, y2) {
|
|
44
|
+
const cx = 3 * x1, bx = 3 * (x2 - x1) - cx, ax = 1 - cx - bx, cy = 3 * y1, by = 3 * (y2 - y1) - cy, ay = 1 - cy - by;
|
|
45
|
+
const sx = t => ((ax * t + bx) * t + cx) * t, sy = t => ((ay * t + by) * t + cy) * t, dx = t => (3 * ax * t + 2 * bx) * t + cx;
|
|
46
|
+
return u => { if (u <= 0) return 0; if (u >= 1) return 1; let t = u; for (let i = 0; i < 10; i++) { const d = dx(t); if (Math.abs(d) < 1e-6) break; t = clamp(t - (sx(t) - u) / d); } return sy(t); };
|
|
47
|
+
}
|
|
48
|
+
E.pop = bezier(0.34, 1.56, 0.64, 1); // the classic MG overshoot curve
|
|
49
|
+
// closed-form damped spring 0 -> 1 (value / velocity), and a damped impulse
|
|
50
|
+
function spring(t, f = 2, z = .4) { if (t <= 0) return 0; const w = 2 * Math.PI * f, a = z * w, b = w * Math.sqrt(1 - z * z); return 1 - Math.exp(-a * t) * (Math.cos(b * t) + (a / b) * Math.sin(b * t)); }
|
|
51
|
+
function springV(t, f = 2, z = .4) { if (t <= 0) return 0; const w = 2 * Math.PI * f, a = z * w, b = w * Math.sqrt(1 - z * z); return Math.exp(-a * t) * (w * w / b) * Math.sin(b * t); }
|
|
52
|
+
function kick(t, t0, f = 3, z = .35) { if (t <= t0) return 0; const w = 2 * Math.PI * f, tt = t - t0; return Math.exp(-z * w * tt) * Math.sin(w * Math.sqrt(1 - z * z) * tt); }
|
|
53
|
+
// spring pop with velocity-driven squash & stretch; residual pinned to exactly 1 after `pin` s (no sub-pixel drift under zoom)
|
|
54
|
+
function pop(t, t0, f = 2.4, z = .42, amt = .12, pin = .9) {
|
|
55
|
+
const tt = t - t0; let s = spring(tt, f, z); let k = clamp(springV(tt, f, z) / (2 * Math.PI * f) * amt * 2, -.3, .3);
|
|
56
|
+
const w = E.ioQ(inv(pin * .7, pin, tt)); s = lerp(s, 1, w); k = lerp(k, 0, w); return [s, k];
|
|
57
|
+
}
|
|
58
|
+
const hump = u => 4 * u * (1 - u);
|
|
59
|
+
|
|
60
|
+
// ============================================================ cues + fonts
|
|
61
|
+
const cues = await (await fetch('/demos/01-flat-vector/cues.json')).json();
|
|
62
|
+
const FR = cues.frames; const F = n => n / 30; const T = k => FR[k] / 30;
|
|
63
|
+
await Promise.all([
|
|
64
|
+
document.fonts.load("700 200px 'Fredoka'", 'popwıse'), document.fonts.load("600 40px 'Fredoka'", 'EVERY DAY'),
|
|
65
|
+
document.fonts.load("700 120px 'Fredoka'", '¥'), document.fonts.load("700 60px 'Resource Han Rounded'", '每天,聪明一点点'),
|
|
66
|
+
...(V5 ? [document.fonts.load("900 260px 'Resource Han Rounded'", '扁平矢量'), document.fonts.load("600 46px 'Fredoka'", 'FLAT VECTOR')] : []),
|
|
67
|
+
]);
|
|
68
|
+
await document.fonts.ready;
|
|
69
|
+
const cv = document.createElement('canvas').getContext('2d');
|
|
70
|
+
const meas = (font, s) => { cv.font = font; return cv.measureText(s); };
|
|
71
|
+
const defs = el('defs', {}, S);
|
|
72
|
+
|
|
73
|
+
// ============================================================ WORLD (city) — camera = one transform
|
|
74
|
+
const GY = 820, R0 = 74, SCC = 1.12, CYC = 454, SUN = { x: 1300, y: 230, r: 124 };
|
|
75
|
+
const world = G(S);
|
|
76
|
+
el('rect', { x: -4000, y: -4000, width: 10000, height: 10000, fill: C.U }, world);
|
|
77
|
+
// opening sky shapes: tone-on-tone brand shapes printed by impact 1 (bookend with the end frame), popped out as the skyline rises
|
|
78
|
+
const SKY = [
|
|
79
|
+
{ x: 430, y: 420, mk: g => el('circle', { cx: 0, cy: 0, r: 96, fill: 'none', stroke: C.UT, 'stroke-width': 30 }, g), t0: F(15), out: F(36), rs: 6 },
|
|
80
|
+
{ x: 1500, y: 470, mk: g => el('path', { d: 'M-34 0 L34 0 M0 -34 L0 34', stroke: C.UT, 'stroke-width': 20, 'stroke-linecap': 'round' }, g), t0: F(17), out: F(38), rs: 14 },
|
|
81
|
+
{ x: 610, y: 700, mk: g => el('rect', { x: -110, y: -36, width: 220, height: 72, rx: 36, fill: C.UT }, g), t0: F(24), out: F(40), rs: -4, r0: -24 },
|
|
82
|
+
{ x: 1330, y: 690, mk: g => el('path', { d: 'M-80 0 A80 80 0 0 1 80 0 Z', fill: C.UT }, g), t0: F(25), out: F(42), rs: 5, r0: 18 },
|
|
83
|
+
].map(d => { const g = G(world); d.mk(g); return { ...d, g }; });
|
|
84
|
+
// far skyline (tone-on-tone), rises centre-out
|
|
85
|
+
const farG = G(world); const far = [];
|
|
86
|
+
{ const r = rng(7); let x = -140;
|
|
87
|
+
while (x < 2060) { const w = 70 + r() * 90, h = 170 + r() * 190; const gg = G(farG);
|
|
88
|
+
el('rect', { x: -w / 2, y: -h, width: w, height: h + 2, fill: C.UL }, gg);
|
|
89
|
+
const cols = Math.max(1, Math.floor((w - 20) / 26)), rows = Math.floor((h - 50) / 40);
|
|
90
|
+
for (let i = 0; i < cols; i++) for (let j = 0; j < rows; j++) if (r() < .33) el('rect', { x: -w / 2 + 14 + i * 26, y: -h + 24 + j * 40, width: 12, height: 16, rx: 3, fill: '#6060FF' }, gg);
|
|
91
|
+
far.push({ g: gg, x: x + w / 2, t0: T('farRise') + Math.abs(x + w / 2 - 960) / 960 * F(12) }); x += w + (r() * 16 - 6); } }
|
|
92
|
+
// sun rays + the hero dot
|
|
93
|
+
const raysG = G(world); const rays = [];
|
|
94
|
+
for (let i = 0; i < 12; i++) rays.push(el('line', { stroke: C.CO, 'stroke-width': 12, 'stroke-linecap': 'round' }, raysG));
|
|
95
|
+
const speedL = [0, 1].map(() => el('line', { stroke: C.CR, 'stroke-width': 8, 'stroke-linecap': 'round' }, world));
|
|
96
|
+
const dotW = el('circle', { cx: 0, cy: 0, r: R0, fill: C.CO }, world);
|
|
97
|
+
// clouds
|
|
98
|
+
function cloud(p) { const gg = G(p); el('rect', { x: -100, y: -28, width: 200, height: 56, rx: 28, fill: C.CR }, gg); el('circle', { cx: -26, cy: -30, r: 44, fill: C.CR }, gg); el('circle', { cx: 42, cy: -20, r: 32, fill: C.CR }, gg); return gg; }
|
|
99
|
+
const CLOUDS = [{ x: 380, y: 210, s: 1.0, from: -320 }, { x: 1600, y: 430, s: .85, from: 2260 }, { x: 860, y: 150, s: .6, from: -260 }].map((c, i) => ({ ...c, g: cloud(world), t0: T('clouds') + i * F(5) }));
|
|
100
|
+
// birds
|
|
101
|
+
const BIRDS = [0, 1, 2].map(i => { const gg = G(world); const wl = G(gg), wr = G(gg);
|
|
102
|
+
el('path', { d: 'M0 0 Q-12 -14 -28 -6', fill: 'none', stroke: C.INK, 'stroke-width': 5, 'stroke-linecap': 'round' }, wl);
|
|
103
|
+
el('path', { d: 'M0 0 Q12 -14 28 -6', fill: 'none', stroke: C.INK, 'stroke-width': 5, 'stroke-linecap': 'round' }, wr);
|
|
104
|
+
return { g: gg, wl, wr, i, s: [1, .8, .9][i] }; });
|
|
105
|
+
// buildings
|
|
106
|
+
const BLD = [
|
|
107
|
+
{ cx: 215, w: 150, h: 250, c: C.MI, roof: 'flat', cols: 2, rows: 3 },
|
|
108
|
+
{ cx: 405, w: 190, h: 400, c: C.CR, roof: 'step', cols: 3, rows: 6, door: 1 },
|
|
109
|
+
{ cx: 600, w: 140, h: 310, c: C.CO, roof: 'arch', cols: 2, rows: 4 },
|
|
110
|
+
{ cx: 770, w: 160, h: 470, c: C.SU, roof: 'antenna', cols: 3, rows: 7, door: 1 },
|
|
111
|
+
{ cx: 970, w: 180, h: 540, c: C.INK, roof: 'sign', hero: true },
|
|
112
|
+
{ cx: 1160, w: 150, h: 360, c: C.MI, roof: 'tri', cols: 2, rows: 5 },
|
|
113
|
+
{ cx: 1345, w: 180, h: 280, c: C.SU, roof: 'flat', cols: 3, rows: 4, door: 1 },
|
|
114
|
+
{ cx: 1530, w: 150, h: 430, c: C.CR, roof: 'dome', cols: 2, rows: 6 },
|
|
115
|
+
{ cx: 1715, w: 170, h: 230, c: C.CO, roof: 'flat', cols: 3, rows: 3 },
|
|
116
|
+
];
|
|
117
|
+
const SHADE = { [C.MI]: C.MID, [C.CR]: C.CRD, [C.CO]: C.COD, [C.SU]: C.SUD, [C.INK]: '#0B0A22' };
|
|
118
|
+
const WUNLIT = { [C.MI]: C.MID, [C.CR]: C.UL, [C.CO]: C.COD, [C.SU]: C.SUD, [C.INK]: C.INKW };
|
|
119
|
+
const bldG = G(world); const lits = []; const wr_ = rng(11);
|
|
120
|
+
BLD.forEach((b, bi) => {
|
|
121
|
+
b.g = G(bldG); b.t0 = T('bldStart') + bi * F(FR.bldStagger); const g = b.g, w = b.w, h = b.h, d = SHADE[b.c], sw = Math.round(w * .14);
|
|
122
|
+
if (b.roof === 'flat') el('rect', { x: -w / 2 - 6, y: -h - 10, width: w + 12, height: 14, rx: 5, fill: d }, g);
|
|
123
|
+
if (b.roof === 'step') { el('rect', { x: -w * .3, y: -h - 40, width: w * .6, height: 44, fill: b.c }, g); el('rect', { x: w * .3 - sw * .8, y: -h - 40, width: sw * .8, height: 44, fill: d }, g); el('rect', { x: -w * .3 - 5, y: -h - 48, width: w * .6 + 10, height: 12, rx: 5, fill: d }, g); }
|
|
124
|
+
if (b.roof === 'arch') { el('path', { d: `M${-w / 2} ${-h + 1} A${w / 2} ${w / 2} 0 0 1 ${w / 2} ${-h + 1} Z`, fill: b.c }, g); el('circle', { cx: 0, cy: -h - w * .2, r: w * .13, fill: SU_or(b) }, g); }
|
|
125
|
+
if (b.roof === 'antenna') { el('rect', { x: -w * .22, y: -h - 26, width: w * .44, height: 28, fill: d }, g); el('line', { x1: 0, y1: -h - 26, x2: 0, y2: -h - 92, stroke: C.INK, 'stroke-width': 6, 'stroke-linecap': 'round' }, g); b.tip = el('circle', { cx: 0, cy: -h - 96, r: 9, fill: C.CO }, g); }
|
|
126
|
+
if (b.roof === 'dome') { el('path', { d: `M${-w * .38} ${-h + 1} A${w * .38} ${w * .38} 0 0 1 ${w * .38} ${-h + 1} Z`, fill: d }, g); }
|
|
127
|
+
if (b.roof === 'tri') el('path', { d: `M${-w / 2 - 8} ${-h + 1} L0 ${-h - 74} L${w / 2 + 8} ${-h + 1} Z`, fill: d }, g);
|
|
128
|
+
if (b.roof === 'sign') { el('rect', { x: -40, y: -h - 30, width: 8, height: 32, fill: C.INK }, g); el('rect', { x: 32, y: -h - 30, width: 8, height: 32, fill: C.INK }, g);
|
|
129
|
+
el('rect', { x: -56, y: -h - 70, width: 112, height: 44, rx: 10, fill: C.CR }, g); el('circle', { cx: -30, cy: -h - 48, r: 11, fill: C.CO }, g);
|
|
130
|
+
el('rect', { x: -12, y: -h - 56, width: 50, height: 8, rx: 4, fill: C.INK }, g); el('rect', { x: -12, y: -h - 44, width: 34, height: 8, rx: 4, fill: C.INK }, g); }
|
|
131
|
+
el('rect', { x: -w / 2, y: -h, width: w, height: h + 1, fill: b.c }, g);
|
|
132
|
+
el('rect', { x: w / 2 - sw, y: -h, width: sw, height: h + 1, fill: d }, g);
|
|
133
|
+
if (b.hero) {
|
|
134
|
+
b.wins = [];
|
|
135
|
+
for (let j = 0; j < 9; j++) for (const x of [-76, 0]) { if (j === 5 && x === -76) el('rect', { x, y: -500 + j * 50, width: 64, height: 36, rx: 3, fill: C.INKW }, g); else el('circle', { cx: x + 32, cy: -482 + j * 50, r: 15, fill: C.INKW }, g); }
|
|
136
|
+
el('rect', { x: -22, y: -56, width: 44, height: 56, rx: 8, fill: C.INKW }, g);
|
|
137
|
+
} else {
|
|
138
|
+
const m = 16, gap = 12, usable = w - sw - 2 * m + 4, ww = (usable - (b.cols - 1) * gap) / b.cols, wh = 28;
|
|
139
|
+
const lit = b.c === C.SU ? C.CR : C.SU;
|
|
140
|
+
for (let j = 0; j < b.rows; j++) for (let i = 0; i < b.cols; i++) {
|
|
141
|
+
const x = -w / 2 + m + i * (ww + gap), y = -h + 36 + j * 46;
|
|
142
|
+
el('circle', { cx: x + ww / 2, cy: y + wh / 2, r: Math.min(ww, wh) * .47, fill: WUNLIT[b.c] }, g);
|
|
143
|
+
if (wr_() < .5) { const lg = G(g); el('circle', { cx: 0, cy: 0, r: Math.min(ww, wh) * .47, fill: lit }, lg); lits.push({ g: lg, x: x + ww / 2, y: y + wh / 2 }); }
|
|
144
|
+
}
|
|
145
|
+
if (b.door) el('rect', { x: -w * .12 - sw / 2, y: -52, width: w * .24, height: 52, rx: 8, fill: d }, g);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
function SU_or(b) { return b.c === C.SU ? C.CR : C.SU; }
|
|
149
|
+
{ const r = rng(5); const order = lits.map((_, i) => i).sort(() => r() - .5); order.forEach((li, k) => lits[li].t0 = T('winStart') + k * (T('winEnd') - T('winStart')) / order.length); }
|
|
150
|
+
// trees
|
|
151
|
+
const TREES = [{ x: 120, h: 60, r: 26 }, { x: 300, h: 70, r: 30 }, { x: 516, h: 84, r: 34 }, { x: 680, h: 62, r: 26 }, { x: 1074, h: 78, r: 32 }, { x: 1246, h: 66, r: 28 }, { x: 1622, h: 88, r: 36 }, { x: 1830, h: 70, r: 30 }]
|
|
152
|
+
.map((tr, i) => { const gg = G(world); el('rect', { x: -4, y: -tr.h, width: 8, height: tr.h, fill: C.INK }, gg); const yT = -tr.h - 1.8 * tr.r, yB = -tr.h + tr.r; el('rect', { x: -tr.r, y: yT, width: 2 * tr.r, height: yB - yT, rx: tr.r, fill: C.MI }, gg);
|
|
153
|
+
el('path', { d: `M0 ${yT} A${tr.r} ${tr.r} 0 0 1 ${tr.r} ${yT + tr.r} L${tr.r} ${yB - tr.r} A${tr.r} ${tr.r} 0 0 1 0 ${yB} Z`, fill: C.MID }, gg); return { ...tr, g: gg, t0: T('trees') + i * F(2) }; });
|
|
154
|
+
const groundR = el('rect', { x: -4000, y: GY, width: 10000, height: 4000, fill: C.CR }, world);
|
|
155
|
+
const groundLead = el('rect', { x: 0, y: GY, width: 170, height: 4000, fill: C.SU }, world);
|
|
156
|
+
const shadow = el('ellipse', { cx: 960, cy: GY + 4, rx: 50, ry: 8, fill: C.CRS }, world);
|
|
157
|
+
const road = el('rect', { x: -4000, y: 872, width: 10000, height: 42, fill: C.CRD }, world);
|
|
158
|
+
const laneG = G(world); const lanes = []; for (let x = -40; x < 2000; x += 96) lanes.push({ x, e: el('rect', { x, y: 890, width: 48, height: 7, rx: 3.5, fill: C.CR }, laneG) });
|
|
159
|
+
const slabs = BLD.map(b => { const g = G(world); el('path', { d: `M${-b.w / 2} 0 L${b.w / 2} 0 L${b.w / 2 - 34} 26 L${-b.w / 2 - 34} 26 Z`, fill: C.CRD }, g); return g; });
|
|
160
|
+
const bus = G(world); const busB = G(bus); const busW = [];
|
|
161
|
+
{ el('rect', { x: -104, y: -78, width: 208, height: 64, rx: 24, fill: C.CO }, busB); el('rect', { x: -104, y: -34, width: 208, height: 20, fill: C.COD }, busB);
|
|
162
|
+
el('path', { d: 'M104 -54 L104 -30 L60 -30 L60 -64 Q96 -64 104 -54 Z', fill: C.CR }, busB);
|
|
163
|
+
[-84, -40, 4].forEach(x => el('rect', { x, y: -64, width: 36, height: 24, rx: 7, fill: C.CR }, busB));
|
|
164
|
+
[-60, 64].forEach(x => { const w = G(bus); el('circle', { cx: 0, cy: 0, r: 17, fill: C.INK }, w); el('rect', { x: -3, y: -12, width: 6, height: 10, rx: 3, fill: C.CR }, w); busW.push({ g: w, x }); }); }
|
|
165
|
+
const IMP = [['impact1', 1], ['impact2', .6]].map(([k, sc]) => ({ t0: T(k), sc,
|
|
166
|
+
rips: [C.SU, C.CRS].map(c => el('ellipse', { cx: 960, cy: GY + 6, rx: 0, ry: 0, fill: 'none', stroke: c, 'stroke-width': 0 }, world)),
|
|
167
|
+
lines: [-168, -143, -117, -63, -37, -12].map((a, i) => ({ a: a * Math.PI / 180, ln: el('line', { stroke: [C.SU, C.CR, C.SU, C.SU, C.CR, C.SU][i], 'stroke-width': 17 * (sc > .8 ? 1 : .75), 'stroke-linecap': 'round' }, world) })) }));
|
|
168
|
+
|
|
169
|
+
// ============================================================ hero window + INTERIOR (drawn at 1920x1080, scaled 1/30 into a 64x36 window)
|
|
170
|
+
const HB = BLD[4]; const HW = { x: 894, y: 570, w: 64, h: 36 }; const HL = { x: HW.x - HB.cx, y: HW.y - GY };
|
|
171
|
+
const cpH = el('clipPath', { id: 'heroClip' }, defs); el('rect', { x: HL.x, y: HL.y, width: HW.w, height: HW.h }, cpH);
|
|
172
|
+
const heroWinG = G(HB.g, { 'clip-path': 'url(#heroClip)' });
|
|
173
|
+
const INT = G(heroWinG, { transform: `translate(${HL.x} ${HL.y}) scale(${HW.w / 1920}) translate(860 -153) scale(1.15) translate(-860 153)` });
|
|
174
|
+
const blind = el('rect', { x: HL.x, y: HL.y, width: HW.w, height: HW.h, fill: C.INKW }, HB.g);
|
|
175
|
+
const sill = el('rect', { x: HL.x - 5, y: HL.y + HW.h + 2, width: HW.w + 10, height: 6, rx: 3, fill: C.CR }, HB.g);
|
|
176
|
+
const heroSparks = [0, 1, 2, 3].map(() => el('line', { stroke: C.CR, 'stroke-width': 4, 'stroke-linecap': 'round' }, HB.g));
|
|
177
|
+
// --- room
|
|
178
|
+
el('rect', { x: -20, y: -20, width: 1960, height: 1120, fill: C.SU }, INT);
|
|
179
|
+
// back window with the city we came from
|
|
180
|
+
{ const cp = el('clipPath', { id: 'bwClip' }, defs); el('rect', { x: 178, y: 158, width: 404, height: 284, rx: 8 }, cp);
|
|
181
|
+
el('rect', { x: 160, y: 140, width: 440, height: 320, rx: 18, fill: C.CR }, INT);
|
|
182
|
+
const gl = G(INT, { 'clip-path': 'url(#bwClip)' }); el('rect', { x: 170, y: 150, width: 420, height: 300, fill: C.U }, gl);
|
|
183
|
+
el('circle', { cx: 490, cy: 250, r: 46, fill: C.CO }, gl);
|
|
184
|
+
[[200, 90], [262, 150], [330, 110], [402, 170], [470, 120], [540, 90]].forEach(([x, h], i) => el('rect', { x, y: 442 - h, width: 58, height: h, fill: C.UL }, gl));
|
|
185
|
+
el('rect', { x: 230, y: 214, width: 96, height: 26, rx: 13, fill: C.CR }, gl);
|
|
186
|
+
el('rect', { x: 373, y: 150, width: 14, height: 300, fill: C.CR }, INT); el('rect', { x: 170, y: 293, width: 420, height: 14, fill: C.CR }, INT);
|
|
187
|
+
el('rect', { x: 136, y: 456, width: 488, height: 22, rx: 11, fill: C.SUD }, INT); }
|
|
188
|
+
// poster
|
|
189
|
+
el('rect', { x: 1470, y: 160, width: 260, height: 330, rx: 16, fill: C.MI }, INT);
|
|
190
|
+
el('rect', { x: 1494, y: 184, width: 212, height: 282, rx: 8, fill: C.CR }, INT);
|
|
191
|
+
el('circle', { cx: 1600, cy: 290, r: 62, fill: C.CO }, INT);
|
|
192
|
+
el('path', { d: 'M1520 440 L1600 360 L1680 440 Z', fill: C.U }, INT);
|
|
193
|
+
// lamp (swings from ceiling)
|
|
194
|
+
const lamp = G(INT); { el('line', { x1: 0, y1: -40, x2: 0, y2: 170, stroke: C.INK, 'stroke-width': 6 }, lamp); el('circle', { cx: 0, cy: 214, r: 18, fill: C.CR }, lamp); el('path', { d: 'M-74 214 L-40 162 L40 162 L74 214 Z', fill: C.INK }, lamp); }
|
|
195
|
+
// character (rig: CH hip null -> torso, shoulders -> elbows, neck -> head)
|
|
196
|
+
const CH = G(INT);
|
|
197
|
+
el('path', { d: 'M-180 80 L-180 -110 Q-180 -200 -90 -200 L90 -200 Q180 -200 180 -110 L180 80 Z', fill: C.MI }, CH);
|
|
198
|
+
el('path', { d: 'M120 -196 Q180 -186 180 -110 L180 80 L132 80 Z', fill: C.MID }, CH);
|
|
199
|
+
el('path', { d: 'M-36 -201 L0 -154 L36 -201 Z', fill: C.SK }, CH);
|
|
200
|
+
function mkArm(side) { const sh = G(CH); const up = G(sh);
|
|
201
|
+
el('line', { x1: 0, y1: 0, x2: 0, y2: 116, stroke: C.MID, 'stroke-width': 60, 'stroke-linecap': 'round' }, up);
|
|
202
|
+
const fo = G(up); tf(fo, 0, 116);
|
|
203
|
+
el('line', { x1: 0, y1: 0, x2: 0, y2: 100, stroke: C.SK, 'stroke-width': 40, 'stroke-linecap': 'round' }, fo);
|
|
204
|
+
el('circle', { cx: 0, cy: 106, r: 25, fill: C.SK }, fo); const fg = G(fo); el('line', { x1: 0, y1: 0, x2: 0, y2: 34, stroke: C.SK, 'stroke-width': 17, 'stroke-linecap': 'round' }, fg); return { sh, up, fo, fg, side }; }
|
|
205
|
+
const armL = mkArm(-1), armR = mkArm(1);
|
|
206
|
+
const neck = G(CH); el('rect', { x: -26, y: -40, width: 52, height: 56, fill: C.SK }, neck);
|
|
207
|
+
const head = G(neck);
|
|
208
|
+
el('circle', { cx: -92, cy: 12, r: 19, fill: C.SK }, head); el('circle', { cx: 92, cy: 12, r: 19, fill: C.SK }, head);
|
|
209
|
+
el('circle', { cx: 0, cy: 0, r: 95, fill: C.SK }, head);
|
|
210
|
+
const bunG = G(head); el('circle', { cx: 34, cy: -118, r: 38, fill: C.INK }, bunG);
|
|
211
|
+
el('path', { d: 'M-99 4 C-104 -76 -52 -112 4 -112 C62 -112 104 -74 99 4 C84 -40 44 -60 -8 -52 C-50 -46 -80 -26 -99 4 Z', fill: C.INK }, head);
|
|
212
|
+
el('circle', { cx: -54, cy: 40, r: 14, fill: C.COL }, head); el('circle', { cx: 54, cy: 40, r: 14, fill: C.COL }, head);
|
|
213
|
+
const eyes = [-34, 34].map(x => { const e = G(head); el('ellipse', { cx: 0, cy: 0, rx: 9, ry: 12, fill: C.INK }, e); return { g: e, x }; });
|
|
214
|
+
const glasses = G(head); [-34, 34].forEach(x => el('circle', { cx: x, cy: 10, r: 28, fill: 'none', stroke: C.INK, 'stroke-width': 6 }, glasses));
|
|
215
|
+
const glint = G(head); [-34, 34].forEach(x => el('line', { x1: x - 14, y1: 8, x2: x - 2, y2: -6, stroke: C.CR, 'stroke-width': 8, 'stroke-linecap': 'round' }, glint));
|
|
216
|
+
el('path', { d: 'M-7 6 Q0 0 7 6', fill: 'none', stroke: C.INK, 'stroke-width': 6, 'stroke-linecap': 'round' }, glasses);
|
|
217
|
+
const brows = [-1, 1].map(s => { const b = G(head); el('line', { x1: s * 22, y1: -30, x2: s * 48, y2: -26, stroke: C.INK, 'stroke-width': 7, 'stroke-linecap': 'round' }, b); return b; });
|
|
218
|
+
const smile = el('path', { d: 'M-18 46 Q0 60 18 46', fill: 'none', stroke: C.INK, 'stroke-width': 6, 'stroke-linecap': 'round' }, head);
|
|
219
|
+
const oMouth = el('ellipse', { cx: 0, cy: 54, rx: 10, ry: 13, fill: C.INK }, head);
|
|
220
|
+
// laptop, desk, props
|
|
221
|
+
el('rect', { x: 710, y: 588, width: 300, height: 156, rx: 18, fill: C.INK }, INT); el('circle', { cx: 860, cy: 668, r: 17, fill: C.CO }, INT);
|
|
222
|
+
const mugHandle = el('circle', { cx: 566, cy: 690, r: 22, fill: 'none', stroke: C.CO, 'stroke-width': 14 }, INT);
|
|
223
|
+
el('rect', { x: 476, y: 646, width: 88, height: 96, rx: 14, fill: C.CO }, INT); el('rect', { x: 476, y: 676, width: 88, height: 14, fill: C.CR }, INT);
|
|
224
|
+
const steam = [0, 1].map(i => el('path', { d: `M${504 + i * 32} 630 C${488 + i * 32} 600 ${522 + i * 32} 580 ${506 + i * 32} 550 C${490 + i * 32} 520 ${522 + i * 32} 500 ${506 + i * 32} 470`, fill: 'none', stroke: C.CR, 'stroke-width': 9, 'stroke-linecap': 'round', 'stroke-dasharray': '46 260' }, INT));
|
|
225
|
+
const plant = G(INT); { [[-30, -34], [0, -8], [30, 30]].forEach(([dx, rot], i) => { const lf = G(plant); tf(lf, 0, 0, 1, 1, rot); el('ellipse', { cx: 0, cy: -58, rx: 22, ry: 58, fill: i === 1 ? C.MI : C.MID }, lf); }); }
|
|
226
|
+
el('rect', { x: 1600, y: 668, width: 80, height: 74, rx: 12, fill: C.INK }, INT);
|
|
227
|
+
const phone = G(INT);
|
|
228
|
+
{ el('path', { d: 'M-50 0 L-34 -30 L34 -30 L50 0 Z', fill: C.INK }, phone); el('rect', { x: -62, y: -238, width: 124, height: 224, rx: 22, fill: C.INK }, phone);
|
|
229
|
+
el('rect', { x: -52, y: -226, width: 104, height: 198, rx: 14, fill: C.UD }, phone); }
|
|
230
|
+
const screenLit = G(phone); { el('rect', { x: -52, y: -226, width: 104, height: 198, rx: 14, fill: C.CR }, screenLit); el('circle', { cx: 26, cy: -196, r: 13, fill: C.CO }, screenLit);
|
|
231
|
+
[[-36, -150, 72], [-36, -128, 52], [-36, -106, 62]].forEach(([x, y, w]) => el('rect', { x, y, width: w, height: 10, rx: 5, fill: C.INK }, screenLit)); }
|
|
232
|
+
const buzz = [-1, 1].map(s => { const b = G(INT); [0, 1].forEach(k => el('path', { d: `M${s * (76 + k * 18)} -150 Q${s * (92 + k * 18)} -120 ${s * (76 + k * 18)} -90`, fill: 'none', stroke: C.INK, 'stroke-width': 6, 'stroke-linecap': 'round' }, b)); return b; });
|
|
233
|
+
el('rect', { x: -40, y: 736, width: 2000, height: 36, fill: C.UL }, INT); el('rect', { x: -40, y: 772, width: 2000, height: 330, fill: C.U }, INT);
|
|
234
|
+
el('rect', { x: 330, y: 830, width: 300, height: 18, rx: 9, fill: C.UD }, INT); el('rect', { x: 1290, y: 830, width: 300, height: 18, rx: 9, fill: C.UD }, INT);
|
|
235
|
+
// notification bubble (pivot = tail tip)
|
|
236
|
+
const BUB = { x: 1300, y: 470 };
|
|
237
|
+
const bubble = G(INT); el('circle', { cx: 0, cy: -84, r: 58, fill: C.CO }, bubble); el('path', { d: 'M-18 -36 L0 0 L18 -36 Z', fill: C.CO }, bubble);
|
|
238
|
+
const bell = G(bubble); { el('path', { d: 'M-22 14 L-22 -4 C-22 -22 -12 -30 0 -30 C12 -30 22 -22 22 -4 L22 14 L29 20 L-29 20 Z', fill: C.CR }, bell); el('circle', { cx: 0, cy: 27, r: 7, fill: C.CR }, bell); el('circle', { cx: 0, cy: -32, r: 5, fill: C.CR }, bell); }
|
|
239
|
+
const pingRings = [0, 1].map(() => el('circle', { cx: BUB.x, cy: BUB.y - 84, r: 60, fill: 'none', stroke: C.CO, 'stroke-width': 10 }, INT));
|
|
240
|
+
const exclaim = G(INT); { el('rect', { x: -9, y: -70, width: 18, height: 58, rx: 9, fill: C.INK }, exclaim); el('circle', { cx: 0, cy: 8, r: 10, fill: C.INK }, exclaim);
|
|
241
|
+
el('line', { x1: -34, y1: -60, x2: -50, y2: -76, stroke: C.INK, 'stroke-width': 6, 'stroke-linecap': 'round' }, exclaim); el('line', { x1: 32, y1: -54, x2: 52, y2: -64, stroke: C.INK, 'stroke-width': 6, 'stroke-linecap': 'round' }, exclaim); }
|
|
242
|
+
// selective shutter: flat wipe edges get 8 % of the global motion-blur span so they stay crisp (no gradient-looking rims)
|
|
243
|
+
const crisp = (t, k = .08) => { const f0 = Math.round(t * 30) / 30; return f0 + (t - f0) * k; };
|
|
244
|
+
const W1 = G(INT); el('circle', { cx: 0, cy: 0, r: 58, fill: C.CO }, W1); const w1t = el('path', { fill: C.CO }, W1);
|
|
245
|
+
const W2 = G(INT); el('rect', { x: -1500, y: -750, width: 3000, height: 1500, rx: 750, fill: C.MI }, W2);
|
|
246
|
+
const W3 = G(INT); el('path', { d: 'M-1700 0 A1700 1700 0 0 0 1700 0 Z', fill: C.CR }, W3);
|
|
247
|
+
|
|
248
|
+
// ============================================================ camera
|
|
249
|
+
const P = { x: HW.x + HW.w / 2, y: HW.y + HW.h / 2 }, S1 = 1920 / HW.w;
|
|
250
|
+
function camera(t) {
|
|
251
|
+
const u = E.ioC(inv(F(24), F(56), t)), uy = E.outQ(inv(F(28), F(50), t));
|
|
252
|
+
let s = lerp(1.4, SCC, u) * (1 + .03 * kick(t, T('impact1') - F(.5), 4, .45) + .014 * kick(t, T('impact2') - F(.5), 4, .45)), cx = 960, cy = lerp(660, CYC, uy);
|
|
253
|
+
const ta = T('camAntic'), tp = T('push'), tg = T('glass'), t1 = T('arrive'), tS = t1 + F(3);
|
|
254
|
+
if (t > ta) {
|
|
255
|
+
const sB = SCC, spB = { x: (P.x - 960) * sB + 960, y: (P.y - CYC) * sB + 540 };
|
|
256
|
+
// ease-in (pow 2.2) to the glass, velocity-matched out-back settle: peak zoom speed lands exactly on the beat
|
|
257
|
+
const NP = 2.2, OB = 1.2, A0 = -0.05, kA = NP / (tg - tp), kB = (OB + 3) / (tS - tg), PG = (kB + A0 * kA) / (kA + kB);
|
|
258
|
+
let p = t < tp ? A0 * E.ioQ(inv(ta, tp, t)) : t < tg ? A0 + (PG - A0) * inv(tp, tg, t) ** NP : PG + (1 - PG) * E.outB(inv(tg, tS, t), OB);
|
|
259
|
+
s = Math.exp(lerp(Math.log(sB), Math.log(S1), p));
|
|
260
|
+
const sp = { x: lerp(spB.x, 960, p), y: lerp(spB.y, 540, p) };
|
|
261
|
+
cx = P.x - (sp.x - 960) / s; cy = P.y - (sp.y - 540) / s;
|
|
262
|
+
if (t > tS) { const w = E.ioQ(inv(tS, T('wipe') + F(10), t)); s *= 1 + 0.14 * w; cx = P.x + (1060 - 960) / S1 * w; cy = P.y + (500 - 540) / S1 * w; }
|
|
263
|
+
}
|
|
264
|
+
return { s, cx, cy };
|
|
265
|
+
}
|
|
266
|
+
function localToScreen(cam, lx, ly) { const wx = HW.x + lx * HW.w / 1920, wy = HW.y + ly * HW.h / 1080; return { x: (wx - cam.cx) * cam.s + 960, y: (wy - cam.cy) * cam.s + 540 }; }
|
|
267
|
+
|
|
268
|
+
// ============================================================ QUADRANTS (screen space): the dot performs every beat on a full-bleed 2x2 grid
|
|
269
|
+
const STRK = G(S); const sr_ = rng(77);
|
|
270
|
+
const FLASH = el('rect', { fill: C.CR }, STRK); vis(FLASH, false);
|
|
271
|
+
const STREAKS = Array.from({ length: 22 }, (_, i) => ({ a: (i / 22) * 2 * Math.PI + (sr_() - .5) * .3, r0: 190 + sr_() * 380, len: 200 + sr_() * 300, w: 16 + sr_() * 8, f0: 102 + (i % 3), p: el('path', { fill: i % 2 ? C.CR : C.SU }, STRK) }));
|
|
272
|
+
const ICO = G(S); el('rect', { x: -10, y: -10, width: 1940, height: 1100, fill: C.CR }, ICO);
|
|
273
|
+
const CHK = [[0, -540], [-960, 0]].map(([x, y]) => { const g = G(ICO); el('rect', { x, y, width: 960, height: 540, fill: C.CRD }, g); return g; });
|
|
274
|
+
const R_D = 50, CEN = { x: 960, y: 540 };
|
|
275
|
+
const mixHex = (a, b, k) => { const p = h => [1, 3, 5].map(i => parseInt(h.slice(i, i + 2), 16)); const A = p(a), B = p(b); return '#' + A.map((v, i) => Math.round(v + (B[i] - v) * k).toString(16).padStart(2, '0')).join(''); };
|
|
276
|
+
const GHOST = G(ICO);
|
|
277
|
+
const QD = [
|
|
278
|
+
{ k: 'bar', x0: 0, y0: 0, col: C.U, ink: C.CR, dsh: C.SU, n: '01', w: '进步', right: false, lx: 76, ly: 108, t0: T('burst1'), L: { x: 600, y: 388 }, K: { x: 600, y: 118 }, da: -24, dr0: 70 },
|
|
279
|
+
{ k: 'check', x0: 960, y0: 0, col: C.MI, ink: C.INK, dsh: C.U, n: '02', w: '搞定', right: true, lx: 1844, ly: 108, t0: T('burst2'), L: { x: 1280, y: 310 }, K: { x: 1605, y: 185 }, da: -165, dr0: 70 },
|
|
280
|
+
{ k: 'heart', x0: 960, y0: 540, col: C.SU, ink: C.INK, dsh: C.U, n: '03', w: '心动', right: true, lx: 1844, ly: 1006, t0: T('burst3'), L: { x: 1440, y: 810 }, K: { x: 1440, y: 810 }, da: -90, dr0: 214 },
|
|
281
|
+
{ k: 'coin', x0: 0, y0: 540, col: C.CO, ink: C.CR, dsh: C.SU, n: '04', w: '攒钱', right: false, lx: 76, ly: 1006, t0: T('burst4'), L: { x: 480, y: 810 }, K: { x: 480, y: 810 }, da: -90, dr0: 206 },
|
|
282
|
+
];
|
|
283
|
+
const qR = (q, P) => Math.max(...[[q.x0, q.y0], [q.x0 + 960, q.y0], [q.x0, q.y0 + 540], [q.x0 + 960, q.y0 + 540]].map(([x, y]) => Math.hypot(x - P.x, y - P.y))) + 6;
|
|
284
|
+
QD.forEach((q, i) => {
|
|
285
|
+
const cp = el('clipPath', { id: 'qc' + i }, defs); el('rect', { x: q.x0, y: q.y0, width: 960, height: 540 }, cp);
|
|
286
|
+
q.g = G(ICO, { 'clip-path': `url(#qc${i})` }); q.RL = qR(q, q.L); q.RK = qR(q, q.K);
|
|
287
|
+
q.burst = el('circle', { cx: q.L.x, cy: q.L.y, r: 0, fill: q.col }, q.g);
|
|
288
|
+
q.ic = G(q.g);
|
|
289
|
+
q.lab = G(q.g); { const tx = el('text', { x: 0, y: 0, 'text-anchor': q.right ? 'end' : 'start', fill: q.ink }, q.lab);
|
|
290
|
+
const a = el('tspan', { style: "font:600 30px 'Fredoka'; letter-spacing:2px" }, tx); a.textContent = q.n;
|
|
291
|
+
const b = el('tspan', { dx: 16, style: "font:700 40px 'Resource Han Rounded'; letter-spacing:5px" }, tx); b.textContent = q.w; }
|
|
292
|
+
q.dashes = [-1, 0, 1].map(j => ({ a: (q.da + j * 44) * Math.PI / 180, ln: el('line', { stroke: q.dsh, 'stroke-width': 12, 'stroke-linecap': 'round' }, q.g) }));
|
|
293
|
+
q.keyD = el('circle', { cx: q.K.x, cy: q.K.y, r: R_D, fill: C.CO }, q.g);
|
|
294
|
+
q.gh = G(GHOST); { const tx = el('text', { x: 0, y: 0, 'text-anchor': q.right ? 'end' : 'start', fill: mixHex(i % 2 ? C.CRD : C.CR, q.col, .3) }, q.gh);
|
|
295
|
+
const a = el('tspan', { style: "font:600 30px 'Fredoka'; letter-spacing:2px" }, tx); a.textContent = q.n;
|
|
296
|
+
const b = el('tspan', { dx: 16, style: "font:700 40px 'Resource Han Rounded'; letter-spacing:5px" }, tx); b.textContent = q.w; }
|
|
297
|
+
});
|
|
298
|
+
{ const q = QD[0]; // bar chart: the dot rides up on the tallest bar and stays as its cap
|
|
299
|
+
q.bars = [[360, 120, C.UL], [480, 190, C.UL], [600, 270, C.CR]].map(([x, h, c]) => { const g = G(q.ic); el('rect', { x: -42, y: -h, width: 84, height: h + 20, rx: 24, fill: c }, g); return { g, x, h }; });
|
|
300
|
+
q.base = G(q.ic); el('rect', { x: -190, y: 440, width: 380, height: 26, rx: 13, fill: C.CR }, q.base); }
|
|
301
|
+
{ const q = QD[1]; // check: the dot is the pen tip, draws the stroke and stays as its full stop
|
|
302
|
+
q.stroke = el('path', { d: 'M1280 310 L1385 415 L1605 185', fill: 'none', stroke: C.INK, 'stroke-width': 62, 'stroke-linecap': 'round', 'stroke-linejoin': 'round', pathLength: 1, 'stroke-dasharray': '1 2', 'stroke-dashoffset': 1 }, q.ic); }
|
|
303
|
+
{ const q = QD[2]; // heart: the dot splits into two circles + a diamond
|
|
304
|
+
q.hg = G(q.ic); q.hc = [-1, 1].map(() => el('circle', { cx: 0, cy: 0, r: 110, fill: C.CO }, q.hg)); q.hd = el('rect', { x: -.5, y: -.5, width: 1, height: 1, fill: C.CO }, q.hg); }
|
|
305
|
+
{ const q = QD[3]; // coin: the dot flips edge-on into a coin
|
|
306
|
+
q.cg = G(q.ic); q.rim = G(q.cg); q.rimC = el('circle', { cx: 0, cy: 0, r: 180, fill: C.SUE }, q.rim); q.face = G(q.cg);
|
|
307
|
+
q.fDot = el('circle', { cx: 0, cy: 0, r: 180, fill: C.CO }, q.face);
|
|
308
|
+
q.fFront = G(q.face); el('circle', { cx: 0, cy: 0, r: 180, fill: C.SU }, q.fFront); el('circle', { cx: 0, cy: 0, r: 140, fill: 'none', stroke: C.SUD, 'stroke-width': 16 }, q.fFront);
|
|
309
|
+
{ const tx = el('text', { x: 0, y: 68, 'text-anchor': 'middle', fill: C.SUE, style: "font:700 200px 'Fredoka'" }, q.fFront); tx.textContent = '¥'; }
|
|
310
|
+
q.fBack = G(q.face); el('circle', { cx: 0, cy: 0, r: 180, fill: C.SUD }, q.fBack); el('circle', { cx: 0, cy: 0, r: 118, fill: C.SUE }, q.fBack); }
|
|
311
|
+
const heroD = el('circle', { cx: 0, cy: 0, r: R_D, fill: C.CO }, ICO);
|
|
312
|
+
const zipD = QD.map(() => { const c = el('circle', { cx: 0, cy: 0, r: R_D, fill: C.CO }, ICO); vis(c, false); return c; });
|
|
313
|
+
// hop schedule: anticipation start a, take-off o, landing l (= the beat), arc height H
|
|
314
|
+
const HOPS = [[171, 173, 'burst1', 250], [185, 188, 'burst2', 110], [200, 203, 'burst3', 140], [215, 218, 'burst4', 520]].map(([a, o, l, H]) => ({ a: F(a), o: F(o), l: T(l), H }));
|
|
315
|
+
|
|
316
|
+
// ============================================================ LOGO scene
|
|
317
|
+
const LOGO = G(S);
|
|
318
|
+
const lbg = el('rect', { x: 0, y: 0, width: 960, height: 540, fill: C.U }, LOGO); // R2: the TL ultramarine quadrant grows into the lockup field
|
|
319
|
+
const stemR = el('rect', { fill: C.CR }, LOGO); // R2: the TL bar morphing into the stem of the i
|
|
320
|
+
const DECO = [
|
|
321
|
+
{ x: 190, y: 190, mk: g => el('circle', { cx: 0, cy: 0, r: 150, fill: 'none', stroke: C.UT, 'stroke-width': 38 }, g), t0: F(243), rs: 4 },
|
|
322
|
+
{ x: 1770, y: 935, mk: g => el('rect', { x: -140, y: -46, width: 280, height: 92, rx: 46, fill: C.UT }, g), t0: F(245), rs: -3, r0: -28 },
|
|
323
|
+
]; // R2: corner decor removed
|
|
324
|
+
// wordmark
|
|
325
|
+
const WORD = 'popwıse'; let WF = 250; { const w0 = meas(`700 ${WF}px Fredoka`, WORD).width; WF = WF * 900 / w0; }
|
|
326
|
+
// v5 title 「扁平矢量」 (Resource Han Rounded Heavy, the film's own CJK family): the coral dot IS the 丶 of 扁 and the TL bar
|
|
327
|
+
// morphs into 扁's top bar (the match cut of the wordmark, re-targeted). The 丶 and the bar are found by rasterising 扁 once.
|
|
328
|
+
function title5() {
|
|
329
|
+
const chars = [...'扁平矢量'], fs = 260, font = `900 ${fs}px 'Resource Han Rounded'`, ls = 14;
|
|
330
|
+
const mm = chars.map(c => meas(font, c)), gw = mm.map(m => m.width), tot = gw.reduce((a, b) => a + b, 0) + ls * (chars.length - 1);
|
|
331
|
+
const asc = Math.max(...mm.map(m => m.actualBoundingBoxAscent)), des = Math.max(...mm.map(m => m.actualBoundingBoxDescent));
|
|
332
|
+
const base = Math.round(505 + (asc - des) / 2); // ink centred on y 505
|
|
333
|
+
let xx = 960 - tot / 2; const cx = gw.map(w => { const c = xx + w / 2; xx += w + ls; return c; });
|
|
334
|
+
const W2 = Math.ceil(gw[0]) + 40, H2 = fs + 60, ox = 20, oy = Math.round(fs * .95);
|
|
335
|
+
const c2 = document.createElement('canvas'); c2.width = W2; c2.height = H2; const x2 = c2.getContext('2d'); x2.font = font; x2.fillStyle = '#000'; x2.fillText(chars[0], ox, oy);
|
|
336
|
+
const d = x2.getImageData(0, 0, W2, H2).data, ink = (x, y) => d[(y * W2 + x) * 4 + 3] > 127;
|
|
337
|
+
const runs = y => { const r = []; let s = -1; for (let x = 0; x <= W2; x++) { const h = x < W2 && ink(x, y); if (h && s < 0) s = x; if (!h && s >= 0) { r.push([s, x - 1]); s = -1; } } return r; };
|
|
338
|
+
let y = 0; while (y < H2 && !runs(y).length) y++; const top = y; let dx0 = 1e9, dx1 = -1;
|
|
339
|
+
for (; y < H2; y++) { const r = runs(y); if (r.length === 1 && r[0][1] - r[0][0] > gw[0] * .45) break; r.forEach(([a, b]) => { dx0 = Math.min(dx0, a); dx1 = Math.max(dx1, b); }); }
|
|
340
|
+
const barTop = y; let bx0 = 1e9, bx1 = -1;
|
|
341
|
+
for (; y < H2; y++) { const r = runs(y); if (r.length !== 1) break; bx0 = Math.min(bx0, r[0][0]); bx1 = Math.max(bx1, r[0][1]); }
|
|
342
|
+
const barBot = y, PX = x => cx[0] - gw[0] / 2 + (x - ox), PY = y => base + (y - oy), lx = x => f3(x - ox - gw[0] / 2), ly = y => f3(y - oy);
|
|
343
|
+
const r = Math.round(fs * .125), bar = { x0: PX(bx0), x1: PX(bx1 + 1), y0: PY(barTop), y1: PY(barBot) };
|
|
344
|
+
const clip = el('clipPath', { id: 'v5cut' }, defs); // everything except the 丶 (glyph-local: origin = glyph centre x, baseline)
|
|
345
|
+
el('path', { d: `M-600 -700 H600 V400 H-600 Z M${lx(dx0 - 4)} ${ly(top - 8)} H${lx(dx1 + 5)} V${f3(ly(barTop) + .5)} H${lx(dx0 - 4)} Z`, 'clip-rule': 'evenodd' }, clip);
|
|
346
|
+
const bl = { x: (bar.x0 + bar.x1) / 2 - cx[0], y: (bar.y0 + bar.y1) / 2 - base };
|
|
347
|
+
const inkB = { x0: cx[0] - gw[0] / 2 - mm[0].actualBoundingBoxLeft, x1: cx[3] - gw[3] / 2 + mm[3].actualBoundingBoxRight, y0: base - asc, y1: base + des };
|
|
348
|
+
const mk = parent => chars.map((ch, i) => { const g = G(parent), gi = G(g); const tx = el('text', { x: f3(-gw[i] / 2), y: 0, fill: C.CR, style: `font:${font}` }, gi); tx.textContent = ch;
|
|
349
|
+
if (i === 0) tx.setAttribute('clip-path', 'url(#v5cut)'); return { g, gi, cx: cx[i], t0: F(244 + i), i }; });
|
|
350
|
+
return { base, cx, r, dx: PX((dx0 + dx1 + 1) / 2), dy: bar.y0 - r - 2, bar, bl, ink: inkB, enB: Math.round(inkB.y1 + 96), mk, fs };
|
|
351
|
+
}
|
|
352
|
+
const T5 = V5 ? title5() : null;
|
|
353
|
+
const wfont = `700 ${WF}px Fredoka`, WBASE = V5 ? T5.base : 548;
|
|
354
|
+
const WTOT = meas(wfont, WORD).width, WX0 = 960 - WTOT / 2;
|
|
355
|
+
const wmG = G(LOGO);
|
|
356
|
+
const LET = V5 ? T5.mk(wmG) : [...WORD].map((ch, i) => { const pre = meas(wfont, WORD.slice(0, i)).width, w = meas(wfont, ch).width; const g = G(wmG);
|
|
357
|
+
const tx = el('text', { x: -w / 2, y: 0, fill: C.CR, style: `font:${wfont}` }, g); tx.textContent = ch; return { g, cx: WX0 + pre + w / 2, t0: T('letters') + F(Math.max(0, Math.abs(i - 4) - 1)), i }; });
|
|
358
|
+
const mI = meas(wfont, 'ı'), mX = meas(wfont, 'x');
|
|
359
|
+
const DOT_R = V5 ? T5.r : WF * 0.15;
|
|
360
|
+
const TIT = V5 ? { x: T5.dx, y: T5.dy } : { x: WX0 + meas(wfont, 'popw').width + (mI.actualBoundingBoxRight - mI.actualBoundingBoxLeft) / 2, y: WBASE - mX.actualBoundingBoxAscent - WF * 0.07 - DOT_R };
|
|
361
|
+
const END_RINGS = [T('beatBob2')].map(t0 => { const c = el('circle', { cx: TIT.x, cy: TIT.y, r: 0, fill: 'none', stroke: C.CO }, LOGO); LOGO.insertBefore(c, wmG); return { t0, c }; });
|
|
362
|
+
// tagline
|
|
363
|
+
const CN = '每天 聪明一点点', cnFont = "700 70px 'Resource Han Rounded'", CNB = 716, CNLS = 7;
|
|
364
|
+
const cnW = [...CN].map(c => c === ' ' ? 36 : meas(cnFont, c).width); const cnTot = cnW.reduce((a, b) => a + b, 0) + CNLS * (CN.length - 1);
|
|
365
|
+
const cpT = el('clipPath', { id: 'tagClip' }, defs); el('rect', { x: 0, y: CNB - 94, width: 1920, height: 116 }, cpT);
|
|
366
|
+
const cpE = el('clipPath', { id: 'enClip' }, defs); el('rect', { x: 0, y: 752, width: 1920, height: 54 }, cpE);
|
|
367
|
+
const tagG = G(LOGO, { 'clip-path': 'url(#tagClip)' }), enClipG = G(LOGO, { 'clip-path': 'url(#enClip)' });
|
|
368
|
+
let xx = 960 - cnTot / 2;
|
|
369
|
+
const CNCH = [...CN].map((c, i) => { const g = G(tagG); const tx = el('text', { x: 0, y: 0, fill: C.CR, style: `font:${cnFont}` }, g); tx.textContent = c; const o = { g, x: xx, t0: T('tagCN') + i * F(1.2) }; xx += cnW[i] + CNLS; return o; });
|
|
370
|
+
const DOTBOX = (() => { const c2 = document.createElement('canvas'); c2.width = 100; c2.height = 110; const x2 = c2.getContext('2d'); x2.font = cnFont; x2.fillStyle = '#000'; x2.fillText('点', 10, 80);
|
|
371
|
+
const d = x2.getImageData(0, 0, 100, 110).data, ink = (x, y) => d[(y * 100 + x) * 4 + 3] > 90, row = y => { for (let x = 0; x < 100; x++) if (ink(x, y)) return true; return false; };
|
|
372
|
+
let yb = 109; while (yb > 0 && !row(yb)) yb--; const y1 = yb; while (yb > 0 && row(yb)) yb--; const y0 = yb + 1;
|
|
373
|
+
const boxes = []; let run = null; for (let x = 0; x <= 100; x++) { let h = false; if (x < 100) for (let y = y0; y <= y1; y++) if (ink(x, y)) { h = true; break; }
|
|
374
|
+
if (h && !run) run = { x0: x }; if (!h && run) { run.x1 = x - 1; boxes.push(run); run = null; } }
|
|
375
|
+
return boxes.map(b => { let a = 999, z = -1; for (let y = y0; y <= y1; y++) for (let x = b.x0; x <= b.x1; x++) if (ink(x, y)) { a = Math.min(a, y); z = Math.max(z, y); } return { x0: b.x0 - 10 - 2, x1: b.x1 - 10 + 2, y0: a - 80 - 2, y1: z - 80 + 2 }; }); })();
|
|
376
|
+
const CNDOTS = []; CNCH.forEach((c, ci) => { if (CN[ci] !== '点') return; DOTBOX.forEach((b, bi) => { const id = `cnd${ci}_${bi}`; const cp = el('clipPath', { id }, defs); el('rect', { x: b.x0, y: b.y0, width: b.x1 - b.x0, height: b.y1 - b.y0 }, cp);
|
|
377
|
+
const g = G(c.g), inner = G(g); const tx = el('text', { x: 0, y: 0, fill: C.CO, stroke: C.CO, 'stroke-width': 1.4, 'clip-path': `url(#${id})`, style: `font:${cnFont}` }, inner); tx.textContent = '点';
|
|
378
|
+
CNDOTS.push({ g, inner, cx: (b.x0 + b.x1) / 2, cy: (b.y0 + b.y1) / 2, sx: c.x + (b.x0 + b.x1) / 2, sy: CNB + (b.y0 + b.y1) / 2, t0: T('cnFlip') + F(CNDOTS.length) }); }); });
|
|
379
|
+
const enG = G(enClipG); { const tx = el('text', { x: 960 + 4, y: 0, 'text-anchor': 'middle', fill: '#CFCFFF', style: "font:600 36px 'Fredoka'; letter-spacing:10px" }, enG); tx.textContent = 'EVERY DAY, A LITTLE WISER';
|
|
380
|
+
if (V5) { tx.setAttribute('x', 960 + 9); tx.setAttribute('style', "font:600 46px 'Fredoka'; letter-spacing:18px"); tx.textContent = 'FLAT VECTOR'; // v5: the EN style line replaces the tagline
|
|
381
|
+
const cr = cpE.firstChild; cr.setAttribute('y', T5.enB - 52); cr.setAttribute('height', 66); vis(tagG, false); } }
|
|
382
|
+
const landRays = [-168, -128, -100, -80, -52, -12].map((d, i) => ({ a: d * Math.PI / 180, len: [76, 104, 88, 110, 92, 72][i], ln: el('line', { stroke: C.SU, 'stroke-width': 14, 'stroke-linecap': 'round' }, LOGO) }));
|
|
383
|
+
const dotL = el('circle', { cx: 0, cy: 0, r: 46, fill: C.CO }, LOGO);
|
|
384
|
+
const bigR = el('circle', { cx: TIT.x, cy: TIT.y, r: 0, fill: 'none', stroke: C.CO }, LOGO); LOGO.insertBefore(bigR, wmG);
|
|
385
|
+
const SIG = [[C.CR, 0, 0], [C.MI, 1, 0], [C.SU, 1, 1], [C.CO, 0, 1]].map(([c, i, j], k) => ({ x: 1738 + i * 26, y: 930 + j * 26, t0: T('tagEN') + F(8 + k), c: el('circle', { cx: 0, cy: 0, r: 9, fill: c }, LOGO) }));
|
|
386
|
+
const spark = el('circle', { cx: 0, cy: 0, r: 13, fill: C.CO }, LOGO), sparkRing = el('circle', { cx: CNDOTS[0].sx, cy: CNDOTS[0].sy, r: 0, fill: 'none', stroke: C.CO }, LOGO);
|
|
387
|
+
const TOPL = G(S); zipD.forEach(z => TOPL.appendChild(z));
|
|
388
|
+
// R2 MATCH CUT: the TL bar + its coral cap become the wordmark's dotless i + tittle
|
|
389
|
+
const IB = V5 ? T5.bar : (() => { const L = LET[4], w = meas(wfont, 'ı').width; return { x0: L.cx - w / 2 - mI.actualBoundingBoxLeft, x1: L.cx - w / 2 + mI.actualBoundingBoxRight, y0: WBASE - mI.actualBoundingBoxAscent, y1: WBASE + mI.actualBoundingBoxDescent }; })();
|
|
390
|
+
const CL = { ca: T('collapseAntic'), c1: F(236), zd: F(3), x0: F(237), x1: F(245) }, ZIP0 = [0, F(235), F(236), F(237)];
|
|
391
|
+
const morphP = t => E.ioC(inv(CL.x0, CL.x1, t));
|
|
392
|
+
const stemBox = p => ({ x0: lerp(558, IB.x0, p), x1: lerp(642, IB.x1, p), y0: lerp(170, IB.y0, p), y1: lerp(460, IB.y1, p), rx: lerp(24, V5 ? (IB.y1 - IB.y0) * .45 : (IB.x1 - IB.x0) * .3, p) });
|
|
393
|
+
function capPos(t) { const p = morphP(t), b = stemBox(p), r = lerp(R_D, DOT_R, p); return { x: V5 ? lerp((b.x0 + b.x1) / 2, TIT.x, p) : (b.x0 + b.x1) / 2, y: b.y0 - r - 2, r }; }
|
|
394
|
+
// v5: 扁 squashes on the dot's anticipation / landing (60 % of the ı stem squash, about the baseline); the resting dot rides its top bar
|
|
395
|
+
const stem5 = t => { const dl = T('dotLand2'), lau = T('dotRise'), an = t < lau ? E.outQ(inv(CL.x1, lau, t)) : 0; return .6 * (.12 * kick(t, dl, 3.5, .4) + .07 * an - .05 * kick(t, lau, 4, .4)); };
|
|
396
|
+
const STEM5_OFF = F(248); // v5: the morphing bar is hidden once 扁 has popped past its first overshoot
|
|
397
|
+
const coinTh = t => 2 * Math.PI * E.ioQ(inv(QD[3].t0, QD[3].t0 + F(8), t));
|
|
398
|
+
|
|
399
|
+
// ============================================================ helpers for animated strokes
|
|
400
|
+
function burstLine(ln, cx, cy, a, r0, r1, t, t0, dur = F(10)) {
|
|
401
|
+
const h = E.outC(inv(t0, t0 + dur * .7, t)), tl = E.outC(inv(t0 + dur * .3, t0 + dur, t));
|
|
402
|
+
if (t < t0 || tl >= 1) { vis(ln, false); return; } vis(ln, true);
|
|
403
|
+
const ra = lerp(r0, r1, tl), rb = lerp(r0, r1, h);
|
|
404
|
+
ln.setAttribute('x1', cx + Math.cos(a) * ra); ln.setAttribute('y1', cy + Math.sin(a) * ra); ln.setAttribute('x2', cx + Math.cos(a) * rb); ln.setAttribute('y2', cy + Math.sin(a) * rb);
|
|
405
|
+
}
|
|
406
|
+
function ring(c, t, t0, r0, r1, w0, dur = F(12)) { const u = inv(t0, t0 + dur, t); if (t < t0 || u >= 1) { vis(c, false); return; } vis(c, true); const w = w0 * (1 - u) ** 1.15; if (w < 1.5) { vis(c, false); return; } c.setAttribute('r', lerp(r0, r1, E.outQ(u))); c.setAttribute('stroke-width', w); }
|
|
407
|
+
|
|
408
|
+
// ============================================================ the hero dot, act 1 (ball -> sun)
|
|
409
|
+
function ballA(t) {
|
|
410
|
+
const ti1 = T('impact1'), ti2 = T('impact2'), ta = T('antic'), tl = T('launch'), yc = GY - R0;
|
|
411
|
+
let x = 960, y = yc, sx = 1, sy = 1, rot = 0, r = R0, sh = 1;
|
|
412
|
+
if (t < ti1) { const g = 2040, a = -0.12; y = yc - 0.5 * g * ((ti1 - a) ** 2 - (t - a) ** 2); const k = Math.min(g * (t - a) / 1500, 1) * .16; sy = 1 + k; sx = 1 / (1 + k); sh = clamp(1 - (yc - y) / 520); }
|
|
413
|
+
else if (t < ti2) { const c = F(3), hopT = ti2 - ti1 - c;
|
|
414
|
+
if (t < ti1 + c) { const q = Math.sin(Math.PI * (t - ti1) / c); sy = 1 - .4 * q; sx = 1 + .36 * q; y = GY - R0 * sy; }
|
|
415
|
+
else { const u = (t - ti1 - c) / hopT; y = yc - 78 * hump(u); const k = Math.abs(1 - 2 * u) * .09; sy = 1 + k; sx = 1 / (1 + k); sh = 1 - .35 * hump(u); } }
|
|
416
|
+
else if (t < tl) { const c = F(2.5);
|
|
417
|
+
if (t < ti2 + c) { const q = Math.sin(Math.PI * (t - ti2) / c); sy = 1 - .2 * q; sx = 1 + .18 * q; }
|
|
418
|
+
const a = E.outQ(inv(ta, tl, t)); sy *= 1 - .3 * a; sx *= 1 + .24 * a; y = GY - R0 * sy; }
|
|
419
|
+
else { const u = inv(tl, T('sunLand'), t); const p = E.outQt(u);
|
|
420
|
+
const bz = q => { const P0 = { x: 960, y: yc }, P1 = { x: 1060, y: 110 }, P2 = SUN; return { x: (1 - q) ** 2 * P0.x + 2 * (1 - q) * q * P1.x + q * q * P2.x, y: (1 - q) ** 2 * P0.y + 2 * (1 - q) * q * P1.y + q * q * P2.y }; };
|
|
421
|
+
const pp = bz(p); x = pp.x; y = pp.y;
|
|
422
|
+
const dt = 1 / 240, p2 = E.outQt(inv(tl, T('sunLand'), t + dt)), pq = bz(p2); const vx = (pq.x - x) / dt, vy = (pq.y - y) / dt, sp = Math.hypot(vx, vy);
|
|
423
|
+
const [s, k] = pop(t, tl, 1.9, .42, .1, 1.2); r = lerp(R0, SUN.r, s);
|
|
424
|
+
const st = clamp(sp / 5200) * .42; rot = Math.atan2(vy, vx) * 180 / Math.PI; sx = 1 + st; sy = 1 / (1 + st);
|
|
425
|
+
if (u >= 1) { rot = 0; sx = sy = 1; }
|
|
426
|
+
sh = clamp(1 - (t - tl) / F(5));
|
|
427
|
+
r *= 1 + 0.012 * Math.sin(2 * Math.PI * .5 * (t - tl)); }
|
|
428
|
+
return { x, y, sx, sy, rot, r, sh };
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// ============================================================ render
|
|
432
|
+
function updWorld(t, cam, tr = t) {
|
|
433
|
+
vis(world, t < T('coreDot') + F(4)); if (t >= T('coreDot') + F(4)) return;
|
|
434
|
+
world.setAttribute('transform', `translate(960 540) scale(${cam.s.toFixed(6)}) translate(${(-cam.cx).toFixed(4)} ${(-cam.cy).toFixed(4)})`);
|
|
435
|
+
const b = ballA(tr); dotW.setAttribute('r', b.r); tf(dotW, b.x, b.y, b.sx, b.sy, b.rot);
|
|
436
|
+
{ const b2 = ballA(tr + 1 / 120); const vx = (b2.x - b.x) * 120, vy = (b2.y - b.y) * 120, sp = Math.hypot(vx, vy); const L = clamp((sp - 100) / 1300) * 190;
|
|
437
|
+
const ux = -vx / (sp || 1), uy = -vy / (sp || 1), nx = -uy, ny = ux;
|
|
438
|
+
speedL.forEach((ln, i) => { const o = [-.3, .3][i], li = L * [1, .78][i]; vis(ln, li > 6 && (t < T('impact1') - 0.004 || (t > T('launch') && t < T('sunLand'))));
|
|
439
|
+
const bx = b.x + nx * o * b.r + ux * (b.r * 1.3 + [0, 24][i]), by = b.y + ny * o * b.r + uy * (b.r * 1.3 + [0, 24][i]);
|
|
440
|
+
ln.setAttribute('x1', bx); ln.setAttribute('y1', by); ln.setAttribute('x2', bx + ux * li); ln.setAttribute('y2', by + uy * li); }); }
|
|
441
|
+
{ const tg = crisp(t, .06), ge = lerp(0, 1900, E.outQ(inv(F(1.5), F(14), tg))), sm = .6 * 1900 * 2 * (1 - inv(F(1.5), F(14), tg)) / 12.5 * (ge > 0 ? 1 : 0);
|
|
442
|
+
groundR.setAttribute('width', ge >= 1899 ? 10000 : ge + 4000); road.setAttribute('width', ge >= 1899 ? 10000 : ge + 4000); lanes.forEach(l => vis(l.e, ge >= 1899 || l.x + 48 < ge)); vis(groundLead, ge > 0 && ge < 1890); groundLead.setAttribute('x', ge - sm); groundLead.setAttribute('width', 170 + sm); }
|
|
443
|
+
SKY.forEach(d => { const [s, k] = pop(t, d.t0, 2.4, .4, .22); const o = inv(d.out, d.out + F(6), t); const so = Math.max(s, 0) * (1 - E.inB(o, 2.2)); vis(d.g, t >= d.t0 && o < 1);
|
|
444
|
+
if (t >= d.t0 && o < 1) tf(d.g, d.x + 5 * Math.sin(1.3 * t + d.x), d.y + 5 * Math.cos(1.1 * t + d.y), Math.max(so, 0.001) * (1 + k), Math.max(so, 0.001) / (1 + k), (d.r0 || 0) + d.rs * (t - d.t0) * 10 - 40 * (1 - s)); });
|
|
445
|
+
IMP.forEach(im => { im.lines.forEach((l, i) => burstLine(l.ln, 960, GY - 6, l.a, 100 + 12 * (i % 2), 100 + [150, 100, 196, 196, 100, 150][i] * im.sc, t, im.t0, F(10)));
|
|
446
|
+
im.rips.forEach((rp, j) => { const t0 = im.t0 + F(3 * j), u = inv(t0, t0 + F(12), t); vis(rp, t >= t0 && u < 1); const rx = lerp(100, 100 + (300 - 60 * j) * im.sc, E.outC(u));
|
|
447
|
+
rp.setAttribute('rx', rx); rp.setAttribute('ry', rx * .13); rp.setAttribute('stroke-width', (15 - 5 * j) * im.sc * (1 - E.outQ(u))); }); });
|
|
448
|
+
const hgt = GY - R0 - b.y; vis(shadow, b.sh > 0.01 && t > F(3)); shadow.setAttribute('rx', 68 * b.sh * (1 - clamp(hgt / 900) * .5) * b.sx); shadow.setAttribute('ry', 9 * b.sh);
|
|
449
|
+
rays.forEach((ln, i) => { const a = i / 12 * Math.PI * 2 + t * .14; burstLine(ln, 0, 0, 0, 0, 0, -1, 0); const t0 = T('rays') + i * F(.6); const u = E.outB(inv(t0, t0 + F(9), t), 2.2);
|
|
450
|
+
if (t < t0) { vis(ln, false); return; } vis(ln, true); const r0 = SUN.r + 26, r1 = r0 + 40 * u;
|
|
451
|
+
ln.setAttribute('x1', SUN.x + Math.cos(a) * r0); ln.setAttribute('y1', SUN.y + Math.sin(a) * r0); ln.setAttribute('x2', SUN.x + Math.cos(a) * r1); ln.setAttribute('y2', SUN.y + Math.sin(a) * r1); });
|
|
452
|
+
far.forEach(f => { const [s, k] = pop(t, f.t0, 2.0, .5, .1); tf(f.g, f.x, GY + 2, 1 / (1 + k * .5), Math.max(s, 0) * (1 + k)); });
|
|
453
|
+
CLOUDS.forEach(c => { const u = E.outQt(inv(c.t0, c.t0 + F(26), t)); tf(c.g, lerp(c.from, c.x, u) + (t - c.t0) * 9 * (u > 0 ? 1 : 0), c.y, c.s * (1 + .04 * kick(t, c.t0 + F(20), 2, .5)), c.s); });
|
|
454
|
+
BIRDS.forEach(bd => { const t0 = T('birds') + bd.i * F(3); const u = inv(t0, t0 + F(60), t); vis(bd.g, false);
|
|
455
|
+
const x = lerp(-90 - bd.i * 80, 1250 - bd.i * 70, u), y = 250 + bd.i * 38 + 12 * Math.sin(2 * Math.PI * 1.1 * t + bd.i) - 60 * u;
|
|
456
|
+
const a = 32 * Math.sin(2 * Math.PI * 4.2 * t + bd.i * 1.7) - 6; tf(bd.g, x, y, bd.s); tf(bd.wl, 0, 0, 1, 1, a); tf(bd.wr, 0, 0, 1, 1, -a); });
|
|
457
|
+
BLD.forEach((bl, i) => { const [s, k] = pop(t, bl.t0, 2.3, .4, .22); tf(bl.g, bl.cx, GY, 1 / (1 + k), Math.max(s, 0) * (1 + k)); tf(slabs[i], bl.cx, GY, Math.max(s, 0) / (1 + k), Math.max(s, 0));
|
|
458
|
+
if (bl.tip) bl.tip.setAttribute('r', 9 * (0.75 + 0.25 * Math.abs(Math.sin(t * 3.2)))); });
|
|
459
|
+
{ const u = inv(1.62, 2.85, t), e = u * u * (3 - 2 * u), x = lerp(-320, 2260, e), v = 6 * u * (1 - u) / 1.23 * 2580; vis(bus, u > 0 && u < 1);
|
|
460
|
+
tf(bus, x, 900); tf(busB, 0, -3 * Math.abs(Math.sin(2 * Math.PI * 5.5 * t)), 1 + .6 * (v / 3146) ** 12, 1 - .12 * (v / 3146) ** 12, -2.2 * (1 - 2 * u) * (u < .5 ? 1 : .5));
|
|
461
|
+
busW.forEach(w => tf(w.g, w.x, 0, 1, 1, (x / 17) * 180 / Math.PI)); }
|
|
462
|
+
lits.forEach(l => { const [s] = pop(t, l.t0, 3, .42); tf(l.g, l.x, l.y, Math.max(s, 0)); });
|
|
463
|
+
TREES.forEach(tr => { const [s, k] = pop(t, tr.t0, 2.8, .38, .2); tf(tr.g, tr.x, GY, Math.max(s, 0) / (1 + k), Math.max(s, 0) * (1 + k), 2.5 * Math.sin(2 * Math.PI * .7 * t + tr.x)); });
|
|
464
|
+
// hero window: blind snaps up, sill + sparks ping
|
|
465
|
+
const hw = T('heroWin'), bu = E.outC(inv(hw, hw + F(5), t)); blind.setAttribute('height', HW.h * (1 - bu)); vis(blind, bu < 1); blind.setAttribute('fill', t >= F(78) ? C.SU : C.INKW);
|
|
466
|
+
const [ss] = pop(t, hw, 3, .45); tf(sill, 0, 0, 1, 1); sill.setAttribute('width', (HW.w + 10) * Math.max(ss, 0)); sill.setAttribute('x', HL.x + HW.w / 2 - (HW.w + 10) * Math.max(ss, 0) / 2);
|
|
467
|
+
heroSparks.forEach((ln, i) => { const a = [-.75, -.25, .25, .75][i] * Math.PI + (i < 2 ? 0 : 0); const ang = [-2.4, -.75, .75, 2.4][i];
|
|
468
|
+
burstLine(ln, HL.x + HW.w / 2, HL.y + HW.h / 2, [-2.5, -.64, .64, 2.5][i] + (i > 1 ? 0 : 0), 44, 64, t, hw + F(1), F(9)); });
|
|
469
|
+
}
|
|
470
|
+
function updInterior(t, cam) {
|
|
471
|
+
if (t >= T('coreDot') + F(4)) return;
|
|
472
|
+
// lamp sway, steam
|
|
473
|
+
tf(lamp, 668, 0, 1, 1, 2.2 * Math.sin(2 * Math.PI * .6 * t) + 3 * kick(t, T('arrive'), 1.2, .25));
|
|
474
|
+
steam.forEach((p, i) => p.setAttribute('stroke-dashoffset', -((t * 150 + i * 150) % 306)));
|
|
475
|
+
plant.setAttribute('transform', `translate(1640 668) rotate(${1.5 * Math.sin(2 * Math.PI * .5 * t)})`);
|
|
476
|
+
const tp = T('ping'), ra = T('reactAntic'), rs = T('headSnap');
|
|
477
|
+
// character rig
|
|
478
|
+
const typing = 1 - inv(tp, tp + F(4), t);
|
|
479
|
+
const lean = 3.5 * pop(t, rs, 2, .4)[0];
|
|
480
|
+
const antic = E.outQ(inv(ra, rs, t)) * (1 - inv(rs, rs + F(2), t));
|
|
481
|
+
tf(CH, 860, 760 + 4 * antic, 1 + .02 * antic, 1 - .03 * antic, lean);
|
|
482
|
+
const nod = typing * 6 * Math.pow(1 - Math.abs(Math.sin(2 * Math.PI * t)), 4);
|
|
483
|
+
armL.sh.setAttribute('transform', `translate(-146 -150) rotate(${14 + 1.5 * Math.sin(2 * Math.PI * 5.5 * t) * typing - 6 * lean / 3.5})`);
|
|
484
|
+
armL.fo.setAttribute('transform', `translate(0 116) rotate(${-98 + 7 * Math.sin(2 * Math.PI * 5.5 * t) * typing})`);
|
|
485
|
+
// acting: after the "!" she reaches for the phone (anticipation tuck, overshooting reach) just before the bubble swells into the wipe
|
|
486
|
+
const rch = E.outB(inv(T('exclaim') + F(3), T('exclaim') + F(10), t), 1.8), tuck = hump(inv(T('exclaim'), T('exclaim') + F(4), t));
|
|
487
|
+
armR.sh.setAttribute('transform', `translate(146 -150) rotate(${lerp(-14 + 1.5 * Math.sin(2 * Math.PI * 5.5 * t + 2) * typing + 4 * lean / 3.5 + 8 * tuck, -84, rch)})`);
|
|
488
|
+
armR.fo.setAttribute('transform', `translate(0 116) rotate(${lerp(98 + 7 * Math.sin(2 * Math.PI * 5.5 * t + 2) * typing + 10 * tuck, 10, rch)})`);
|
|
489
|
+
const [hs] = pop(t, rs, 2.6, .35, 0, 1.4);
|
|
490
|
+
const hr = -4 * antic + 11 * Math.max(hs, 0) * (t > rs ? 1 : 0);
|
|
491
|
+
tf(neck, 8 * hs * (t > rs ? 1 : 0), -200 + nod * .4 + 6 * antic, 1, 1, hr * .35);
|
|
492
|
+
tf(head, 0, -86 + nod + 8 * antic, 1, 1, hr * .65);
|
|
493
|
+
const bl = T('blink'); const blink = Math.max(hump(inv(bl, bl + F(4), t)), hump(inv(F(160), F(164), t)));
|
|
494
|
+
const wide = t > rs ? 1 + .22 * Math.max(hs, 0) : 1, look = t > rs ? 9 * hs : 0;
|
|
495
|
+
eyes.forEach(e => tf(e.g, e.x + look, 10, wide, wide * (1 - .9 * blink) * (1 - .65 * antic)));
|
|
496
|
+
brows.forEach(b => tf(b, 0, -12 * (t > rs ? hs : 0) + 4 * antic));
|
|
497
|
+
const got = T('exclaim') + F(10), surprised = t > rs && t < got; vis(smile, !surprised); vis(oMouth, surprised); if (surprised) tf(oMouth, look * .5, 0, Math.max(hs, .01));
|
|
498
|
+
{ const [gs] = pop(t, got, 3, .45, 0); tf(smile, look * .5, t >= got ? -6 * Math.max(gs, 0) : 0, t >= got ? 1 + .45 * Math.max(gs, 0) : 1, t >= got ? 1 + .6 * Math.max(gs, 0) : 1); }
|
|
499
|
+
bunG.setAttribute('transform', `rotate(${f3(-8 * kick(t, rs, 2.4, .3) + 3 * kick(t, T('reactAntic'), 2.6, .4))} 0 -95)`);
|
|
500
|
+
vis(glint, t >= T('exclaim') && t < T('exclaim') + F(2));
|
|
501
|
+
armL.fg.setAttribute('transform', 'translate(0 118) rotate(0)'); armR.fg.setAttribute('transform', `translate(8 118) rotate(${f3(-30 * E.outB(inv(got - F(1), got + F(3), t), 2))})`);
|
|
502
|
+
// phone + bubble
|
|
503
|
+
const vib = t > tp ? (1 - inv(tp, tp + F(12), t)) : 0;
|
|
504
|
+
const bz = t >= tp && t < tp + F(6) ? 3 * Math.cos(2 * Math.PI * 15 * (t - tp)) : 0; tf(phone, 1300 + bz, 740 - 4 * kick(t, T('exclaim') + F(10), 4, .4), 1, 1, 2 * bz / 3);
|
|
505
|
+
vis(screenLit, t >= tp); if (t >= tp) { const [s] = pop(t, tp, 4, .5); tf(screenLit, 0, -127 * (1 - s), 1, s); }
|
|
506
|
+
buzz.forEach((b, i) => { const on = t >= tp && t < tp + F(13); vis(b, on); if (on) { const [s] = pop(t, tp, 4, .4); tf(b, 1300, 740 - 10 * Math.sin(2 * Math.PI * 12 * t), Math.max(s, .01) * (1 - E.inB(inv(tp + F(9), tp + F(13), t)))); } });
|
|
507
|
+
const bt = tp + F(1), ba = T('bubbleAntic'), wt = T('wipe');
|
|
508
|
+
vis(bubble, t >= bt && t < wt);
|
|
509
|
+
if (t >= bt) { const [s, k] = pop(t, bt, 2.6, .38, .25); const an = E.ioQ(inv(ba, wt, t)); const grow = E.inC(inv(wt, wt + F(3), t));
|
|
510
|
+
tf(bubble, BUB.x, BUB.y + 6 * an - 34 * E.outQ(inv(T('exclaim') + F(10), wt, t)), Math.max(s, 0) / (1 + k) * (1 + .12 * an) * (1 + grow * .3), Math.max(s, 0) * (1 + k) * (1 - .18 * an) * (1 + grow * .3), 3 * Math.sin(2 * Math.PI * 1.2 * (t - bt)) * (1 - an));
|
|
511
|
+
tf(bell, 0, -84, 1 - E.inB(inv(wt, wt + F(3), t)), 1 - E.inB(inv(wt, wt + F(3), t)), 20 * kick(t, bt + F(3), 4.5, .22)); }
|
|
512
|
+
pingRings.forEach((c, i) => ring(c, t, bt + F(3 + i * 6), 64, 130, 12, F(12)));
|
|
513
|
+
{ const [s, k] = pop(t, T('exclaim'), 3, .35, .3); vis(exclaim, t >= T('exclaim')); tf(exclaim, 1036, 262, Math.max(s, 0) / (1 + k), Math.max(s, 0) * (1 + k), 12 - 4 * s); }
|
|
514
|
+
// shape wipe: coral -> mint -> cream from the bubble
|
|
515
|
+
{ const tc = crisp(t, 0), BC = { x: BUB.x, y: BUB.y - 84 };
|
|
516
|
+
const t1 = T('wipe'), u1 = inv(t1, t1 + F(10), tc); vis(W1, t >= t1);
|
|
517
|
+
if (t >= t1) { const k = Math.exp(lerp(Math.log(1.12), Math.log(34), E.ioQ(u1))), L = 84 + 120 * E.outQ(inv(t1, t1 + F(5), tc)), a0 = 62 * Math.PI / 180;
|
|
518
|
+
w1t.setAttribute('d', `M${f3(-58 * Math.cos(a0))} ${f3(58 * Math.sin(a0))} L0 ${f3(L)} L${f3(58 * Math.cos(a0))} ${f3(58 * Math.sin(a0))} Z`);
|
|
519
|
+
tf(W1, BC.x - 160 * E.ioQ(u1), BC.y + 60 * E.ioQ(u1), k, k, 175 * E.outC(u1)); }
|
|
520
|
+
const t2 = T('wipe2'), u2 = inv(t2, t2 + F(9), tc); vis(W2, t >= t2);
|
|
521
|
+
if (t >= t2) { const k = Math.exp(lerp(Math.log(.03), 0, E.ioQ(u2))); tf(W2, lerp(BC.x, 1060, E.outQ(u2)), lerp(BC.y, 500, E.outQ(u2)), k, k, 25 * (1 - E.outB(u2, 1.6))); }
|
|
522
|
+
const t3 = T('wipe3'), sl = T('slam'); vis(W3, t >= t3);
|
|
523
|
+
if (t >= t3) { const u3 = inv(t3, sl, tc), kk = kick(tc, sl, 3.5, .4); tf(W3, 1060, -110 - 2300 * (1 - E.inQ(u3)) + 40 * kk, 1, 1 - .06 * kk, 7 * (1 - u3)); } }
|
|
524
|
+
|
|
525
|
+
}
|
|
526
|
+
function arcP(P0, P1, H, q) { const cx = (P0.x + P1.x) / 2, cy = Math.min(P0.y, P1.y) - H; return { x: (1 - q) ** 2 * P0.x + 2 * (1 - q) * q * cx + q * q * P1.x, y: (1 - q) ** 2 * P0.y + 2 * (1 - q) * q * cy + q * q * P1.y }; }
|
|
527
|
+
const barS = t => Math.max(pop(t, QD[0].t0 + F(1), 3.2, .42, 0)[0], 0);
|
|
528
|
+
const capY = t => 440 - 270 * barS(t) - R_D - 2;
|
|
529
|
+
function penP(t) { const p = E.ioQ(inv(QD[1].t0 + F(1), QD[1].t0 + F(6), t)); const l1 = Math.hypot(105, 105), l2 = Math.hypot(220, 230), d = p * (l1 + l2);
|
|
530
|
+
return d < l1 ? { x: 1280 + 105 * d / l1, y: 310 + 105 * d / l1, p } : { x: 1385 + 220 * (d - l1) / l2, y: 415 - 230 * (d - l1) / l2, p }; }
|
|
531
|
+
const restPos = (k, t) => k === 0 ? CEN : k === 1 ? { x: 600, y: capY(t) } : k === 2 ? penP(t) : QD[2].K;
|
|
532
|
+
function hero(t) {
|
|
533
|
+
const cd = T('coreDot'), tc0 = T('coreHit');
|
|
534
|
+
if (!(HOPS.some(h => t >= h.o && t < h.l - F(.5)) || (t >= cd && t < tc0 - F(.5)))) t = crisp(t, 0); // R2: crisp except in flight
|
|
535
|
+
if (t < cd || t >= HOPS[3].l) return null;
|
|
536
|
+
let k = 0; while (k < 4 && t >= HOPS[k].o) k++;
|
|
537
|
+
if (k > 0 && t < HOPS[k - 1].l) { const h = HOPS[k - 1], P0 = restPos(k - 1, h.o), P1 = QD[k - 1].L;
|
|
538
|
+
const p = arcP(P0, P1, h.H, inv(h.o, h.l, t)), pq = arcP(P0, P1, h.H, inv(h.o, h.l, t + 1 / 240)); const vx = (pq.x - p.x) * 240, vy = (pq.y - p.y) * 240;
|
|
539
|
+
const st = 1 + clamp(Math.hypot(vx, vy) / 5000) * .22; return { x: p.x, y: p.y, sx: st, sy: 1 / st, rot: Math.atan2(vy, vx) * 180 / Math.PI }; }
|
|
540
|
+
if (k === 3) return null; // inside the heart
|
|
541
|
+
if (k === 0) { // R2: the dot SQUIRTS out of the slammed half-disc, rises 170 px, drops back onto the centre cross (contact cracks the grid open)
|
|
542
|
+
if (t < tc0) { const u = inv(cd, tc0, t), v = Math.abs(1 - 2 * u), st = 1 + .5 * v * v, e = Math.min(1, .3 + inv(cd, cd + F(2), t) * .7);
|
|
543
|
+
return { x: CEN.x, y: CEN.y - 170 * 4 * u * (1 - u), sx: e / st, sy: e * st, rot: 0 }; }
|
|
544
|
+
const an = E.outQ(inv(tc0, tc0 + F(1), t)), sy = 1 - .3 * an, sx = 1 + .24 * an; return { x: CEN.x, y: CEN.y + R_D * (1 - sy), sx, sy, rot: 0 }; }
|
|
545
|
+
const h = HOPS[k], p = restPos(k, t), s = k === 0 ? Math.max(pop(t, cd, 3, .45, 0)[0], 0) : 1;
|
|
546
|
+
const an = E.outQ(inv(h.a, h.o, t)), land = k > 0 ? kick(t, HOPS[k - 1].l, 3.5, .4) : 0;
|
|
547
|
+
const sy = (1 - .3 * an) * (1 - .22 * land), sx = (1 + .2 * an) * (1 + .2 * land);
|
|
548
|
+
return { x: p.x, y: p.y + R_D * (1 - sy), sx: s * sx, sy: s * sy, rot: 0 };
|
|
549
|
+
}
|
|
550
|
+
function updIcons(t, cam, tr = t) {
|
|
551
|
+
const on = t >= T('coreDot') && t <= CL.x1; vis(ICO, on);
|
|
552
|
+
// R2 critical fix: zip dots are handled FIRST and are hidden unless in flight (no early return can strand one at the origin)
|
|
553
|
+
zipD.forEach((z, i) => { const z0 = ZIP0[i], u = i ? inv(z0, z0 + CL.zd, tr) : 1, sh = i > 0 && tr >= z0 - F(.5) && u < 1; vis(z, sh); if (!sh) return;
|
|
554
|
+
const q = QD[i], cp = capPos(tr), e = u ** 1.8, e2 = Math.min(1, (u + .08) ** 1.8), x = lerp(q.K.x, cp.x, e), y = lerp(q.K.y, cp.y, e);
|
|
555
|
+
const vx = lerp(q.K.x, cp.x, e2) - x, vy = lerp(q.K.y, cp.y, e2) - y, st = 1 + clamp(Math.hypot(vx, vy) / 60) * .35;
|
|
556
|
+
z.setAttribute('r', f3(lerp(R_D, cp.r * .8, e))); tf(z, x, y, st, 1 / st, Math.atan2(vy, vx) * 180 / Math.PI); });
|
|
557
|
+
if (!on) return;
|
|
558
|
+
const tc0 = T('coreHit'), cu1 = E.inQ(inv(CL.ca, CL.c1, t));
|
|
559
|
+
// the dot's contact on the centre cross cracks the checker open (R2)
|
|
560
|
+
CHK.forEach((g, i) => { const s = Math.max(pop(t, tc0 + F(i * .5), 3.2, .5, 0)[0], 0); vis(g, s > .002); tf(g, 960, 540, s, s); });
|
|
561
|
+
QD.forEach((q, i) => { const [s] = pop(t, tc0 + F(1 + i * 1.2), 3, .45, 0), g = Math.max(s, 0) * (1 - cu1); vis(q.gh, g > .01); tf(q.gh, q.lx, q.ly, g, g); });
|
|
562
|
+
const hd = hero(tr); vis(heroD, !!hd); if (hd) tf(heroD, hd.x, hd.y, hd.sx, hd.sy, hd.rot);
|
|
563
|
+
QD.forEach((q, i) => {
|
|
564
|
+
const t0 = q.t0, live = t >= t0; vis(q.g, live); if (!live) return;
|
|
565
|
+
const c = i === 0 ? 0 : cu1; // TL is NOT collapsed: its field becomes the lockup background (match cut)
|
|
566
|
+
if (c <= 0) { q.burst.setAttribute('cx', q.L.x); q.burst.setAttribute('cy', q.L.y); q.burst.setAttribute('r', Math.max(0, q.RL * spring(t - t0, 2.6, .55))); }
|
|
567
|
+
else { q.burst.setAttribute('cx', q.K.x); q.burst.setAttribute('cy', q.K.y); q.burst.setAttribute('r', q.RK * (1 - c)); }
|
|
568
|
+
const cs = 1 - c; vis(q.ic, cs > .002); q.ic.setAttribute('transform', `translate(${q.K.x} ${q.K.y}) scale(${Math.max(cs, 1e-4)}) translate(${-q.K.x} ${-q.K.y})`);
|
|
569
|
+
{ const [s] = pop(t, t0 + F(2), 3, .45, 0); const ls = Math.max(s, 0) * (1 - cu1); vis(q.lab, ls > .01); tf(q.lab, q.lx, q.ly, ls, ls); }
|
|
570
|
+
q.dashes.forEach(d => burstLine(d.ln, q.L.x, q.L.y, d.a, q.dr0, q.dr0 + 96, t, t0, F(6)));
|
|
571
|
+
let kd = 0; if (q.k === 'bar' && t >= HOPS[1].o) kd = 1; if (q.k === 'check' && t >= HOPS[2].o) kd = 1;
|
|
572
|
+
if ((q.k === 'heart' || q.k === 'coin') && cu1 > 0) kd = Math.max(pop(t, CL.ca, 4, .5, 0)[0], 0);
|
|
573
|
+
const kEnd = i === 0 ? CL.x0 : ZIP0[i]; vis(q.keyD, kd > 0 && t < kEnd); if (kd > 0) { const ky = q.k === 'bar' ? capY(t) : q.K.y; q.keyD.setAttribute('cy', ky); q.keyD.setAttribute('r', R_D * kd); }
|
|
574
|
+
if (q.k === 'bar') { const b = barS(t), sk = 1 - cu1; q.bars.forEach((bg, j) => { const [bs, bk] = j === 2 ? [b, 0] : pop(t, t0 + F(3 + j * 2), 3, .4, .18); vis(bg.g, j === 2 || sk > .002); tf(bg.g, bg.x, 440, 1 / (1 + bk), Math.max(bs, 0) * (1 + bk) * (j === 2 ? 1 : sk)); });
|
|
575
|
+
const bsc = Math.max(pop(t, t0, 3.4, .5, 0)[0], 0) * sk; vis(q.base, bsc > .002); tf(q.base, 480, 0, bsc, 1); }
|
|
576
|
+
if (q.k === 'check') q.stroke.setAttribute('stroke-dashoffset', 1 - penP(t).p);
|
|
577
|
+
if (q.k === 'heart') { const [m] = pop(t, t0, 3, .45, 0), mm = Math.max(m, 0);
|
|
578
|
+
q.hc.forEach((c, j) => { c.setAttribute('cx', (j ? 78 : -78) * mm); c.setAttribute('cy', lerp(0, -62, mm)); c.setAttribute('r', lerp(R_D, 110, mm)); });
|
|
579
|
+
tf(q.hd, 0, lerp(0, 16, mm), lerp(60, 220, mm), lerp(60, 220, mm), 45);
|
|
580
|
+
const h3 = HOPS[3], an = E.outQ(inv(h3.a, h3.o, t)) * (t < h3.o ? 1 : 0), kb = .16 * kick(t, h3.o, 3.4, .32) + .08 * kick(t, t0 + F(7), 3.4, .32);
|
|
581
|
+
tf(q.hg, 1440, 810 + 14 * an, (1 + .1 * an) * (1 + kb), (1 - .14 * an) * (1 + kb * .8)); }
|
|
582
|
+
if (q.k === 'coin') { const [g] = pop(t, t0, 2.6, .48, 0), R = lerp(R_D, 180, Math.max(g, 0)), th = 2 * Math.PI * E.ioQ(inv(t0, t0 + F(8), t)), c = Math.cos(th);
|
|
583
|
+
const sxF = Math.max(Math.abs(c), .03) * R / 180; vis(q.fDot, th < Math.PI / 2); vis(q.fFront, th >= Math.PI / 2 && c >= 0); vis(q.fBack, th >= Math.PI / 2 && c < 0);
|
|
584
|
+
q.rimC.setAttribute('fill', th < Math.PI / 2 ? C.COD : C.SUE); vis(q.rim, Math.abs(c) < .985);
|
|
585
|
+
tf(q.face, 0, 0, sxF, R / 180); tf(q.rim, 20 * Math.sin(th) * R / 180, 0, sxF, R / 180); tf(q.cg, 480, 810); }
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
// LOGO camera zoom and the cap / tittle dot as pure functions of t (used by updLogo and by the v5 linkAt)
|
|
589
|
+
function logoZ(t) { const dl = T('dotLand2'), push = t < dl ? .22 * E.inC(inv(T('dotDrop'), dl, t)) : .22 * (1 - spring(t - dl, 4.5, .48)); return (1 + push) * (1 + .045 * E.ioQ(inv(dl, 10.4, t))); }
|
|
590
|
+
function logoDot(t, tr = t) {
|
|
591
|
+
const dl = T('dotLand2'), dA = T('dotApex'), dD = T('dotDrop'), lau = T('dotRise'), land = kick(t, dl, 3.2, .38);
|
|
592
|
+
// the cap: absorbs the three zip dots, squashes on the finished stem, snaps up, hangs, drops ON 8.50 (raw time only in flight)
|
|
593
|
+
const AP = { x: TIT.x, y: TIT.y - 210 }, cp1 = capPos(CL.x1), fly = tr >= lau - F(.5) && tr < dl - F(.5), td = fly ? tr : t;
|
|
594
|
+
let x = TIT.x, y = TIT.y, r = DOT_R, sx = 1, sy = 1, rot = 0;
|
|
595
|
+
if (td < CL.x1) { const cp = capPos(td); x = cp.x; r = cp.r; let sq = 0; for (let i = 1; i < 4; i++) sq += .26 * kick(td, ZIP0[i] + CL.zd, 4, .4); sy = 1 - sq; sx = 1 + sq; y = cp.y + r * (1 - sy); }
|
|
596
|
+
else if (td < lau) { const a2 = E.outQ(inv(CL.x1, lau, td)); x = cp1.x; r = cp1.r; sy = 1 - .3 * a2; sx = 1 + .24 * a2; y = cp1.y + r * (1 - sy); }
|
|
597
|
+
else if (td < dA) { const yy = q => lerp(cp1.y, AP.y, E.outQ(q)); x = AP.x; y = yy(inv(lau, dA, td)); const vy = (yy(inv(lau, dA, td + 1 / 240)) - y) * 240, st = 1 + clamp(Math.abs(vy) / 4000) * .25; sx = 1 / st; sy = st; }
|
|
598
|
+
else if (td < dD) { x = AP.x; y = AP.y + 4 * E.inQ(inv(dA, dD, td)); }
|
|
599
|
+
else if (td < dl) { const u = inv(dD, dl, td); x = AP.x; y = lerp(AP.y + 4, TIT.y, E.inQ(u)); const st = 1 + .15 * E.outQ(clamp(u * 2)); sx = 1 / st; sy = st; }
|
|
600
|
+
else { const bobs = [T('beatBob1'), T('beatBob2')]; let hop = 0, sq = land;
|
|
601
|
+
bobs.forEach(tb => { const u = inv(tb, tb + F(9), t); if (u > 0 && u < 1) hop += 16 * hump(E.outQ(u)); sq += .6 * kick(t, tb + F(9), 3.2, .4) - .35 * kick(t, tb - F(2), 5, .5) * (t < tb + F(1) ? 1 : 0); });
|
|
602
|
+
sy = 1 - .32 * sq; sx = 1 + .3 * sq; y = TIT.y - hop + r * (1 - sy); }
|
|
603
|
+
if (V5 && !fly && td >= CL.x1) y += (WBASE - IB.y0) * stem5(t); // v5: the resting dot rides 扁's squashing top bar
|
|
604
|
+
return { x, y, r, sx, sy, rot };
|
|
605
|
+
}
|
|
606
|
+
function updLogo(t, tr = t) {
|
|
607
|
+
const on = t >= CL.x0; vis(LOGO, on); if (!on) return;
|
|
608
|
+
const dl = T('dotLand2'), dA = T('dotApex'), dD = T('dotDrop'), lau = T('dotRise'), PC = { x: TIT.x, y: (IB.y0 + IB.y1) / 2 };
|
|
609
|
+
// camera: push 1.22x onto the i during the drop, snap back on the landing with ~4 % overshoot, then the slow end push
|
|
610
|
+
{ const z = logoZ(t);
|
|
611
|
+
LOGO.setAttribute('transform', `translate(${f3(PC.x)} ${f3(PC.y)}) scale(${z.toFixed(5)}) translate(${f3(-PC.x)} ${f3(-PC.y)})`); }
|
|
612
|
+
if (t < CL.x1) { const u = E.outQt(inv(CL.x0, CL.x1, t)); lbg.setAttribute('x', 0); lbg.setAttribute('y', 0); lbg.setAttribute('width', f3(lerp(960, 1920, u))); lbg.setAttribute('height', f3(lerp(540, 1080, u))); }
|
|
613
|
+
else { lbg.setAttribute('x', -400); lbg.setAttribute('y', -400); lbg.setAttribute('width', 2720); lbg.setAttribute('height', 1880); }
|
|
614
|
+
const stemEnd = V5 ? STEM5_OFF : CL.x1;
|
|
615
|
+
vis(stemR, t < stemEnd); if (t < stemEnd) { const b = stemBox(morphP(t)); stemR.setAttribute('x', f3(b.x0)); stemR.setAttribute('y', f3(b.y0)); stemR.setAttribute('width', f3(b.x1 - b.x0)); stemR.setAttribute('height', f3(b.y1 - b.y0)); stemR.setAttribute('rx', f3(b.rx)); }
|
|
616
|
+
const st5 = V5 ? stem5(t) : 0;
|
|
617
|
+
if (V5 && t < stemEnd) stemR.setAttribute('transform', `translate(${f3(LET[0].cx)} ${WBASE}) scale(${(1 + .5 * st5).toFixed(5)} ${(1 - st5).toFixed(5)}) translate(${f3(-LET[0].cx)} ${-WBASE})`);
|
|
618
|
+
const land = kick(t, dl, 3.2, .38), an = t < lau ? E.outQ(inv(CL.x1, lau, t)) : 0;
|
|
619
|
+
LET.forEach(L => {
|
|
620
|
+
if (V5 && L.i === 0) { // 扁 grows out of the bar (pop anchored on the bar centre), then squashes about the baseline under the dot
|
|
621
|
+
const [s, k] = pop(t, L.t0, 5, .65, .1, .5), sc = Math.max(s, 0), px = sc / (1 + k * .5), py = sc * (1 + k); vis(L.g, t >= L.t0);
|
|
622
|
+
tf(L.g, L.cx, WBASE, 1 + .5 * st5, 1 - st5); tf(L.gi, T5.bl.x * (1 - px), T5.bl.y * (1 - py), px, py); return; }
|
|
623
|
+
if (L.i === 4) { vis(L.g, t >= CL.x1); const stem = .12 * kick(t, dl, 3.5, .4) + .07 * an - .05 * kick(t, lau, 4, .4); tf(L.g, L.cx, WBASE, 1 + .5 * stem, 1 - stem); return; }
|
|
624
|
+
const [s, k] = pop(t, L.t0, 5, .65, .1, .5); vis(L.g, t >= L.t0); const dip = 10 * kick(t, dl + F(Math.abs(L.i - (V5 ? 0 : 4))), 3.2, .4);
|
|
625
|
+
const sc = Math.max(s, 0); tf(L.g, L.cx, WBASE + dip, sc / (1 + k * .5), sc * (1 + k)); });
|
|
626
|
+
{ const D = logoDot(t, tr); dotL.setAttribute('r', f3(D.r)); tf(dotL, D.x, D.y, D.sx, D.sy, D.rot); }
|
|
627
|
+
ring(bigR, t, dl, 60, 1100, 24, F(10));
|
|
628
|
+
END_RINGS.forEach(o => ring(o.c, t, o.t0, DOT_R + 14, DOT_R + 170, 18, F(12)));
|
|
629
|
+
landRays.forEach(lr => burstLine(lr.ln, TIT.x, TIT.y + DOT_R * .5, lr.a, DOT_R + 20, DOT_R + 20 + lr.len, t, dl, F(5)));
|
|
630
|
+
// 9.0: the tittle flicks a small coral dot into 点点; the 8 dots flip coral (1 f stagger, 115 % spring pop)
|
|
631
|
+
if (V5) { vis(spark, false); vis(sparkRing, false); } // v5: no tagline, no spark
|
|
632
|
+
else { const s0 = T('beatBob1'), s1 = T('cnFlip'), P0 = { x: TIT.x, y: TIT.y - 24 }, P1 = { x: CNDOTS[0].sx, y: CNDOTS[0].sy }, ts = tr >= s0 && tr < s1 ? tr : t, fl = ts >= s0 && ts < s1; vis(spark, fl);
|
|
633
|
+
if (fl) { const u = inv(s0, s1, ts), p = arcP(P0, P1, 110, u), p2 = arcP(P0, P1, 110, Math.min(1, u + .06)), st = 1 + clamp(Math.hypot(p2.x - p.x, p2.y - p.y) / 40) * .4; tf(spark, p.x, p.y, st, 1 / st, Math.atan2(p2.y - p.y, p2.x - p.x) * 180 / Math.PI); }
|
|
634
|
+
ring(sparkRing, t, s1, 18, 84, 10, F(9)); }
|
|
635
|
+
CNDOTS.forEach(d => { const on = t >= d.t0; vis(d.g, on); if (!on) return; const sc = Math.max(spring(t - d.t0, 3.4, .5), 0);
|
|
636
|
+
tf(d.g, d.cx, d.cy - 5 * kick(t, d.t0, 3, .45), sc, sc); d.inner.setAttribute('transform', `translate(${-d.cx} ${-d.cy})`); });
|
|
637
|
+
CNCH.forEach(c => { const u = E.outB(inv(c.t0, c.t0 + F(11), t), 1.4); tf(c.g, c.x, CNB + 100 * (1 - u)); });
|
|
638
|
+
{ const te = V5 ? T('tagCN') : T('tagEN'), u = E.outB(inv(te, te + F(12), t), 1.2); tf(enG, 0, (V5 ? T5.enB : 792) + 50 * (1 - u)); }
|
|
639
|
+
SIG.forEach(o => { const s = Math.max(pop(t, o.t0, 3.2, .45, 0)[0], 0); vis(o.c, s > .01); tf(o.c, o.x, o.y, s, s); });
|
|
640
|
+
}
|
|
641
|
+
// ============================================================ reel v5: link elements (pure functions of t, 1920x1080 px) + ?nolink
|
|
642
|
+
// 'dot' = the film's one coral hero dot, whichever shape it currently is: ball -> sun -> notification bubble -> circle wipe ->
|
|
643
|
+
// grid dot -> coral coin face -> TL bar cap -> the 丶 of 扁 (v5 title).
|
|
644
|
+
function camFor(t) { const tq = crisp(t, 0), fr = Math.round(t * 30), cam = { ...camera(t > 3.2 && t < 3.66 ? crisp(t, .45) : tq) }; // = renderAt's camera
|
|
645
|
+
if (fr === 112) { cam.s *= 1.03; cam.cy -= 9 / cam.s; } else if (fr === 113) { cam.s *= .988; cam.cx += 7 / cam.s; } return cam; }
|
|
646
|
+
const w2s = (cam, x, y) => ({ x: (x - cam.cx) * cam.s + 960, y: (y - cam.cy) * cam.s + 540 });
|
|
647
|
+
const i2s = (cam, lx, ly) => localToScreen(cam, 860 + 1.15 * (lx - 860), -153 + 1.15 * (ly + 153)); // interior coords (INT group, incl. its 1.15 zoom)
|
|
648
|
+
const lz = (t, x, y) => { const z = logoZ(t), P = { x: TIT.x, y: (IB.y0 + IB.y1) / 2 }; return { x: P.x + (x - P.x) * z, y: P.y + (y - P.y) * z, z }; };
|
|
649
|
+
const R2 = v => Math.round(v * 100) / 100;
|
|
650
|
+
const inFrame = (x, y, r) => r > .5 && x + r > 0 && x - r < 1920 && y + r > 0 && y - r < 1080;
|
|
651
|
+
const circ = (id, x, y, rx, ry, rot, a, note, extra = {}) => ({ id, type: 'circle', x: R2(x), y: R2(y), r: R2(Math.sqrt(Math.abs(rx * ry))), rx: R2(Math.abs(rx)), ry: R2(Math.abs(ry)), rot: R2(rot),
|
|
652
|
+
a: a && inFrame(x, y, Math.max(Math.abs(rx), Math.abs(ry))) ? 1 : 0, look: 'flat', fill: C.CO, stroke: C.CO, sw: 0, note, ...extra });
|
|
653
|
+
const rectL = (id, x0, y0, x1, y1, a, note, extra = {}) => ({ id, type: 'rect', x: R2((x0 + x1) / 2), y: R2((y0 + y1) / 2), w: R2(x1 - x0), h: R2(y1 - y0), rot: 0, a, note, ...extra });
|
|
654
|
+
function dotAt(t) {
|
|
655
|
+
const tq = crisp(t, 0), bt = T('ping') + F(1), wt = T('wipe'), cd = T('coreDot');
|
|
656
|
+
if (tq < 3.9) { const cam = camFor(t), b = ballA(t), p = w2s(cam, b.x, b.y);
|
|
657
|
+
return circ('dot', p.x, p.y, b.r * b.sx * cam.s, b.r * b.sy * cam.s, b.rot, 1, t < T('launch') ? 'coral ball: falls in (already falling at f0), bounces 0.50 / 0.80, crouches' : t < T('sunLand') ? 'coral ball flying up, growing into the sun' : 'coral sun (12 coral rays round it)'); }
|
|
658
|
+
if (tq < cd) { const cam = camFor(t), k2 = cam.s * 1.15 / 30;
|
|
659
|
+
if (tq < bt) { const p = i2s(cam, BUB.x, BUB.y - 84); return circ('dot', p.x, p.y, 0, 0, 0, 0, 'none: interior before the notification pops'); }
|
|
660
|
+
if (tq < wt) { const [s, k] = pop(tq, bt, 2.6, .38, .25), an = E.ioQ(inv(T('bubbleAntic'), wt, tq)), grow = E.inC(inv(wt, wt + F(3), tq));
|
|
661
|
+
const sx = Math.max(s, 0) / (1 + k) * (1 + .12 * an) * (1 + grow * .3), sy = Math.max(s, 0) * (1 + k) * (1 - .18 * an) * (1 + grow * .3), rot = 3 * Math.sin(2 * Math.PI * 1.2 * (tq - bt)) * (1 - an);
|
|
662
|
+
const by = BUB.y + 6 * an - 34 * E.outQ(inv(T('exclaim') + F(10), wt, tq)), th = rot * Math.PI / 180, p = i2s(cam, BUB.x + 84 * sy * Math.sin(th), by - 84 * sy * Math.cos(th));
|
|
663
|
+
return circ('dot', p.x, p.y, 58 * sx * k2, 58 * sy * k2, rot, 1, 'coral notification bubble (cream bell, small tail below)'); }
|
|
664
|
+
if (tq < T('wipe2')) { const u1 = inv(wt, wt + F(10), tq), k = Math.exp(lerp(Math.log(1.12), Math.log(34), E.ioQ(u1))), p = i2s(cam, BUB.x - 160 * E.ioQ(u1), BUB.y - 84 + 60 * E.ioQ(u1));
|
|
665
|
+
return circ('dot', p.x, p.y, 58 * k * k2, 58 * k * k2, 0, 1, 'the bubble swells into a full-frame coral circle wipe'); }
|
|
666
|
+
return circ('dot', 960, 540, R_D, R_D, 0, 0, 'none: mint capsule + cream half-disc wipes; the dot squirts out of the centre at 5.50'); }
|
|
667
|
+
if (tq < HOPS[3].l) { const hd = hero(t);
|
|
668
|
+
if (hd) return circ('dot', hd.x, hd.y, R_D * hd.sx, R_D * hd.sy, hd.rot, 1, 'coral grid dot (squirts 5.50-5.70, hops onto the beats 6.0 / 6.5 / 7.0 / 7.5)');
|
|
669
|
+
return circ('dot', QD[2].K.x, QD[2].K.y, R_D, R_D, 0, 0, 'none: inside the coral heart (7.00-7.27)'); }
|
|
670
|
+
if (coinTh(tq) < Math.PI / 2) { const [g] = pop(tq, QD[3].t0, 2.6, .48, 0), R = lerp(R_D, 180, Math.max(g, 0));
|
|
671
|
+
return circ('dot', 480, 810, Math.max(Math.abs(Math.cos(coinTh(tq))), .03) * R, R, 0, 1, 'coral disc growing, about to flip edge-on into the gold coin'); }
|
|
672
|
+
if (tq < CL.x0) return circ('dot', 600, capY(tq), R_D, R_D, 0, 1, 'coral cap on the tall cream bar (top-left quadrant); the other key dots zip into it 7.83-8.00');
|
|
673
|
+
const D = logoDot(tq, t), p = lz(tq, D.x, D.y);
|
|
674
|
+
return circ('dot', p.x, p.y, D.r * D.sx * p.z, D.r * D.sy * p.z, D.rot, 1, V5 ? 'coral dot = the 丶 of 扁 (rides the bar as it morphs, hops up 8.23, lands ON 8.50, bobs 9.0 / 9.5)' : 'tittle of popwise');
|
|
675
|
+
}
|
|
676
|
+
function linkExtras(t) {
|
|
677
|
+
const tq = crisp(t, 0), out = [];
|
|
678
|
+
if (tq < 3.5) { const cam = camFor(t), ge = lerp(0, 1900, E.outQ(inv(F(1.5), F(14), tq))), y = (GY - cam.cy) * cam.s + 540, x1 = ge >= 1899 ? 1920 : Math.min(1920, (ge - cam.cx) * cam.s + 960);
|
|
679
|
+
if (y > 0 && y < 1080 && x1 > 0) out.push({ id: 'horizon', type: 'line', x0: 0, y0: R2(y), x1: R2(x1), y1: R2(y), w: 0, a: 1, fill: C.CR, note: 'top edge of the cream ground (flat fill below, ultramarine sky above); sweeps in L->R 0.05-0.47 behind a sunflower lead bar' }); }
|
|
680
|
+
if (tq >= QD[0].t0 + F(1) && tq < CL.x0) { const b = barS(tq); if (b > .02) out.push(rectL('bar', 558, 440 - 270 * b, 642, 440, 1, 'tall cream bar of the top-left bar chart (its cap = the dot); morphs into the title bar 7.90-8.17', { fill: C.CR, rx: 24 })); }
|
|
681
|
+
else if (tq >= CL.x0 && tq < (V5 ? STEM5_OFF : CL.x1)) { const bb = stemBox(morphP(tq)), a = lz(tq, bb.x0, bb.y0), c = lz(tq, bb.x1, bb.y1); out.push(rectL('bar', a.x, a.y, c.x, c.y, 1, V5 ? 'cream bar morphing into the top bar of 扁' : 'cream bar morphing into the stem of the i', { fill: C.CR })); }
|
|
682
|
+
if (tq >= HOPS[1].o && tq < CL.x0 && !(tq >= QD[3].t0 && coinTh(tq) >= Math.PI / 2)) out.push(circ('cap', 600, capY(tq), R_D, R_D, 0, 1, 'coral cap left on the tall cream bar (becomes the main dot from 7.60)'));
|
|
683
|
+
if (tq >= QD[3].t0 && coinTh(tq) >= Math.PI / 2 && tq < CL.c1) { const [g] = pop(tq, QD[3].t0, 2.6, .48, 0), R = lerp(R_D, 180, Math.max(g, 0)), cs = 1 - E.inQ(inv(CL.ca, CL.c1, tq));
|
|
684
|
+
out.push(circ('coin', 480, 810, Math.max(Math.abs(Math.cos(coinTh(tq))), .03) * R * cs, R * cs, 0, 1, 'gold ¥ coin (flips edge-on 7.50-7.77, shrinks into a coral dot 7.77-7.87)', { fill: C.SU, stroke: C.SUD, sw: 16 })); }
|
|
685
|
+
if (V5 && tq >= LET[0].t0) { const sc = Math.min(...LET.map(L => Math.max(pop(tq, L.t0, 5, .65, .1, .5)[0], 0))), a = lz(tq, T5.ink.x0, T5.ink.y0), c = lz(tq, T5.ink.x1, T5.ink.y1);
|
|
686
|
+
out.push(rectL('title', a.x, a.y, c.x, c.y, sc > .9 ? 1 : 0, 'title 「扁平矢量」 ink box (cream Resource Han Rounded 900 on ultramarine), without the dot', { fill: C.CR })); }
|
|
687
|
+
return out;
|
|
688
|
+
}
|
|
689
|
+
function hideLinks(tq) { // ?nolink=dot[,cap,rays,coin]: remove that element (and the ball's shadow + speed lines), nothing else moves
|
|
690
|
+
const capIsDot = tq >= QD[3].t0 && coinTh(tq) >= Math.PI / 2;
|
|
691
|
+
if (NOLINK.has('dot')) { [dotW, shadow, ...speedL, bubble, W1, heroD, QD[3].fDot, dotL].forEach(e => vis(e, false)); if (capIsDot) vis(QD[0].keyD, false); }
|
|
692
|
+
if (NOLINK.has('cap') && !capIsDot) vis(QD[0].keyD, false);
|
|
693
|
+
if (NOLINK.has('rays')) rays.forEach(l => vis(l, false));
|
|
694
|
+
if (NOLINK.has('coin')) vis(QD[3].cg, false);
|
|
695
|
+
}
|
|
696
|
+
if (V5) window.linkAt = t => [dotAt(t), ...linkExtras(t)];
|
|
697
|
+
window.renderAt = async (t) => {
|
|
698
|
+
const tq = crisp(t, 0), fr = Math.round(t * 30); // R2: everything is crisp (frame-quantised) except the camera push and the dot flights
|
|
699
|
+
const cam = { ...camera(t > 3.2 && t < 3.66 ? crisp(t, .45) : tq) };
|
|
700
|
+
if (fr === 112) { cam.s *= 1.03; cam.cy -= 9 / cam.s; } else if (fr === 113) { cam.s *= .988; cam.cx += 7 / cam.s; } // 2-frame arrival shake
|
|
701
|
+
vis(FLASH, fr === 105); if (fr === 105) { const a = localToScreen(cam, 0, 0), b = localToScreen(cam, 1920, 1080); FLASH.setAttribute('x', f3(a.x)); FLASH.setAttribute('y', f3(a.y)); FLASH.setAttribute('width', f3(b.x - a.x)); FLASH.setAttribute('height', f3(b.y - a.y)); }
|
|
702
|
+
{ const c = localToScreen(cam, 960, 540), tc = crisp(t, .3); STREAKS.forEach(k => { const u = (tc - F(k.f0)) / F(2); if (u < 0 || u >= 1) { vis(k.p, false); return; } vis(k.p, true);
|
|
703
|
+
const ra = k.r0 + 320 * u, rb = ra + k.len * (1 - .35 * u), w = k.w * (1 - .3 * u), wi = w * .3, dx = Math.cos(k.a), dy = Math.sin(k.a), nx = -dy, ny = dx;
|
|
704
|
+
const Q = (r, o) => `${f3(c.x + dx * r + nx * o)} ${f3(c.y + dy * r + ny * o)}`;
|
|
705
|
+
k.p.setAttribute('d', `M${Q(ra, wi / 2)} L${Q(rb, w / 2)} A${f3(w / 2)} ${f3(w / 2)} 0 0 0 ${Q(rb, -w / 2)} L${Q(ra, -wi / 2)} A${f3(wi / 2)} ${f3(wi / 2)} 0 0 0 ${Q(ra, wi / 2)} Z`); }); }
|
|
706
|
+
updWorld(tq, cam, t); updInterior(tq, cam); updIcons(tq, cam, t); updLogo(tq, t);
|
|
707
|
+
if (V5 && NOLINK.size) hideLinks(tq);
|
|
708
|
+
};
|
|
709
|
+
await window.renderAt(0);
|
|
710
|
+
window.__ready = true;
|
|
711
|
+
</script>
|
|
712
|
+
</body></html>
|