2.8 KiB
2.8 KiB
Gravity Engine Fix — Design Spec
Date: 2026-04-24
Branch: gravity
Files in scope: lib/gravity-engine.ts
Problem
Two bugs in the current GravityEngine:
- Lag —
tick()callsel.getBoundingClientRect()on every body every frame, forcing a layout reflow at 60fps. - Container blowout —
extractLeaves()setswidth/heighton intermediate nodes, butstop()only clears styles on leaf bodies and.gravity-bodyroots. Intermediate nodes keep their locked dimensions, breaking layout on deactivation.
Approach — Option A: Cache + dirty-set + CSS transition glide-back
Data structures
PhysicsBody gains four new fields:
width,height— snapshotted fromgetBoundingClientRect()atstart(), used intick()for floor collision instead of live DOM queriesoriginX,originY—rect.left/rect.topat activation time, used as the glide-back transform target
GravityEngine gains:
dirtyElements: HTMLElement[]— every element whose inline styles are touched (leaves, intermediate containers,.gravity-bodyroots).stop()iterates this single list to fully wipe styles.
start() — three strictly ordered phases
- Size-lock phase — walk
.gravity-bodycontainers and all descendants viaextractLeaves. For each intermediate node (non-solid, has children), writewidth/heightfromoffsetWidth/offsetHeightand push todirtyElements. Push.gravity-bodyroots too. - Read phase — batch all
getBoundingClientRect()across collected leaf elements before touching any positions. One layout flush, no interleaving. - Write phase — using snapshotted rects, set each leaf to
position: fixed, zeroleft/top, applytranslate(rect.left, rect.top). Cachewidth/height/originX/originY. Push leaf todirtyElements. Attach pointer events.
tick() — one change
Replace el.getBoundingClientRect().height with body.height. Remove redundant position/left/top re-sets inside the dragging branch (set once at start, never change).
stop() — glide-back then full cleanup
- Cancel animation frame, set
isRunning = false. - For each body: apply
transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1)and settransform: translate(originX, originY). - After
600ms(setTimeout): iteratedirtyElements, clear all inline styles (transform,position,left,top,width,height,margin,transition). Document flow fully restored. - Clear
bodies[]anddirtyElements[].
Success criteria
- No layout reflow during animation loop
- Toggling gravity off glides all elements back to their original positions with a spring curve
- After glide completes, all intermediate container styles are cleared and layout is indistinguishable from the initial page load