diff --git a/packages/parsers/src/clipTweens.ts b/packages/parsers/src/clipTweens.ts new file mode 100644 index 0000000000..525a8810b5 --- /dev/null +++ b/packages/parsers/src/clipTweens.ts @@ -0,0 +1,48 @@ +import type { GsapAnimation } from "./gsapSerialize.js"; + +type TweenTarget = Pick; +type TweenTime = Pick; + +// Tweens a timeline move or retime of a clip carries: an own selector, plus (given a DOM) a fully known target +// set that is the clip or sits inside it with no nearer `data-start` clip. A tween also aiming outside stays put. +export function clipTweenMatcher( + clipSelectors: string | readonly string[], + root?: ParentNode, +): (tween: TweenTarget) => boolean { + const own = typeof clipSelectors === "string" ? [clipSelectors] : clipSelectors; + const clips = root ? own.flatMap((selector) => queryAll(root, selector)) : []; + return ({ targetSelector, hasPartialSelector }) => { + if (hasPartialSelector) return false; + if (own.includes(targetSelector)) return true; + if (!root || clips.length === 0) return false; + const targets = queryAll(root, targetSelector); + return ( + targets.length > 0 && + targets.every((target) => { + const owner = target.closest("[data-start]"); + return clips.includes(target) || (owner !== null && clips.includes(owner)); + }) + ); + }; +} + +/** Where a GSAP script's clips live: linkedom keeps a `