Prevent flashing during navigation
This commit is contained in:
@@ -45,6 +45,21 @@
|
|||||||
<div class="shell">
|
<div class="shell">
|
||||||
{{if not .EditMode}}<aside class="tree-sidebar col">
|
{{if not .EditMode}}<aside class="tree-sidebar col">
|
||||||
<div class="tree-scroll"></div>
|
<div class="tree-scroll"></div>
|
||||||
|
<script>
|
||||||
|
// Paint the previous page's tree before first render so the rail
|
||||||
|
// doesn't flash empty; tree-sidebar.js swaps in the live tree.
|
||||||
|
(function () {
|
||||||
|
try {
|
||||||
|
var s = JSON.parse(sessionStorage.getItem("tree-snapshot"));
|
||||||
|
if (!s) return;
|
||||||
|
var el = document.currentScript.previousElementSibling;
|
||||||
|
el.innerHTML = s.html;
|
||||||
|
var a = el.querySelector('a.tree-name[href="' + CSS.escape(location.pathname.replace(/\/*$/, "/")) + '"]');
|
||||||
|
if (a) a.parentNode.classList.add("is-active");
|
||||||
|
el.scrollTop = s.top;
|
||||||
|
} catch (e) {}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
</aside>{{end}}
|
</aside>{{end}}
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<main>
|
<main>
|
||||||
|
|||||||
@@ -48,6 +48,28 @@
|
|||||||
--reading-width: 60rem;
|
--reading-width: 60rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Cross-document view transitions: page navigations stay full reloads, but the
|
||||||
|
browser holds the old frame until the new page is ready and swaps them
|
||||||
|
without a blank flash. The header and tree rail are named so they swap in
|
||||||
|
place (no crossfade) — only the rest of the page fades, and only briefly. */
|
||||||
|
@view-transition { navigation: auto; }
|
||||||
|
body > header { view-transition-name: app-header; }
|
||||||
|
.tree-sidebar { view-transition-name: tree-sidebar; }
|
||||||
|
::view-transition-group(*),
|
||||||
|
::view-transition-old(*),
|
||||||
|
::view-transition-new(*) { animation-duration: 0.12s; }
|
||||||
|
::view-transition-group(app-header),
|
||||||
|
::view-transition-group(tree-sidebar),
|
||||||
|
::view-transition-old(app-header),
|
||||||
|
::view-transition-new(app-header),
|
||||||
|
::view-transition-old(tree-sidebar),
|
||||||
|
::view-transition-new(tree-sidebar) { animation: none; }
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
::view-transition-group(*),
|
||||||
|
::view-transition-old(*),
|
||||||
|
::view-transition-new(*) { animation: none; }
|
||||||
|
}
|
||||||
|
|
||||||
/* Anchor / TOC jumps are handled by the .center scroll container's own
|
/* Anchor / TOC jumps are handled by the .center scroll container's own
|
||||||
scroll-padding-top (the header no longer overlaps content in the app-shell),
|
scroll-padding-top (the header no longer overlaps content in the app-shell),
|
||||||
so no html-level scroll padding is needed. */
|
so no html-level scroll padding is needed. */
|
||||||
|
|||||||
+30
-1
@@ -40,6 +40,7 @@
|
|||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
var activeRow = null
|
var activeRow = null
|
||||||
|
var live = false // set once the fetched tree is on screen
|
||||||
|
|
||||||
function disabledRow(text) {
|
function disabledRow(text) {
|
||||||
var row = document.createElement("div")
|
var row = document.createElement("div")
|
||||||
@@ -169,15 +170,26 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
container.textContent = ""
|
// If the layout pre-painted a snapshot of the previous page's tree, keep it
|
||||||
|
// on screen and build the live tree off-DOM, swapping it in once ready —
|
||||||
|
// no "…" placeholder, no scroll jump.
|
||||||
var listing = document.createElement("div")
|
var listing = document.createElement("div")
|
||||||
|
var hasSnapshot = container.firstChild !== null
|
||||||
|
if (!hasSnapshot) {
|
||||||
container.appendChild(listing)
|
container.appendChild(listing)
|
||||||
listing.appendChild(disabledRow("…"))
|
listing.appendChild(disabledRow("…"))
|
||||||
|
}
|
||||||
|
|
||||||
fetchFolder("/", activePath === "/" ? "" : activePath)
|
fetchFolder("/", activePath === "/" ? "" : activePath)
|
||||||
.then(function (resp) {
|
.then(function (resp) {
|
||||||
listing.textContent = ""
|
listing.textContent = ""
|
||||||
renderInto(listing, "/", resp.entries)
|
renderInto(listing, "/", resp.entries)
|
||||||
|
if (hasSnapshot) {
|
||||||
|
var top = container.scrollTop
|
||||||
|
container.replaceChildren(listing)
|
||||||
|
container.scrollTop = top
|
||||||
|
}
|
||||||
|
live = true
|
||||||
if (activeRow) {
|
if (activeRow) {
|
||||||
try {
|
try {
|
||||||
activeRow.scrollIntoView({ block: "nearest" })
|
activeRow.scrollIntoView({ block: "nearest" })
|
||||||
@@ -185,17 +197,34 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
|
live = false
|
||||||
|
container.replaceChildren(listing)
|
||||||
listing.textContent = ""
|
listing.textContent = ""
|
||||||
var err = document.createElement("div")
|
var err = document.createElement("div")
|
||||||
err.className = "tree-row"
|
err.className = "tree-row"
|
||||||
err.textContent = "(failed — tap to retry)"
|
err.textContent = "(failed — tap to retry)"
|
||||||
err.addEventListener("click", function () {
|
err.addEventListener("click", function () {
|
||||||
|
container.textContent = ""
|
||||||
render()
|
render()
|
||||||
})
|
})
|
||||||
listing.appendChild(err)
|
listing.appendChild(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Snapshot the tree (markup + scroll) on the way out so the next page's
|
||||||
|
// layout can paint it immediately. Skipped until the live tree has rendered,
|
||||||
|
// so a placeholder or error row never becomes the snapshot. The active
|
||||||
|
// highlight is left out; the next page marks its own row.
|
||||||
|
window.addEventListener("pagehide", function () {
|
||||||
|
if (!live) return
|
||||||
|
if (activeRow) activeRow.classList.remove("is-active")
|
||||||
|
var html = container.innerHTML
|
||||||
|
if (activeRow) activeRow.classList.add("is-active") // bfcache may restore this page
|
||||||
|
try {
|
||||||
|
sessionStorage.setItem("tree-snapshot", JSON.stringify({ html: html, top: container.scrollTop }))
|
||||||
|
} catch (e) {}
|
||||||
|
})
|
||||||
|
|
||||||
function setupMenuItem() {
|
function setupMenuItem() {
|
||||||
var specials = document.querySelector(".actions-special")
|
var specials = document.querySelector(".actions-special")
|
||||||
if (!specials) return
|
if (!specials) return
|
||||||
|
|||||||
Reference in New Issue
Block a user