diff --git a/lib/.gitattributes b/lib/.gitattributes
index 8ec8526..c7f813d 100644
--- a/lib/.gitattributes
+++ b/lib/.gitattributes
@@ -38,3 +38,13 @@ tests/__screenshots__/particles/particles-chromium.png filter=lfs diff=lfs merge
tests/__screenshots__/particles/particles-firefox.png filter=lfs diff=lfs merge=lfs -text
tests/__screenshots__/particles/particles-iphone.png filter=lfs diff=lfs merge=lfs -text
tests/__screenshots__/pointer/pointer-chromium.png filter=lfs diff=lfs merge=lfs -text
+tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-android.png filter=lfs diff=lfs merge=lfs -text
+tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-firefox.png filter=lfs diff=lfs merge=lfs -text
+tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-iphone.png filter=lfs diff=lfs merge=lfs -text
+tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-safari.png filter=lfs diff=lfs merge=lfs -text
+tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-chromium.png filter=lfs diff=lfs merge=lfs -text
+tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-firefox.png filter=lfs diff=lfs merge=lfs -text
+tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-iphone.png filter=lfs diff=lfs merge=lfs -text
+tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-safari.png filter=lfs diff=lfs merge=lfs -text
+tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-chromium.png filter=lfs diff=lfs merge=lfs -text
+tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-android.png filter=lfs diff=lfs merge=lfs -text
diff --git a/lib/package.json b/lib/package.json
index cf44fc6..13c6832 100644
--- a/lib/package.json
+++ b/lib/package.json
@@ -29,9 +29,10 @@
"prepack": "pnpm build",
"release": "changelogen --release --clean",
"test": "playwright test",
+ "test:local": "docker build -t usegl . && docker run --rm -v $(pwd)/test-results:/app/test-results usegl /bin/sh -c 'xvfb-run pnpm run test'",
"test:ui": "playwright test --ui",
- "typecheck": "tsc --noEmit && astro check",
- "updateScreenshots": "docker build -t usegl . && docker run --rm -v $(pwd)/test-results:/app/test-results -v $(pwd)/tests/__screenshots__:/app/tests/__screenshots__ usegl"
+ "test:update": "docker build -t usegl . && docker run --rm -v $(pwd)/test-results:/app/test-results -v $(pwd)/tests/__screenshots__:/app/tests/__screenshots__ usegl",
+ "typecheck": "tsc --noEmit && astro check"
},
"devDependencies": {
"@astrojs/check": "0.9.4",
diff --git a/lib/playground/src/components/GlobalPlayPause.astro b/lib/playground/src/components/GlobalPlayPause.astro
new file mode 100644
index 0000000..b449060
--- /dev/null
+++ b/lib/playground/src/components/GlobalPlayPause.astro
@@ -0,0 +1,25 @@
+---
+import PlayPause from "./PlayPause.astro";
+---
+
+
+
+
+
+
diff --git a/lib/playground/src/components/PlayPause.astro b/lib/playground/src/components/PlayPause.astro
new file mode 100644
index 0000000..3cbf8ea
--- /dev/null
+++ b/lib/playground/src/components/PlayPause.astro
@@ -0,0 +1,73 @@
+
+
+
diff --git a/lib/playground/src/components/RenderCount.astro b/lib/playground/src/components/RenderCount.astro
index af66013..a6519fe 100644
--- a/lib/playground/src/components/RenderCount.astro
+++ b/lib/playground/src/components/RenderCount.astro
@@ -4,7 +4,7 @@
#renders {
color: white;
position: absolute;
- bottom: 1.5em;
- right: 1.5em;
+ bottom: 1.5rem;
+ right: 1.5rem;
}
diff --git a/lib/playground/src/layouts/Layout.astro b/lib/playground/src/layouts/Layout.astro
index 40edce9..a6cf2f7 100644
--- a/lib/playground/src/layouts/Layout.astro
+++ b/lib/playground/src/layouts/Layout.astro
@@ -37,6 +37,7 @@ import RenderCount from "../components/RenderCount.astro";
grid-template-columns: auto 1fr;
font-family: Verdana, Geneva, Tahoma, sans-serif;
background: black;
+ color: white;
color-scheme: dark;
}
diff --git a/lib/playground/src/pages/blob.astro b/lib/playground/src/pages/blob.astro
index cf21047..474d712 100644
--- a/lib/playground/src/pages/blob.astro
+++ b/lib/playground/src/pages/blob.astro
@@ -1,9 +1,10 @@
---
+import GlobalPlayPause from "../components/GlobalPlayPause.astro";
import Layout from "../layouts/Layout.astro";
---
+
diff --git a/lib/playground/src/pages/gradient.astro b/lib/playground/src/pages/gradient.astro
index 95ccfd1..45ec521 100644
--- a/lib/playground/src/pages/gradient.astro
+++ b/lib/playground/src/pages/gradient.astro
@@ -1,36 +1,57 @@
---
+import GlobalPlayPause from "../components/GlobalPlayPause.astro";
import Layout from "../layouts/Layout.astro";
---
+
diff --git a/lib/playground/src/pages/particles.astro b/lib/playground/src/pages/particles.astro
index 7f1eaa8..042afc5 100644
--- a/lib/playground/src/pages/particles.astro
+++ b/lib/playground/src/pages/particles.astro
@@ -1,9 +1,10 @@
---
+import GlobalPlayPause from "../components/GlobalPlayPause.astro";
import Layout from "../layouts/Layout.astro";
---
+
diff --git a/lib/playground/src/pages/pause.astro b/lib/playground/src/pages/pause.astro
new file mode 100644
index 0000000..bb3b3ec
--- /dev/null
+++ b/lib/playground/src/pages/pause.astro
@@ -0,0 +1,158 @@
+---
+import PlayPause from "../components/PlayPause.astro";
+import Layout from "../layouts/Layout.astro";
+---
+
+
+
+
+
+
+
+
diff --git a/lib/playwright.config.ts b/lib/playwright.config.ts
index b7bd284..8fef492 100644
--- a/lib/playwright.config.ts
+++ b/lib/playwright.config.ts
@@ -3,6 +3,8 @@ import { defineConfig, devices } from "@playwright/test";
const desktopViewport = { width: 800, height: 400 };
const mobileViewport = { width: 360, height: 640 };
+const serverUrl = "http://localhost:4321";
+
/**
* See https://playwright.dev/docs/test-configuration.
*/
@@ -12,12 +14,12 @@ export default defineConfig({
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
- /* Retry on CI only */
- retries: process.env.CI ? 2 : 0,
+ retries: 3,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: "html",
use: {
trace: "on-first-retry",
+ baseURL: serverUrl,
},
snapshotPathTemplate: "{testDir}/__screenshots__/{testName}/{testName}-{projectName}{ext}",
expect: {
@@ -32,7 +34,7 @@ export default defineConfig({
...devices["Desktop Chrome"],
viewport: desktopViewport,
launchOptions: {
- args: ["--use-gl=egl", "--ignore-gpu-blocklist", "--use-gl=angle"],
+ args: ["--use-angle=gl"],
},
},
},
@@ -67,7 +69,7 @@ export default defineConfig({
],
webServer: {
command: "pnpm dev",
- url: "http://localhost:4321",
+ url: serverUrl,
reuseExistingServer: !process.env.CI,
},
});
diff --git a/lib/src/helpers/loop.ts b/lib/src/helpers/loop.ts
deleted file mode 100644
index 4f71b69..0000000
--- a/lib/src/helpers/loop.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export function loop(callback: ({ time, deltaTime }: { time: number; deltaTime: number }) => void) {
- const startTime = performance.now();
-
- requestAnimationFrame((time) => {
- const deltaTime = time - startTime;
- callback({ time, deltaTime });
- loop(callback);
- });
-}
diff --git a/lib/src/hooks/useLoop.ts b/lib/src/hooks/useLoop.ts
new file mode 100644
index 0000000..0053d7f
--- /dev/null
+++ b/lib/src/hooks/useLoop.ts
@@ -0,0 +1,115 @@
+interface LoopData {
+ /**
+ * time elapsed in milliseconds since the loop started, excluding pauses.
+ *
+ * This timer is paused when the loop is paused, to avoid jumps in animations. If you want to get the time elapsed including pauses, use `elapsedTime` instead.
+ */
+ time: number;
+ /**
+ * Δt in milliseconds since the previous loop iteration.
+ */
+ deltaTime: number;
+ /**
+ * time elapsed in milliseconds since the loop started, including pauses.
+ *
+ * This timer is NOT paused when the loop is paused, which can cause jumps in animations. If you want to get the time elapsed excluding pauses, use `time` instead.
+ */
+ elapsedTime: number;
+}
+
+export interface LoopOptions {
+ /**
+ * If true, the loop will start immediately.
+ *
+ * If false, the loop will start when the `play` method is called.
+ * @default true
+ */
+ immediate?: boolean;
+}
+
+interface LoopObj {
+ play: () => void;
+ pause: () => void;
+}
+
+const allLoops: Array = [];
+
+/**
+ * A custom hook that creates an animation loop.
+ * @param callback A function that will be called on every animation frame.
+ * @param options Options for the loop.
+ * @returns An object with `play` and `pause` methods to control the animation loop.
+ */
+export function useLoop(callback: ({ time, deltaTime }: LoopData) => void, options?: LoopOptions) {
+ let animationFrameHandle: number;
+ let pauseTime: number | null;
+ let loopStartTime: number;
+ let delay = 0;
+
+ const { immediate = true } = options || {};
+
+ function loopFn(previousTime: number, delay = 0) {
+ const currentTime = performance.now();
+ const elapsedTime = currentTime - loopStartTime;
+ const time = elapsedTime - delay;
+ const deltaTime = currentTime - previousTime;
+ callback({ time, elapsedTime, deltaTime });
+
+ animationFrameHandle = requestAnimationFrame(() => loopFn(currentTime, delay));
+ }
+
+ function play() {
+ const currentTime = performance.now();
+ if (loopStartTime === undefined) {
+ loopStartTime = performance.now();
+ }
+ delay += currentTime - (pauseTime || currentTime);
+ cancelAnimationFrame(animationFrameHandle);
+ animationFrameHandle = requestAnimationFrame(() => loopFn(currentTime, delay));
+ pauseTime = null;
+ }
+
+ function pause() {
+ if (pauseTime == null) {
+ pauseTime = performance.now();
+ }
+ cancelAnimationFrame(animationFrameHandle);
+ }
+
+ if (immediate) {
+ play();
+ }
+
+ const loop = {
+ /**
+ * Play the animation loop.
+ */
+ play,
+ /**
+ * Pause the animation loop.
+ */
+ pause,
+ };
+
+ allLoops.push(loop);
+
+ return loop;
+}
+
+/**
+ * Play all loops that have been registered with `useLoop`.
+ */
+export function playAllLoops() {
+ for (const loop of allLoops) {
+ loop.play();
+ }
+}
+
+/**
+ * Pause all loops that have been registered with `useLoop`.
+ */
+export function pauseAllLoops() {
+ for (const loop of allLoops) {
+ loop.pause();
+ }
+}
diff --git a/lib/src/hooks/useWebGLCanvas.ts b/lib/src/hooks/useWebGLCanvas.ts
index dd2eea3..cfc055a 100644
--- a/lib/src/hooks/useWebGLCanvas.ts
+++ b/lib/src/hooks/useWebGLCanvas.ts
@@ -1,12 +1,13 @@
import { onCanvasResize } from "../helpers/resize";
-import { loop } from "../helpers/loop";
import type { Attribute, DrawMode, PostEffect, Uniforms } from "../types";
import { useWebGLContext } from "./useWebGLContext";
import { useQuadRenderPass } from "./useQuadRenderPass";
import { useCompositor } from "./useCompositor";
import { findUniformName } from "../internal/findName";
+import type { LoopOptions } from "./useLoop";
+import { useLoop } from "./useLoop";
-interface Props {
+interface Props extends LoopOptions {
canvas: HTMLCanvasElement | OffscreenCanvas | string;
fragment: string;
vertex?: string;
@@ -25,6 +26,7 @@ export const useWebGLCanvas = (props: Props) => {
vertex,
dpr = window.devicePixelRatio,
postEffects = [],
+ immediate,
} = props;
const { gl, canvas, setSize: setCanvasSize } = useWebGLContext(canvasProp);
@@ -32,6 +34,27 @@ export const useWebGLCanvas = (props: Props) => {
const primaryPass = useQuadRenderPass(gl, props);
const compositor = useCompositor(gl, primaryPass, postEffects);
+ function render() {
+ compositor.render();
+ }
+
+ let requestedRender = false;
+
+ /**
+ * Request a render to be executed on the next animation frame.
+ * If this function is called multiple times before the next animation frame,
+ * the render will only be executed once.
+ */
+ function requestRender() {
+ if (requestedRender) return;
+ requestedRender = true;
+
+ requestAnimationFrame(() => {
+ requestedRender = false;
+ render();
+ });
+ }
+
for (const pass of compositor.allPasses) {
pass.onUpdated(requestRender);
}
@@ -43,11 +66,21 @@ export const useWebGLCanvas = (props: Props) => {
}
const timeUniformName = findUniformName(fragment + vertex, "time");
+ let play = () => {};
+ let pause = () => {};
if (timeUniformName && primaryPass.uniforms[timeUniformName] === undefined) {
- loop(({ time }) => {
- (primaryPass.uniforms as Record)[timeUniformName] = time / 500;
+ requestAnimationFrame(() => {
+ // use RAF to avoid triggering an extra render for the initialization of the time uniform
+ (primaryPass.uniforms as Record)[timeUniformName] = 0;
});
+
+ ({ play, pause } = useLoop(
+ ({ deltaTime }) => {
+ (primaryPass.uniforms as Record)[timeUniformName] += deltaTime / 500;
+ },
+ { immediate },
+ ));
}
if (canvas instanceof HTMLCanvasElement) {
@@ -57,32 +90,13 @@ export const useWebGLCanvas = (props: Props) => {
});
}
- function render() {
- compositor.render();
- }
-
- let requestedRender = false;
-
- /**
- * Request a render to be executed on the next animation frame.
- * If this function is called multiple times before the next animation frame,
- * the render will only be executed once.
- */
- function requestRender() {
- if (requestedRender) return;
- requestedRender = true;
-
- requestAnimationFrame(() => {
- requestedRender = false;
- render();
- });
- }
-
return {
gl,
render,
canvas,
setSize,
+ play,
+ pause,
dpr,
uniforms: primaryPass.uniforms,
onUpdated: primaryPass.onUpdated,
diff --git a/lib/src/index.ts b/lib/src/index.ts
index 23a1b2a..9ed6ff8 100644
--- a/lib/src/index.ts
+++ b/lib/src/index.ts
@@ -1,17 +1,17 @@
-export * from "./core/attribute";
-export * from "./core/buffer";
-export * from "./core/program";
-export * from "./core/renderTarget";
-export * from "./core/shader";
-export * from "./core/texture";
+export { setAttribute } from "./core/attribute";
+export { createAndBindBuffer } from "./core/buffer";
+export { createProgram } from "./core/program";
+export { createRenderTarget, setRenderTarget } from "./core/renderTarget";
+export { createShader } from "./core/shader";
+export { createTexture, loadTexture } from "./core/texture";
-export * from "./hooks/useCompositor";
-export * from "./hooks/useEffectPass";
-export * from "./hooks/useQuadRenderPass";
-export * from "./hooks/useRenderPass";
-export * from "./hooks/useWebGLCanvas";
-export * from "./hooks/useWebGLContext";
+export { useCompositor } from "./hooks/useCompositor";
+export { useEffectPass } from "./hooks/useEffectPass";
+export { useQuadRenderPass } from "./hooks/useQuadRenderPass";
+export { useRenderPass } from "./hooks/useRenderPass";
+export { useWebGLCanvas } from "./hooks/useWebGLCanvas";
+export { useWebGLContext } from "./hooks/useWebGLContext";
+export { useLoop, playAllLoops, pauseAllLoops } from "./hooks/useLoop";
-export * from "./helpers/loop";
-export * from "./helpers/pointer";
-export * from "./helpers/resize";
+export { onPointerEvents } from "./helpers/pointer";
+export { onCanvasResize } from "./helpers/resize";
diff --git a/lib/tests/__screenshots__/blob/blob-android.png b/lib/tests/__screenshots__/blob/blob-android.png
index c285550..2b53d87 100644
--- a/lib/tests/__screenshots__/blob/blob-android.png
+++ b/lib/tests/__screenshots__/blob/blob-android.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0b610ef8a7295a49952f06715aaef5470e1386905a88cb7b73f5841c180bca10
-size 7598
+oid sha256:3c96b566b1d817cf63c7ee21541e6f4cc5ccfeeb4d438bf8fdf81cdcae1eb12a
+size 7772
diff --git a/lib/tests/__screenshots__/blob/blob-chromium.png b/lib/tests/__screenshots__/blob/blob-chromium.png
index 449e685..a16ac22 100644
--- a/lib/tests/__screenshots__/blob/blob-chromium.png
+++ b/lib/tests/__screenshots__/blob/blob-chromium.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d353071f7164c0513d35814e18050824c171a9c36f427fb00198e5fd0830164d
-size 9911
+oid sha256:692a9842b4875c7939a1d1e4e935f8e1c28362ac2ebec569233eaacc168d9ded
+size 10251
diff --git a/lib/tests/__screenshots__/blob/blob-firefox.png b/lib/tests/__screenshots__/blob/blob-firefox.png
index a7b5e6e..9ccc556 100644
--- a/lib/tests/__screenshots__/blob/blob-firefox.png
+++ b/lib/tests/__screenshots__/blob/blob-firefox.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:127c173ec0faa0fcaf8db5c89f3b6f06ccc01cb2b0144f0c3c3215b1dd5cdd38
-size 18014
+oid sha256:f262c7bafdc8910ffd906b101b7546c2c17237c53087cc2d89ded4f30f4b220b
+size 18245
diff --git a/lib/tests/__screenshots__/blob/blob-iphone.png b/lib/tests/__screenshots__/blob/blob-iphone.png
index 701f662..29d95b1 100644
--- a/lib/tests/__screenshots__/blob/blob-iphone.png
+++ b/lib/tests/__screenshots__/blob/blob-iphone.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4433e30c6876711dfc1612b739484cf31cd3bc5adf9afdba00bc1644b4e16f20
-size 8381
+oid sha256:3749ea2b949bda42f1f645112e3ab975df2125c34bd73046e83e5202a83ec6af
+size 8650
diff --git a/lib/tests/__screenshots__/blob/blob-safari.png b/lib/tests/__screenshots__/blob/blob-safari.png
index 85878ce..59fbc74 100644
--- a/lib/tests/__screenshots__/blob/blob-safari.png
+++ b/lib/tests/__screenshots__/blob/blob-safari.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:271d6d5747638c8676ee5e3c53878a01ee13b612f4e49b9723dff91a2f7430c2
-size 9912
+oid sha256:a75c383cc946096dd285b3e7e71d7dbe7ab949c9405eb91a66b9084633e77f77
+size 10232
diff --git a/lib/tests/__screenshots__/bloom/bloom-chromium.png b/lib/tests/__screenshots__/bloom/bloom-chromium.png
index 6481846..8469981 100644
--- a/lib/tests/__screenshots__/bloom/bloom-chromium.png
+++ b/lib/tests/__screenshots__/bloom/bloom-chromium.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ccf76bb2c29d489913fb5b9241201035589223fbd12a1330db8a89be56c50b1c
-size 59900
+oid sha256:11ae1193336f44872d71ea920c3f009929f9ed502eecca9c3403d780eda56591
+size 67477
diff --git a/lib/tests/__screenshots__/circle/circle-chromium.png b/lib/tests/__screenshots__/circle/circle-chromium.png
index 5954d91..3414c05 100644
--- a/lib/tests/__screenshots__/circle/circle-chromium.png
+++ b/lib/tests/__screenshots__/circle/circle-chromium.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8c36b9493974a29bcaa5edf50a67c1399f4f00b2332ffc0a7ade7561722d7335
-size 8432
+oid sha256:48520f38f70e635dc6c6341e0a18dd6c2fce45cb4c29b0df17fc2bbbe4e0d8ba
+size 8444
diff --git a/lib/tests/__screenshots__/gradient/gradient-android.png b/lib/tests/__screenshots__/gradient/gradient-android.png
index b26b163..9a4b8c5 100644
--- a/lib/tests/__screenshots__/gradient/gradient-android.png
+++ b/lib/tests/__screenshots__/gradient/gradient-android.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:791383d82f62fd90f03136c5ce39fa2a28bc64668bff698f7caeda5b06651f6f
-size 4301
+oid sha256:ddb41d9b5273c312a92c540acab7cdaf3d585c1ede0877daaba174c9b0003fb8
+size 4424
diff --git a/lib/tests/__screenshots__/gradient/gradient-chromium.png b/lib/tests/__screenshots__/gradient/gradient-chromium.png
index 37b2a47..aec8af7 100644
--- a/lib/tests/__screenshots__/gradient/gradient-chromium.png
+++ b/lib/tests/__screenshots__/gradient/gradient-chromium.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:84f53c7d58fac6962fbb186ae7bbf5edb7a8940ab3672c213237957180d259fb
-size 5339
+oid sha256:ce4544d48409d7447cd3e26eb0c40fe48d75f50e54a1a920f5c2832cb4754850
+size 5799
diff --git a/lib/tests/__screenshots__/gradient/gradient-firefox.png b/lib/tests/__screenshots__/gradient/gradient-firefox.png
index 6a56ffb..128ca62 100644
--- a/lib/tests/__screenshots__/gradient/gradient-firefox.png
+++ b/lib/tests/__screenshots__/gradient/gradient-firefox.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:19485231f760a922c245613f8862998447b00d665e8c2089fa859c36f1ea29dd
-size 13737
+oid sha256:b83da802f944d5592764ac1b09865facb40da2304cd8ed2a36c3c34650504164
+size 17086
diff --git a/lib/tests/__screenshots__/gradient/gradient-iphone.png b/lib/tests/__screenshots__/gradient/gradient-iphone.png
index 7c1f037..c6d4613 100644
--- a/lib/tests/__screenshots__/gradient/gradient-iphone.png
+++ b/lib/tests/__screenshots__/gradient/gradient-iphone.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:49325d0de76beec1d134e4b67bb55319c06d217e52c70dbdfef31969ce93c06d
-size 4330
+oid sha256:8aa63fb71876451a71db2655e256eaabf8a000962a82aac9f62560103f65ede8
+size 4525
diff --git a/lib/tests/__screenshots__/gradient/gradient-safari.png b/lib/tests/__screenshots__/gradient/gradient-safari.png
index 23cb057..c981c1e 100644
--- a/lib/tests/__screenshots__/gradient/gradient-safari.png
+++ b/lib/tests/__screenshots__/gradient/gradient-safari.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:62e0463b064c93be45caeccbcfd0079d59badecb09b02c6d1d703afeff6c820c
-size 4980
+oid sha256:fd91e153b712447d8d46aa07b6407bb061361b3d3ec9d2c62d7c0000f5a666a3
+size 5394
diff --git a/lib/tests/__screenshots__/particles/particles-android.png b/lib/tests/__screenshots__/particles/particles-android.png
index cc7306f..d8f0ee5 100644
--- a/lib/tests/__screenshots__/particles/particles-android.png
+++ b/lib/tests/__screenshots__/particles/particles-android.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:bc1c49878bf2e89a8c0c9c0f1103ca383ce40f416ce9d52fc507507a2f03d3a0
-size 23267
+oid sha256:9d213060ded3a71a7cbc27b88c7c2b78db468fee00f304362fb1369e4bad1fd0
+size 23613
diff --git a/lib/tests/__screenshots__/particles/particles-chromium.png b/lib/tests/__screenshots__/particles/particles-chromium.png
index 4eb256c..67800b2 100644
--- a/lib/tests/__screenshots__/particles/particles-chromium.png
+++ b/lib/tests/__screenshots__/particles/particles-chromium.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:fa4a67d177681dace771b4a8de1f17be8503c61edc3d6e7f4f8194cf14f06a3f
-size 12380
+oid sha256:1eacbb04ca299aa82e827c32c6c4dc8011407279b46b6c06e63364bc7772b616
+size 12449
diff --git a/lib/tests/__screenshots__/particles/particles-firefox.png b/lib/tests/__screenshots__/particles/particles-firefox.png
index 7ccbc10..db79a4f 100644
--- a/lib/tests/__screenshots__/particles/particles-firefox.png
+++ b/lib/tests/__screenshots__/particles/particles-firefox.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:151369f10a2c3be4537ca3a664ac6ea49c305767046b7706041dad89301c4a91
-size 21963
+oid sha256:8ad021479867f9bd28af8934317b4583862e3581da71f8f0b30f4b15c4240f23
+size 22016
diff --git a/lib/tests/__screenshots__/particles/particles-iphone.png b/lib/tests/__screenshots__/particles/particles-iphone.png
index 86f2a60..b2ecf52 100644
--- a/lib/tests/__screenshots__/particles/particles-iphone.png
+++ b/lib/tests/__screenshots__/particles/particles-iphone.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4f676971e200d49c6771f1ba958d2fd762419108b344af7e6141fa12791f481b
-size 26791
+oid sha256:8960229e06812b4be67f65f9d3377d1569aa7a4174343d7227a411aa73942a5d
+size 26860
diff --git a/lib/tests/__screenshots__/particles/particles-safari.png b/lib/tests/__screenshots__/particles/particles-safari.png
index d280bed..46a2ccd 100644
--- a/lib/tests/__screenshots__/particles/particles-safari.png
+++ b/lib/tests/__screenshots__/particles/particles-safari.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:cf891879e69978edd001c038a9364e0261cadb513447a8c6865e8dae424f088e
-size 24970
+oid sha256:9b7a5031b71ac3a114599f84c8a865b1f952c34356d07104a46d4f48ab9e84d5
+size 24966
diff --git a/lib/tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-android.png b/lib/tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-android.png
new file mode 100644
index 0000000..a605b82
--- /dev/null
+++ b/lib/tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-android.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4e58a4d1f17ee9164664cab1ebea19ad00198cfdc8e3e7a5aeb429ba4de96a3f
+size 6338
diff --git a/lib/tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-chromium.png b/lib/tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-chromium.png
new file mode 100644
index 0000000..63a19b6
--- /dev/null
+++ b/lib/tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-chromium.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f1d3f93803709744810eea3b3cfbc80610c882ae053d8c7909b9bb3eb96e7f9c
+size 7516
diff --git a/lib/tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-firefox.png b/lib/tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-firefox.png
new file mode 100644
index 0000000..d2cd521
--- /dev/null
+++ b/lib/tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-firefox.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5ce4325d78887fbfe5f843b637b72311701537a6053d240a3c53214ea4d040e1
+size 13952
diff --git a/lib/tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-iphone.png b/lib/tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-iphone.png
new file mode 100644
index 0000000..d3540cb
--- /dev/null
+++ b/lib/tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-iphone.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d33d1b46f6256c27bc237af7412fb528ea2b7c0e231912dd530533c68bab480c
+size 6181
diff --git a/lib/tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-safari.png b/lib/tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-safari.png
new file mode 100644
index 0000000..d5a7488
--- /dev/null
+++ b/lib/tests/__screenshots__/play-pause-controls---global/play-pause-controls---global-safari.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:eb4bbd913bebcbde36c59a8fb092a15f8b48da7ee1687a4ae0c07908d443898c
+size 7288
diff --git a/lib/tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-android.png b/lib/tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-android.png
new file mode 100644
index 0000000..389d06f
--- /dev/null
+++ b/lib/tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-android.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8c978064a8f2a8aa0f21f17f1df6fbc58f64c0d09dceef29a97bcd41cfc852ee
+size 6355
diff --git a/lib/tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-chromium.png b/lib/tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-chromium.png
new file mode 100644
index 0000000..546ca5b
--- /dev/null
+++ b/lib/tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-chromium.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:96109fbb821d76ae6ff9e7628dd561bb751dbe974d6598a1c84e0f2527c356fc
+size 7612
diff --git a/lib/tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-firefox.png b/lib/tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-firefox.png
new file mode 100644
index 0000000..f2241e3
--- /dev/null
+++ b/lib/tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-firefox.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a8a71d8531290393f17118049e925bd9749570f02b81969d2088adf841fc0f92
+size 14541
diff --git a/lib/tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-iphone.png b/lib/tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-iphone.png
new file mode 100644
index 0000000..5949bf3
--- /dev/null
+++ b/lib/tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-iphone.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:714840f1a03b4fe1c270550db1237b85555736658ba6a2a53914896857216305
+size 6961
diff --git a/lib/tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-safari.png b/lib/tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-safari.png
new file mode 100644
index 0000000..df2bc5c
--- /dev/null
+++ b/lib/tests/__screenshots__/play-pause-controls---local/play-pause-controls---local-safari.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0eb9062bf89e59d8f8c9fd7fbefb9822cf909e66596d14dfeaf68592f53ff1c5
+size 8289
diff --git a/lib/tests/__screenshots__/pointer-move/pointer-move-android.png b/lib/tests/__screenshots__/pointer-move/pointer-move-android.png
index 9c84536..2431439 100644
--- a/lib/tests/__screenshots__/pointer-move/pointer-move-android.png
+++ b/lib/tests/__screenshots__/pointer-move/pointer-move-android.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5b2d0981aa3adee2bb4e76748422ae9d81aef8ef9fe24ce4132df3fc7a0443c8
-size 4311
+oid sha256:467a7ebfae8341992b8233ad976953ca666ffa69d35e8992c18a38e4d51b3bfc
+size 4455
diff --git a/lib/tests/__screenshots__/pointer/pointer-chromium.png b/lib/tests/__screenshots__/pointer/pointer-chromium.png
index 4e0948f..2dc71e3 100644
--- a/lib/tests/__screenshots__/pointer/pointer-chromium.png
+++ b/lib/tests/__screenshots__/pointer/pointer-chromium.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:411fa8c905f9b0f414b5490d34ad3d15e835d7861d7a5c8f8cf3d8bbd448a476
-size 4772
+oid sha256:c82b9367203ac73af41a68e5734bb14fe2b6fe6f5c219fff0ed9911c2db37c20
+size 4722
diff --git a/lib/tests/__screenshots__/texture/texture-chromium.png b/lib/tests/__screenshots__/texture/texture-chromium.png
index 630a036..03a4328 100644
--- a/lib/tests/__screenshots__/texture/texture-chromium.png
+++ b/lib/tests/__screenshots__/texture/texture-chromium.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e36532389e9c5009aa0dcef936e761caafc4b79ce74493e398ea20a660a61356
-size 176766
+oid sha256:be3daa26a69c64387037937904cc83e98b1509e8e600ebd79e3a4b5a20d0bb39
+size 176782
diff --git a/lib/tests/screenshots.spec.ts b/lib/tests/screenshots.spec.ts
index 6d221a8..a642975 100644
--- a/lib/tests/screenshots.spec.ts
+++ b/lib/tests/screenshots.spec.ts
@@ -2,23 +2,91 @@ import { test, expect } from "@playwright/test";
import { routes } from "../playground/src/components/routes";
-for (const route of routes) {
- test(route, async ({ page }) => {
- await page.goto(`http://localhost:4321/${route}`);
+const ignoreRoutes = new Set(["pause"]);
+
+const routesToTest = routes.filter((route) => !ignoreRoutes.has(route));
+
+for (const route of routesToTest) {
+ test(route, async ({ page, baseURL }) => {
+ await page.goto(`${baseURL}/${route}`);
await expect(page.locator("main")).toHaveScreenshot();
});
}
-test("pointer move", async ({ page, viewport }) => {
- await page.goto(`http://localhost:4321/pointer`);
+test("pointer move", async ({ page, viewport, baseURL }) => {
+ await page.goto(`${baseURL}/pointer`);
await expect(page.getByText("Renders: 1")).toBeVisible();
await page.mouse.move((viewport?.width || 0) * 0.5, (viewport?.height || 0) * 0.45);
await expect(page.getByText("Renders: 2")).toBeVisible();
- await page.waitForTimeout(200);
+ await page.waitForTimeout(100);
+
+ await expect(page.locator("main")).toHaveScreenshot();
+});
+
+test("play / pause controls - local", async ({ page, baseURL }) => {
+ await page.goto(`${baseURL}/pause`);
+
+ await expect(page.getByText("Renders: 1")).toBeVisible();
+
+ await page.evaluate(() => {
+ return new Promise((resolve) => {
+ const playPauseBtn = document.querySelector(
+ ".controls.local button:nth-of-type(1)",
+ )!;
+
+ playPauseBtn.click();
+
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ playPauseBtn.click();
+ resolve();
+ });
+ });
+ });
+ });
+ });
+ });
+
+ await expect(page.getByText("Renders: 5")).toBeVisible();
+
+ await page.waitForTimeout(100);
+
+ await expect(page.locator("main")).toHaveScreenshot();
+});
+
+test("play / pause controls - global", async ({ page, baseURL }) => {
+ await page.goto(`${baseURL}/pause`);
+
+ await expect(page.getByText("Renders: 1")).toBeVisible();
+
+ await page.evaluate(() => {
+ return new Promise((resolve) => {
+ const playPauseBtn = document.querySelector(".controls.global button")!;
+
+ playPauseBtn.click();
+
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ playPauseBtn.click();
+ resolve();
+ });
+ });
+ });
+ });
+ });
+ });
+
+ await expect(page.getByText("Renders: 5")).toBeVisible();
+
+ await page.waitForTimeout(100);
await expect(page.locator("main")).toHaveScreenshot();
});