Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix flickering during view transitions #8772

Merged
merged 3 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/seven-seas-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fix flickering during view transitions
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
import { ViewTransitions } from 'astro:transitions';
---
<html>
<head>
<ViewTransitions/>
</head>
<body>
<p>Local transitions</p>
<slot/>
<script>
document.addEventListener("astro:after-swap", () => {
document.querySelector("p").addEventListener("transitionstart", () => {
console.info("transitionstart");
});
document.documentElement.setAttribute("class", "blue");
});
document.dispatchEvent(new Event("astro:after-swap"));
</script>
</body>
<style>
p {
transition: background-color 1s;
}
p {
background-color: #0ee;
color: red;
}
.blue p {
background-color: #ee0;
color: blue;
}
</style>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
import ListenerLayout from '../components/listener-layout.astro';
---
<ListenerLayout>
<a id="totwo" href="/listener-two">Go to listener two</a>
</ListenerLayout>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
import ListenerLayout from '../components/listener-layout.astro';
---
<ListenerLayout>
<a id="toone" href="/listener-one">Go to listener one</a>
</ListenerLayout>
22 changes: 22 additions & 0 deletions packages/astro/e2e/view-transitions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,28 @@ test.describe('View Transitions', () => {
await expect(h, 'imported CSS updated').toHaveCSS('background-color', 'rgba(0, 0, 0, 0)');
});

test('No page rendering during swap()', async ({ page, astro }) => {
let transitions = 0;
page.on('console', (msg) => {
if (msg.type() === 'info' && msg.text() === 'transitionstart') ++transitions;
});

// Go to page 1
await page.goto(astro.resolveUrl('/listener-one'));
let p = page.locator('#totwo');
await expect(p, 'should have content').toHaveText('Go to listener two');
// on load a CSS transition is started triggered by a class on the html element
expect(transitions).toEqual(1);

// go to page 2
await page.click('#totwo');
p = page.locator('#toone');
await expect(p, 'should have content').toHaveText('Go to listener one');
// swap() resets that class, the after-swap listener sets it again.
// the temporarily missing class must not trigger page rendering
expect(transitions).toEqual(1);
});

test('click hash links does not do navigation', async ({ page, astro }) => {
// Go to page 1
await page.goto(astro.resolveUrl('/one'));
Expand Down
12 changes: 10 additions & 2 deletions packages/astro/src/transitions/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,18 +146,24 @@ function isInfinite(animation: Animation) {

const updateHistoryAndScrollPosition = (toLocation: URL, replace: boolean, intraPage: boolean) => {
const fresh = !samePage(toLocation);
let scrolledToTop = false;
if (toLocation.href !== location.href) {
if (replace) {
history.replaceState({ ...history.state }, '', toLocation.href);
} else {
history.replaceState({ ...history.state, intraPage }, '');
history.pushState({ index: ++currentHistoryIndex, scrollX, scrollY }, '', toLocation.href);
history.pushState(
{ index: ++currentHistoryIndex, scrollX: 0, scrollY: 0 },
'',
toLocation.href
);
}
// now we are on the new page for non-history navigations!
// (with history navigation page change happens before popstate is fired)
// freshly loaded pages start from the top
if (fresh) {
scrollTo({ left: 0, top: 0, behavior: 'instant' });
scrolledToTop = true;
}
}
if (toLocation.hash) {
Expand All @@ -166,7 +172,9 @@ const updateHistoryAndScrollPosition = (toLocation: URL, replace: boolean, intra
// that won't reload the page but instead scroll to the fragment
location.href = toLocation.href;
} else {
scrollTo({ left: 0, top: 0, behavior: 'instant' });
if (!scrolledToTop) {
scrollTo({ left: 0, top: 0, behavior: 'instant' });
}
}
};

Expand Down