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
What happens
A CSS animation inside a shadow root, or on a
::before/::afterpseudo-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
currentTimeat that moment (ensureBaselineinpackages/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.Read after
seek(2):getComputedStyle(document.getElementById("el"), "::before").transformgetComputedStyle(document.getElementById("host").shadowRoot.getElementById("in")).transformExpected:
translateX(50px)on every load.Observed: about 50.4 to 51.25 px, different from run to run.
Notes
data-startand are exact.