Skip to content

Commit

Permalink
Add support for SVG animations (#39)
Browse files Browse the repository at this point in the history
* add animated svg to page

* check svg animation time in test

Todo: make this test useful

* add documentation for `svg-anim`

* fixes problem where the video player did not start

* check that animations are not resetting

* Update _Description.astro

* fixes a race condition

* adopt to new syntax

* add animated SVG to all pages

* update docs with final implementation

* add SVGs to descriptions

---------

Co-authored-by: Martin Trapp <94928215+martrapp@users.noreply.github.com>
  • Loading branch information
Trombach and martrapp authored Oct 30, 2024
1 parent 33aeded commit 05fa5c4
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 39 deletions.
2 changes: 1 addition & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default defineConfig({
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
use: { ...devices['Desktop Chrome'], channel: 'chrome', },
},
],

Expand Down
29 changes: 29 additions & 0 deletions src/components/AnimatedSvg.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
const PATH_VALUES = `
M131.6 -244.4C153.5 -215.3 142.6 -145.6 176.5 -98.2C210.4 -50.7 289.2 -25.3 277.8 -6.6C266.4 12.2 164.8 24.3 131.1 72.3C97.5 120.2 131.7 203.8 121.2 218.8C110.7 233.7 55.3 179.8 11.6 159.8C-32.2 139.7 -64.3 153.4 -103.6 155.1C-142.9 156.8 -189.2 146.4 -179.8 118.5C-170.4 90.7 -105.2 45.3 -120.1 -8.6C-134.9 -62.5 -229.8 -125 -223.2 -125C-216.5 -125 -108.3 -62.5 -54.1 -73.1C0 -83.7 0 -167.3 27.4 -214.8C54.8 -262.3 109.7 -273.6 131.6 -244.4;
M166.3 -251.2C207.6 -264.2 227.6 -203.6 254.5 -149.5C281.5 -95.3 315.2 -47.7 322 3.9C328.8 55.5 308.6 111 266.4 138.7C224.3 166.4 160.1 166.4 112.1 200.8C64 235.2 32 304.1 5 295.4C-22 286.8 -44 200.5 -44 138.4C-44 76.2 -22 38.1 -30 19.1C-38 0 -76 0 -110.6 -20C-145.3 -40 -176.6 -80 -180.8 -120.3C-185.1 -160.5 -162.3 -201.1 -127.6 -191.9C-93 -182.7 -46.5 -123.9 8 -137.7C62.5 -151.6 125 -238.2 166.3 -251.2;
M122.2 -168C167.1 -185.8 218.2 -170.6 261.4 -137.2C304.6 -103.7 339.8 -51.8 301.1 -22.3C262.4 7.2 149.8 14.3 87.3 14.3C24.8 14.3 12.4 7.2 6.2 41.8C0 76.3 0 152.7 -14.4 177.6C-28.8 202.6 -57.7 176.2 -84.5 153.5C-111.4 130.9 -136.2 111.9 -179 87C-221.7 62 -282.4 31 -299.4 -9.8C-316.4 -50.7 -289.8 -101.3 -232.7 -101.3C-175.5 -101.3 -87.8 -50.7 -43.9 -33.5C0 -16.3 0 -32.7 19.3 -66.2C38.7 -99.6 77.3 -150.3 122.2 -168;
M131.6 -244.4C153.5 -215.3 142.6 -145.6 176.5 -98.2C210.4 -50.7 289.2 -25.3 277.8 -6.6C266.4 12.2 164.8 24.3 131.1 72.3C97.5 120.2 131.7 203.8 121.2 218.8C110.7 233.7 55.3 179.8 11.6 159.8C-32.2 139.7 -64.3 153.4 -103.6 155.1C-142.9 156.8 -189.2 146.4 -179.8 118.5C-170.4 90.7 -105.2 45.3 -120.1 -8.6C-134.9 -62.5 -229.8 -125 -223.2 -125C-216.5 -125 -108.3 -62.5 -54.1 -73.1C0 -83.7 0 -167.3 27.4 -214.8C54.8 -262.3 109.7 -273.6 131.6 -244.4;
`;
type Props = {
expression?: string;
};
---

<svg
viewBox="0 0 900 600"
width="200"
height="112.5"
{...(Astro.props.expression ? { "data-vtbag-x": Astro.props.expression } : {})}
>
<g transform="translate(400 300)">
<path fill="none" stroke="currentColor" stroke-width="4">
<animate
attributeName="d"
dur="8000ms"
repeatCount="indefinite"
values={PATH_VALUES}></animate>
</path>
</g>
</svg>
2 changes: 2 additions & 0 deletions src/content/docs/tools/element-crossing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ The style expression identifies an entry of the current HTML element's `style` p
#### anim
The anim expression references a CSSAnimation of the current HTML element. If the target element on the next page has an animation with the same `animationName`, the current playback time is transferred. Note these current restrictions of the Element Crossing: The `animationName` values must be unique per HTML element, and only CSSAnimations are supported, not CSSTransitions.

The `anim` expression can also be used to persist SVG animation states. Using the special `anim:/svg` expression, it transfers the current animation playback time to the target element. The expression needs to be declared on the `<svg>` element, not on the `<animate>` element.

### Abbreviated Forms
The long forms `id:`, `prop:`, `class:`, `style:`, and `anim:` can be abbreviated as `#`, `@`, `.`, `-`, and `~`, respectively. For example, instead of writing `data-vtbag-x="id:group class:active prop:value"`, you could write `data-vtbag-x="#group .active @value"`, or use a combination of both forms.

Expand Down
28 changes: 18 additions & 10 deletions src/pages/crossing/over-the-top/_Description.astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
---
import AnimatedSvg from "@/components/AnimatedSvg.astro";
---
<p>This is a demonstration of the <b>over-the-top flavor</b> of the element crossing. This flavor is experimental technology and it is not certain whether it will ever be mature enough for production.</p>
Expand All @@ -8,16 +10,17 @@
left and reposition its scrollbar.
</p>
<p>
Then select the other page from the navigation sidebar to see whether your changes got
preserved. Also check the playback time of the video and the animation in the
footer.
Then select the other page from the navigation sidebar to see whether your changes got preserved. Also check the playback time of the video and the animated SVG as well as the animation in the footer.
</p>


<video controls width="200" loop autoplay data-vtbag-x="#video @currentTime">
<source src="/video.mp4" type="video/mp4" />Your browser does not support the
video tag.</video
>
<div class="animations">
<video controls width="200" loop autoplay data-vtbag-x="#video @currentTime">
<source src="/video.mp4" type="video/mp4" />Your browser does not support the
video tag.</video
>
<AnimatedSvg expression="#svg ~/svg" />
</div>

<form id="exampleForm" data-vtbag-x="&form">
<div class="form-group">
Expand Down Expand Up @@ -102,9 +105,14 @@
</form>

<style is:global>
video {
margin: auto;
display: block;
.animations {
display: flex;
flex-direction: row;
justify-content: space-around;
margin-bottom: 20px;
}

.animations > * {
border: inset 10px #8888;
}

Expand Down
27 changes: 19 additions & 8 deletions src/pages/crossing/plain/_Description.astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
---
import AnimatedSvg from "@/components/AnimatedSvg.astro";
---

Expand All @@ -8,10 +10,14 @@
single-page applications to your multi-page app.
</p>

<video controls width="200" loop autoplay>
<source src="/video.mp4" type="video/mp4" />Your browser does not support the
video tag.</video
>

<div class="animations">
<video controls width="200" loop autoplay>
<source src="/video.mp4" type="video/mp4" />Your browser does not support the
video tag.</video
>
<AnimatedSvg />
</div>

<p>
Some HTML elements on your page may change their internal state over time or
Expand All @@ -34,7 +40,7 @@
<li>
The current playback time of media elements, including audio and video.
</li>
<li>The current playback time of CSS animations and transitions.</li>
<li>The current playback time of CSS animations, transitions and animated SVGs.</li>
<li>The state and content of input fields.</li>
<li>The content of iframes.</li>
</ul>
Expand Down Expand Up @@ -107,9 +113,14 @@
</form>

<style is:global>
video {
margin: auto;
display: block;
.animations {
display: flex;
flex-direction: row;
justify-content: space-around;
margin-bottom: 20px;
}

.animations > * {
border: inset 10px #8888;
}

Expand Down
36 changes: 23 additions & 13 deletions src/pages/crossing/vanilla/_Description.astro
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
---
import AnimatedSvg from "@/components/AnimatedSvg.astro"
---
<p>This is a demonstration of the <b>vanilla flavor</b> of the element crossing. This flavor provides well understood technology and is meant for production.</p>

<p>
This is a demonstration of the <b>vanilla flavor</b> of the element crossing. This
flavor provides well understood technology and is meant for production.
</p>

<p>
Enter data in the form fields, resize the text area, change the checkmarks,
switch the color-scheme, collapse a group in the navigation sidebar on the
left and reposition its scrollbar.
</p>
<p>
Then select the other page from the navigation sidebar to see whether your changes got
preserved. Also check the playback time of the video and the animation in the
footer.
Then select the other page from the navigation sidebar to see whether your
changes got preserved. Also check the playback time of the video and the animated SVG as well as the animation in the footer.
</p>


<video controls width="200" loop autoplay data-vtbag-x="#video @currentTime">
<source src="/video.mp4" type="video/mp4" />Your browser does not support the
video tag.</video
>
<div class="animations">
<video controls width="200" loop autoplay data-vtbag-x="#video @currentTime">
<source src="/video.mp4" type="video/mp4" />Your browser does not support the
video tag.</video
>
<AnimatedSvg expression="#svg ~/svg" />
</div>

<form id="exampleForm">
<div class="form-group">
Expand Down Expand Up @@ -110,9 +115,14 @@
</form>

<style is:global>
video {
margin: auto;
display: block;
.animations {
display: flex;
flex-direction: row;
justify-content: space-around;
margin-bottom: 20px;
}

.animations > * {
border: inset 10px #8888;
}

Expand Down
23 changes: 16 additions & 7 deletions tests/01_crossing.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,30 @@ test.describe("vtbag", () => {


test('preserves animations and media timing', async ({ page }) => {
// todo: unable to get the video to play yet
await page.goto('http://localhost:4321/crossing/vanilla/1/');
await expect(page).toHaveTitle('Vanilla Element Crossing 1/2 | @vtbag');
await page.evaluate(async () => {
const video = document.querySelector("video");
video && video.play();
document.querySelector("video")!.play();
});
await new Promise(r => setTimeout(r, 1000));
const video1 = await page.$eval('video', el => el.currentTime);
console.log(video1);
const svg1 = await page.$eval('svg', (el: SVGSVGElement) => el.getCurrentTime());
const footer1 =await page.$eval('footer div', el => ~~getComputedStyle(el).transform.split(/,\s*/)[4]!);

await page.click('text=Page 2');
await expect(page).toHaveTitle('Vanilla Element Crossing 2/2 | @vtbag');

const video2 = await page.$eval('video', el => el.currentTime);
console.log(video2);
await new Promise(r=>setTimeout(r)); // get rid of race condition

const video2 = await page.$eval("video", (el) => el.currentTime);
const svg2 = await page.$eval('svg', (el: SVGSVGElement) => el.getCurrentTime());
const footer2 =await page.$eval('footer div', el => ~~getComputedStyle(el).transform.split(/,\s*/)[4]!);

expect(video1).toBeGreaterThan(1);
expect(video2).toBeGreaterThan(video1);
expect(svg1).toBeGreaterThan(1);
expect(svg2).toBeGreaterThan(svg1);
expect(footer1).toBeGreaterThan(1);
expect(footer2).toBeGreaterThan(footer1);
});
});

Expand Down

0 comments on commit 05fa5c4

Please sign in to comment.