为你的博客制作阅读进度条📊

2025-05-26

为你的博客制作阅读进度条📊

我们可以在标准博客中添加任何可以增强阅读体验的内容吗?

阅读进度条怎么样?

进度条

进度条是固定的,只有在帖子进入视野时才会显示。向下滚动,你会看到一个时髦的紫色进度条。💜

HTML

<progress id="reading-progress" max="100" value="0" ></progress>
Enter fullscreen mode Exit fullscreen mode

我选择使用<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);
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

优化代码

我们的代码性能还不错,但还可以改进。如果你感兴趣,请继续阅读!

我们的代码中有两部分导致其性能不佳。

第一部分是,某些方法会触发浏览器重新计算布局(Mozilla 术语中称为reflowgetBoundingClientRect() )。这是一个开销很大的操作,应该只在必要时执行。当我们调用 时,就会触发此操作。

第二点是滚动事件的触发频率很高。如果事件处理程序以这种频率执行,可能会造成浪费。

那么,我们能改变什么呢?

仅在必要时触发布局

我们可以稍微改变一下逻辑,这样只有当帖子在视口中getBoundingClientRect()时才会调用它。

通过移动 getBoundingClientRect 来优化代码

优化事件处理程序

我们希望限制调用滚动事件处理程序来更新进度条的频率。

去抖动可以调节函数随时间执行的速率,是一种常见的优化技术。

我们有几种选择:

  1. 您可以使用具有去抖动功能的库,例如LodashUnderscore
  2. 您可以使用requestAnimationFrame回调。
  3. 您可以制作自己的去抖动实现。

如果您要直接“绘制”或为属性设置动画,建议使用此方法requestAnimationFrame。我们将更改触发绘制的value属性,因此我们将使用它。

我们获得的优势requestAnimationFrame是,浏览器会在下次请求页面绘制时执行更改,而使用去抖动功能,它会按照我们选择的预定速率执行。

代码变化很小。

var timeout;

window.onscroll = function () {
    if (timeout) {
        window.cancelAnimationFrame(timeout);
    }

    timeout = window.requestAnimationFrame(function () {
        showReadingProgress();
  }); 
}
Enter fullscreen mode Exit fullscreen mode

如果您想了解有关debouncing 和 requestAnimationFrame 的更多信息,我推荐这篇文章。

性能增益是多少?

我比较了从上到下快速滚动文章的性能。以下是来自 Google Devtools 的结果。您可以看到,优化后的代码重绘时间减少了约 75%。

Chrome DevTools 中的性能比较

浏览器支持

requestAnimationFrame适用于 IE10 及以上版本的所有浏览器。您可以使用Paul Irish 提供的 polyfill来支持旧版浏览器,但会回退到setTimeout()

最后的话

感谢阅读!如果你喜欢这篇文章,请告诉我。

也许接下来,我将谈论如何计算博客文章的阅读时间。

黑客快乐!👩‍💻👨‍💻🙌


感谢您的阅读!欢迎订阅我的RSS 源,并在社交媒体上与他人分享这篇文章。💌

您可以通过ko-fi给我买杯咖啡来表达您的感激之情。🙂

文章来源:https://dev.to/robole/pimp-your-blog-by-adding-a-reading-progress-bar-573i
PREV
VS Code:你不需要那个扩展
NEXT
如何通过上传制作流畅的 CSS 动画