阅读进度条

顶部的细条随着你往下读不断填满,让读者随时知道还剩多少。

  • JS
  • 布局
  • 滚动
  • 收录于
0% 已读

设计平静的界面

AI 提示词

用 React 为文章制作阅读进度条。用被动监听器监听滚动元素(整页时为 window)的 scroll 事件,按 scrollTop / (scrollHeight - clientHeight) 计算进度,并把它写入容器的 CSS 自定义属性 --p,而不是 React state,这样滚动时不会触发重新渲染。顶部的 sticky 进度条使用 transform: scaleX(var(--p)),transform-origin 为 left,背景为主色;旁边用一个小号等宽字体标签显示四舍五入后的百分比。进度条不加过渡,确保精确跟随滚动。在支持 animation-timeline: scroll() 的浏览器中,也可以改用纯 CSS 实现。在 prefers-reduced-motion 下保持不变,因为它只会随用户滚动而移动。文章标题为“设计平静的界面”。

源代码

Demo.tsx
'use client'

import { useRef } from 'react'
import s from './demo.module.css'

export default function ScrollProgress({ labels }: { labels: Record<string, string> }) {
  const pct = useRef<HTMLSpanElement>(null)

  const onScroll = (e: React.UIEvent<HTMLDivElement>) => {
    const el = e.currentTarget
    const p = el.scrollTop / (el.scrollHeight - el.clientHeight || 1)
    el.style.setProperty('--p', String(p))
    if (pct.current) pct.current.textContent = `${Math.round(p * 100)}% ${labels.read}`
  }

  return (
    <div className={s.frame} tabIndex={0} onScroll={onScroll}>
      <div className={s.top}>
        <span className={s.bar} />
        <span ref={pct} className={s.pct}>
          0% {labels.read}
        </span>
      </div>
      <article className={s.article}>
        <h3 className={s.title}>{labels.title}</h3>
        {Array.from({ length: 14 }, (_, i) => (
          <span key={i} className={s.line} style={{ width: `${[100, 94, 98, 70][i % 4]}%` }} />
        ))}
      </article>
    </div>
  )
}
demo.module.css
.frame {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}

.frame:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -4px; }

.hint {
  display: block;
  font: 500 var(--text-xs) / 1 var(--font-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: center;
  color: var(--stage-muted);
}

.frame { --p: 0; }

.top {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  justify-content: flex-end;
  padding: 10px 16px 8px;
  background: var(--stage-bg);
}

.bar {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 3px;
  background: var(--stage-accent);
  transform: scaleX(var(--p));
  transform-origin: left;
}

.pct {
  font: 500 var(--text-xs) / 1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--stage-muted);
}

.article {
  display: grid;
  gap: 12px;
  max-width: 300px;
  margin: 0 auto;
  padding: 8px 16px 40px;
}

.title {
  margin: 0 0 6px;
  font: 600 var(--text-xl) / 1.2 var(--font-display);
  color: var(--stage-ink);
}

.line {
  display: block;
  height: 8px;
  border-radius: 4px;
  background: var(--stage-surface);
}

投稿创意

看到想收录的动效或主题?简单描述一下,会打开一个预先填好的 GitHub Issue,确认后即可提交。

你想投稿的是?

需要 GitHub 账号,投稿内容会公开。