Skip to content

Shadow-root and pseudo-element CSS animations land about a frame off, differently each load #4555

Description

@miguel-heygen

What happens

A CSS animation inside a shadow root, or on a ::before/::after pseudo-element, lands at a slightly different frame each time the page loads. Seeking to the same time gives a position that varies by about one frame between runs (0.4 to 1.25 px on a 4 s, 100 px slide), in Studio's preview and in renders.

The CSS adapter only drives an element's own animations, so these are left to the WAAPI adapter. The WAAPI adapter records a baseline for each animation the first time it sees it, including the animation's own currentTime at that moment (ensureBaseline in packages/core/src/runtime/adapters/waapi.ts). For a CSS animation that has been running since the page loaded, that value is however much wall-clock time passed before discover, so every later seek is offset by it.

Repro

Headless Chrome, the runtime built from main, window.__player.seek(2), then read the computed transform. Load the page several times.

<style>
@keyframes slide { from { transform: translateX(0) } to { transform: translateX(100px) } }
#el::before { content: "x"; display: inline-block; animation: slide 4s linear 1 both; }
</style>
<div data-composition-id="main" data-root="true" data-start="0" data-duration="10" data-width="640" data-height="360">
  <div id="el" class="clip" data-start="0" data-duration="10">el</div>
  <div id="host" class="clip" data-start="0" data-duration="10"></div>
</div>
<script>
  const sr = document.getElementById("host").attachShadow({ mode: "open" });
  sr.innerHTML = '<style>@keyframes slide { from { transform: translateX(0) } to { transform: translateX(100px) } } #in { animation: slide 4s linear 1 both; }</style><div id="in">in</div>';
</script>
<!-- then the runtime script -->

Read after seek(2):

  • getComputedStyle(document.getElementById("el"), "::before").transform
  • getComputedStyle(document.getElementById("host").shadowRoot.getElementById("in")).transform

Expected: translateX(50px) on every load.
Observed: about 50.4 to 51.25 px, different from run to run.

Notes

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