rustdoc: avoid whole page layout on each frame

This makes two changes, based on experimenting with different browsers:

- It debounces resizing the body text. This improves behavior on huge
  pages like struct.Vec.html, because it doesn't have to do layout.

- It does the sidebar width updates directly on the sidebar instead of
  doing it on the `<HTML>` element. Doing it on `<HTML>` causes it
  to recalculate CSS for the entire document, also causing layout jank.
This commit is contained in:
Michael Howell 2023-10-07 11:10:36 -07:00
parent 9aabfd5892
commit 77fa09d9a1
3 changed files with 67 additions and 9 deletions

View File

@ -389,12 +389,14 @@ img {
.sidebar {
font-size: 0.875rem;
flex: 0 0 var(--desktop-sidebar-width);
width: var(--desktop-sidebar-width);
overflow-y: scroll;
overscroll-behavior: contain;
position: sticky;
height: 100vh;
top: 0;
left: 0;
z-index: 100;
}
.rustdoc.src .sidebar {
@ -403,7 +405,6 @@ img {
overflow-x: hidden;
/* The sidebar is by default hidden */
overflow-y: hidden;
z-index: 1;
}
.hide-sidebar .sidebar,
@ -415,8 +416,8 @@ img {
touch-action: none;
width: 9px;
cursor: col-resize;
z-index: 10;
position: absolute;
z-index: 200;
position: fixed;
height: 100%;
/* make sure there's a 1px gap between the scrollbar and resize handle */
left: calc(var(--desktop-sidebar-width) + 1px);
@ -445,6 +446,14 @@ img {
cursor: col-resize !important;
}
.sidebar-resizing .sidebar {
position: fixed;
z-index: 100;
}
.sidebar-resizing > body {
padding-left: var(--resizing-sidebar-width);
}
.sidebar-resizer:hover,
.sidebar-resizer:active,
.sidebar-resizer:focus,
@ -474,7 +483,7 @@ img {
.sidebar-resizer.active {
/* make the resize tool bigger when actually resizing, to avoid :hover styles on other stuff
while resizing */
while resizing */
padding: 0 140px;
width: 2px;
margin-left: -140px;
@ -503,6 +512,7 @@ img {
.src-sidebar-expanded .src .sidebar {
overflow-y: auto;
flex-basis: var(--src-sidebar-width);
width: var(--src-sidebar-width);
}
.src-sidebar-expanded .src .sidebar > *:not(#src-sidebar-toggle) {

View File

@ -1337,6 +1337,13 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
// it gets the sidebar to restore its size.
let desiredSidebarSize = null;
// Sidebar resize debouncer.
//
// The sidebar itself is resized instantly, but the body HTML can be too
// big for that, causing reflow jank. To reduce this, we queue up a separate
// animation frame and throttle it.
let pendingSidebarResizingFrame = false;
// If this page has no sidebar at all, bail out.
const resizer = document.querySelector(".sidebar-resizer");
const sidebar = document.querySelector(".sidebar");
@ -1360,12 +1367,26 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
if (isSrcPage) {
window.rustdocCloseSourceSidebar();
updateLocalStorage("src-sidebar-width", null);
// [RUSTDOCIMPL] CSS variable fast path
//
// The sidebar width variable is attached to the <html> element by
// storage.js, because the sidebar and resizer don't exist yet.
// But the resize code, in `resize()`, sets the property on the
// sidebar and resizer elements (which are the only elements that
// use the variable) to avoid recalculating CSS on the entire
// document on every frame.
//
// So, to clear it, we need to clear all three.
document.documentElement.style.removeProperty("--src-sidebar-width");
sidebar.style.removeProperty("--src-sidebar-width");
resizer.style.removeProperty("--src-sidebar-width");
} else {
addClass(document.documentElement, "hide-sidebar");
updateLocalStorage("hide-sidebar", "true");
updateLocalStorage("desktop-sidebar-width", null);
document.documentElement.style.removeProperty("--desktop-sidebar-width");
sidebar.style.removeProperty("--desktop-sidebar-width");
resizer.style.removeProperty("--desktop-sidebar-width");
}
}
@ -1384,15 +1405,27 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
}
}
// Call this to set the correct CSS variable and setting.
// This function doesn't enforce size constraints. Do that before calling it!
/**
* Call this to set the correct CSS variable and setting.
* This function doesn't enforce size constraints. Do that before calling it!
*
* @param {number} size - CSS px width of the sidebar.
*/
function changeSidebarSize(size) {
if (isSrcPage) {
updateLocalStorage("src-sidebar-width", size);
document.documentElement.style.setProperty("--src-sidebar-width", size + "px");
// [RUSTDOCIMPL] CSS variable fast path
//
// While this property is set on the HTML element at load time,
// because the sidebar isn't actually loaded yet,
// we scope this update to the sidebar to avoid hitting a slow
// path in WebKit.
sidebar.style.setProperty("--src-sidebar-width", size + "px");
resizer.style.setProperty("--src-sidebar-width", size + "px");
} else {
updateLocalStorage("desktop-sidebar-width", size);
document.documentElement.style.setProperty("--desktop-sidebar-width", size + "px");
sidebar.style.setProperty("--desktop-sidebar-width", size + "px");
resizer.style.setProperty("--desktop-sidebar-width", size + "px");
}
}
@ -1424,6 +1457,19 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
const constrainedPos = Math.min(pos, window.innerWidth - BODY_MIN, SIDEBAR_MAX);
changeSidebarSize(constrainedPos);
desiredSidebarSize = constrainedPos;
if (pendingSidebarResizingFrame !== false) {
clearTimeout(pendingSidebarResizingFrame);
}
pendingSidebarResizingFrame = setTimeout(() => {
if (currentPointerId === null || pendingSidebarResizingFrame === false) {
return;
}
pendingSidebarResizingFrame = false;
document.documentElement.style.setProperty(
"--resizing-sidebar-width",
desiredSidebarSize + "px"
);
}, 100);
}
}
// Respond to the window resize event.
@ -1447,6 +1493,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
window.removeEventListener("pointermove", resize, false);
window.removeEventListener("pointerup", stopResize, false);
removeClass(document.documentElement, "sidebar-resizing");
document.documentElement.style.removeProperty( "--resizing-sidebar-width");
if (resizer.releasePointerCapture) {
resizer.releasePointerCapture(currentPointerId);
currentPointerId = null;
@ -1472,6 +1519,8 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
window.addEventListener("pointerup", stopResize, false);
addClass(resizer, "active");
addClass(document.documentElement, "sidebar-resizing");
const pos = e.clientX - sidebar.offsetLeft - 3;
document.documentElement.style.setProperty( "--resizing-sidebar-width", pos + "px");
desiredSidebarSize = null;
}
resizer.addEventListener("pointerdown", initResize, false);

View File

@ -100,7 +100,6 @@ pub(crate) fn for_each<E>(f: impl Fn(&StaticFile) -> Result<(), E>) -> Result<()
storage_js => "static/js/storage.js",
scrape_examples_js => "static/js/scrape-examples.js",
wheel_svg => "static/images/wheel.svg",
sidebar_svg => "static/images/sidebar.svg",
clipboard_svg => "static/images/clipboard.svg",
copyright => "static/COPYRIGHT.txt",
license_apache => "static/LICENSE-APACHE.txt",