Skip to content

Commit

Permalink
Web Animation API amimations
Browse files Browse the repository at this point in the history
  • Loading branch information
martrapp committed Oct 4, 2024
1 parent 31d01a9 commit 54acd79
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 35 deletions.
5 changes: 5 additions & 0 deletions .changeset/modern-birds-appear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@vtbag/inspection-chamber': patch
---

Now the Chamber can also handle animations of view transition pseudo-elements that where started using JavaScript and the Web Animation API
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ The @vtbag website can be found at https://vtbag.pages.dev/

## !!! News !!!

Improved error handling.
In addition to CSS animations of pseudo-elements, the Chamber can now also handle animations of pseudo-elements started using the Web Animation API.

For details, see the [CHANGELOG](https://github.com/vtbag/inspection-chamber/blob/main/CHANGELOG.md)
For latest changes, see the [CHANGELOG](https://github.com/vtbag/inspection-chamber/blob/main/CHANGELOG.md)

## What happened so far:

> Improved error handling
> Names in the Animation Groups panel are now shown in the order in which the transition groups appear as children of the `::view-transition` pseudo-element. This makes it easier to understand how the individual groups overlap during rendering.
> You can now observe in real-time how CSS properties are updated by animations and easily see which other properties are applied to the pseudo-elements created by the View Transition API.
Expand Down
28 changes: 10 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
"url": "https://github.com/sponsors/martrapp"
},
"devDependencies": {
"@changesets/cli": "^2.27.8",
"@eslint/js": "^9.11.1",
"@changesets/cli": "^2.27.9",
"@eslint/js": "^9.12.0",
"@types/dom-view-transitions": "^1.0.5",
"esbuild": "^0.24.0",
"prettier": "^3.3.3",
Expand Down
61 changes: 48 additions & 13 deletions src/animations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,17 @@ export async function retrieveViewTransitionAnimations() {
const set = new WeakSet();
let growing = true;
let rootRootAnimation = false;
let cnt = 0;
while (growing) {
growing = false;
frameDoc.getAnimations().forEach((animationObject) => {
if (animationObject.effect?.pseudoElement?.startsWith('::view-transition')) {
const { pseudoName, viewTransitionName } = namesOfAnimation(animationObject)!;
if (!set.has(animationObject)) {
const keyframeName =
animationObject instanceof CSSAnimation ? animationObject.animationName : undefined;
let keyframeName =
animationObject instanceof CSSAnimation
? animationObject.animationName
: (animationObject.id ||= `vtbag-js-animation-${++cnt}`);
const transitionProperty =
animationObject instanceof CSSTransition
? animationObject.transitionProperty
Expand Down Expand Up @@ -138,9 +141,10 @@ export function listAnimations(name: string) {
anim.querySelectorAll<HTMLInputElement>('input[type="checkbox"]').forEach((box) => {
const context = JSON.parse(box.dataset.vtbagContext!);
box.removeAttribute('data-vtbag-context');
box.checked = selectAnimation(name, context.pseudo, context.idx)?.playState === 'paused';
box.checked =
selectAnimation(name, context.pseudo, context.idx, context.id)?.playState === 'paused';
box.addEventListener('change', () => {
if (!stopAndGo(name, context.pseudo, context.idx, box.checked)) {
if (!stopAndGo(name, context.pseudo, context.idx, context.id, box.checked)) {
box.checked = !box.checked;
}
});
Expand All @@ -164,7 +168,7 @@ export function listAnimations(name: string) {
cssAnimation.split(/,(?![^(]*\))/).forEach((animation, idx) => {
if (inspectionChamber.keyframesMap?.get(animationNames[idx])) {
res.push(
`<details><summary><input type="checkbox" data-vtbag-context='{"pseudo":"${pseudo}","idx":${idx - skipped}}'/> ${pseudo}: <tt>${animationNames[idx]}</tt></summary>${details(animationNames[idx], animation.endsWith(animationName) ? animation.slice(0, -animationName.length) : animation)}</details>`
`<details><summary><input type="checkbox" data-vtbag-context='{"pseudo":"${pseudo}","idx":${idx - skipped},"id":""}'/> ${pseudo}: <tt>${animationNames[idx]}</tt></summary>${details(animationNames[idx], animation.endsWith(animationName) ? animation.slice(0, -animationName.length) : animation)}</details>`
);
} else {
res.push(
Expand All @@ -173,11 +177,29 @@ export function listAnimations(name: string) {
++skipped;
}
});
if (allProps.size > 0) {
}

inspectionChamber.animations
?.filter((a) => {
return (
!('animationName' in a) &&
a.effect?.pseudoElement === `::view-transition-${pseudo}(${name})`
);
})
.forEach((animation, idx) => {
const animationString = (a: Animation) => {
const t = a.effect?.getTiming();
return `${t?.duration || '0'}ms ${t?.easing ?? ''} ${t?.delay ?? '0'}ms ${t?.iterations ?? ''} ${t?.direction ?? ''} ${t?.fill ?? ''} ${a.playState}`;
};
res.push(
`<details data-vtbag-live-values="${pseudo + ',' + [...allProps].sort().join(',')}"><summary>&nbsp;🌀&thinsp; ${pseudo}: live values</summary></details>`
`<details><summary><input type="checkbox" data-vtbag-context='{"pseudo":"${pseudo}","idx":-1,"id":"${animation.id}"}'/> ${pseudo}: <tt>${animation.id}</tt></summary>${details(animation.id, animationString(animation))}</details>`
);
}
});

if (allProps.size > 0) {
res.push(
`<details data-vtbag-live-values="${pseudo + ',' + [...allProps].sort().join(',')}"><summary>&nbsp;🌀&thinsp; ${pseudo}: live values</summary></details>`
);
}

if (style) {
Expand Down Expand Up @@ -241,8 +263,8 @@ export function resetAnimationVisibility() {
control();
}

function stopAndGo(name: string, pseudo: any, idx: any, checked: boolean) {
const anim = selectAnimation(name, pseudo, idx);
function stopAndGo(name: string, pseudo: string, idx: number, id: string, checked: boolean) {
const anim = selectAnimation(name, pseudo, idx, id);
if (!anim) return false;
if (checked) {
anim.pause();
Expand All @@ -253,12 +275,25 @@ function stopAndGo(name: string, pseudo: any, idx: any, checked: boolean) {
return true;
}

export function selectAnimation(name: string, pseudo: string, idx: number) {
export function selectAnimation(name: string, pseudo: string, idx: number, id: string) {
const pseudoElement = `::view-transition-${pseudo}(${name})`;
const chamber = top!.__vtbag.inspectionChamber!;
const animations = chamber.animations!;

if (id) {
const filtered = animations.filter(
(anim) => anim.id === id && anim.effect?.pseudoElement === pseudoElement
);
if (filtered.length === 1) {
return filtered[0];
}
console.error(
`[injection chamber] found ${filtered.length} animations for ${pseudoElement} when looking for animation with id ${id} `
);
return;
}
const styleMap = chamber.styleMap!;
const animationName = styleMap.get(`${pseudo}-${name}`)!.animationName.split(', ')[idx];
const animations = chamber.animations!;
const pseudoElement = `::view-transition-${pseudo}(${name})`;
const selected = animations.filter((anim) => anim.effect?.pseudoElement === pseudoElement);
if (idx >= selected.length) {
console.error(
Expand Down

0 comments on commit 54acd79

Please sign in to comment.