Skip to content

Preview: tl.set holds never render when the root timeline has zero duration #4519

Description

@Harlan66

Describe the bug

A root timeline whose own duration is 0 — e.g. one that only holds tl.set(...) at position 0, which is exactly what Studio writes for a static resize — never renders its sets in the preview. After resizing an element in Studio the file is correct, but the element snaps back to its authored size in the preview (keeping the drag offset), so it looks like the resize was lost.

Cause: in packages/core/src/runtime/init.ts, an unusable (zero) root duration takes the createDurationFloorTimeline fallback: the root timeline is added to a new paused wrapper padded to the composition duration. The author's root was created gsap.timeline({ paused: true }) and stays paused, and a paused child is never rendered when its parent is seeked. The nested-composition path already unpauses held children (ensureChildCandidatesActive); the floor wrapper does not.

Minimal reproduction

<div id="root" data-composition-id="main" data-start="0" data-duration="2" data-width="1920" data-height="1080">
  <div id="box" class="clip" data-start="0" data-duration="2" data-track-index="1"
       style="position:absolute; left:100px; top:100px; width:400px; height:300px; background:tomato"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script>
  window.__timelines = window.__timelines || {};
  const tl = gsap.timeline({ paused: true });
  tl.set("#box", { width: 200, height: 150 }, 0);
  window.__timelines["main"] = tl;
</script>

Steps to reproduce

  1. npx hyperframes preview on the composition above (or produce it by resizing an element in Studio in a composition without tweens).
  2. Look at #box at any playhead position.

Expected behavior

#box renders at 200×150.

Actual behavior

#box stays at 400×300. In the preview iframe, __timelines.main.paused() is true, its children are never initialised, and __player.seek(t) has no effect on it. After __timelines.main.paused(false), __player.seek(0.5) renders 200×150. A single tween in the timeline (non-zero duration) avoids the fallback and the bug. hyperframes snapshot --at 0.5 of the same file renders 200×150 correctly, so only the preview runtime is affected.

Environment

hyperframes CLI 0.8.77 (also on main @ 0985b45), GSAP 3.12.5 and 3.15.0, macOS, Chrome (headed and headless).

Additional context

Fix PR to follow: unpause the root timeline once the floor wrapper actually holds it, mirroring ensureChildCandidatesActive.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions