为你的博客制作阅读进度条📊
我们可以在标准博客中添加任何可以增强阅读体验的内容吗?
阅读进度条怎么样?
进度条
进度条是固定的,只有在帖子进入视野时才会显示。向下滚动,你会看到一个时髦的紫色进度条。💜
HTML
<progress id="reading-progress" max="100" value="0" ></progress>
我选择使用<progress>
,这是适合该工作的语义 HTML 匹配,向右滑动!✅o
我们
使用以下属性:
max
描述任务所需的工作量。我们将其设置为 100,可能值的范围是 0 到 100。value
指定任务已完成的程度。我们赋予它的初始值为 0,当用户滚动时,我们会在 JavaScript 中更新此值。
CSS
样式化并不是一件简单的事<progress>
,你需要做一些额外的工作才能使用它,而不是<div>
像大多数人那样去使用它!🙄😄 你可以阅读这篇文章来了解更详细的信息。
我们希望进度条固定在文章顶部,因此我们使用属性:position: sticky;
和top: 0;
。我们使用所有浏览器前缀来避免任何兼容性问题。
对于栏目本身的样式,我通过使用 CSS 变量明确了样式。正如您所见,为了保持一致的样式,需要同时支持 3 个不同的浏览器组,并使用不同的属性来实现相同的效果。它在 Firefox 和 Chrome 中看起来确实不错,但我还没有在其他浏览器中测试过。
:root {
--progress-width: 100%;
--progress-height: 8px;
--progress-bar-color: rgb(115, 0, 209);
--progress-bg: none;
--progress-border-radius: 5px;
}
progress {
position: -moz-sticky;
position: -ms-sticky;
position: -o-sticky;
position: -webkit-sticky;
position: sticky;
top: 0;
}
/*Target this for applying styles*/
progress[value] {
/* Reset the default appearance */
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
/* Get rid of default border in Firefox. */
border: none;
width: var(--progress-width);
height: var(--progress-height);
/* Firefox: any style applied here applies to the container. */
background-color: var(--progress-bg);
border-radius: var(--progress-border-radius);
/* For IE10 */
color: var(--progress-bar-color);
}
/* For Firefox: progress bar */
progress[value]::-moz-progress-bar {
background-color: var(--progress-bar-color);
border-radius: var(--progress-border-radius);
}
/* WebKit/Blink browsers:
-webkit-progress-bar is to style the container */
progress[value]::-webkit-progress-bar {
background-color: var(--progress-bg);
border-radius: var(--progress-border-radius);
}
/*-webkit-progress-value is to style the progress bar.*/
progress[value]::-webkit-progress-value {
background-color: var(--progress-bar-color);
border-radius: var(--progress-border-radius);
}
JavaScript
JavaScript 代码相当简单,希望它不言自明!😅
我使用了一个Intersection Observer,它会告诉我们帖子何时可见。我们使用它来确保仅在可见时更新进度条。现在,浏览器已经很好地支持了这个 API 。
为了确定我们在文章中的当前位置,我们检查了边界框的顶部坐标。如果是负数,则表示我们已经向内(或超出)了文章的某个范围。我们将这个值除以边界框的高度,得到滚动的百分比。
最后一部分是为页面(窗口)添加一个滚动监听器,它调用我们的函数来更新进度条。
const post = document.getElementById("post");
const progress = document.getElementById("reading-progress");
let inViewport = false;
let observer = new IntersectionObserver(handler);
observer.observe(post);
//Whenever the post comes in or out of view, this handler is invoked.
function handler(entries, observer) {
for (entry of entries) {
if (entry.isIntersecting) {
inViewport = true;
} else {
inViewport = false;
}
}
}
// Get the percentage scrolled of an element. It returns zero if its not in view.
function getScrollProgress(el) {
let coords = el.getBoundingClientRect();
let height = coords.height;
let progressPercentage = 0;
if (inViewport && coords.top < 0) {
progressPercentage = (Math.abs(coords.top) / height) * 100;
}
return progressPercentage;
}
function showReadingProgress() {
progress.setAttribute("value", getScrollProgress(post));
}
//scroll event listener
window.onscroll = showReadingProgress;
优化代码
我们的代码性能还不错,但还可以改进。如果你感兴趣,请继续阅读!
我们的代码中有两部分导致其性能不佳。
第一部分是,某些方法会触发浏览器重新计算布局(Mozilla 术语中称为reflowgetBoundingClientRect()
)。这是一个开销很大的操作,应该只在必要时执行。当我们调用 时,就会触发此操作。
第二点是滚动事件的触发频率很高。如果事件处理程序以这种频率执行,可能会造成浪费。
那么,我们能改变什么呢?
仅在必要时触发布局
我们可以稍微改变一下逻辑,这样只有当帖子在视口中getBoundingClientRect()
时才会调用它。
优化事件处理程序
我们希望限制调用滚动事件处理程序来更新进度条的频率。
去抖动可以调节函数随时间执行的速率,是一种常见的优化技术。
我们有几种选择:
- 您可以使用具有去抖动功能的库,例如Lodash和Underscore。
- 您可以使用
requestAnimationFrame
回调。 - 您可以制作自己的去抖动实现。
如果您要直接“绘制”或为属性设置动画,建议使用此方法requestAnimationFrame
。我们将更改触发绘制的value属性,因此我们将使用它。
我们获得的优势requestAnimationFrame
是,浏览器会在下次请求页面绘制时执行更改,而使用去抖动功能,它会按照我们选择的预定速率执行。
代码变化很小。
var timeout;
window.onscroll = function () {
if (timeout) {
window.cancelAnimationFrame(timeout);
}
timeout = window.requestAnimationFrame(function () {
showReadingProgress();
});
}
如果您想了解有关debouncing 和 requestAnimationFrame 的更多信息,我推荐这篇文章。
性能增益是多少?
我比较了从上到下快速滚动文章的性能。以下是来自 Google Devtools 的结果。您可以看到,优化后的代码重绘时间减少了约 75%。
浏览器支持
requestAnimationFrame
适用于 IE10 及以上版本的所有浏览器。您可以使用Paul Irish 提供的 polyfill来支持旧版浏览器,但会回退到setTimeout()
。
最后的话
感谢阅读!如果你喜欢这篇文章,请告诉我。
也许接下来,我将谈论如何计算博客文章的阅读时间。
黑客快乐!👩💻👨💻🙌
感谢您的阅读!欢迎订阅我的RSS 源,并在社交媒体上与他人分享这篇文章。💌
您可以通过ko-fi给我买杯咖啡来表达您的感激之情。🙂
文章来源:https://dev.to/robole/pimp-your-blog-by-adding-a-reading-progress-bar-573i