里程表数字

每一位数字都是一条 0–9 的竖向数字带,滚动到新的数值,右侧的数字滚得更远。

  • CSS
  • 文字
  • 点击
  • 收录于
每月访客

AI 提示词

用 CSS 为 React 制作里程表式数字。每一位数字是一个固定高度的窗口(height 1em,overflow hidden),里面放一条 0 到 9 的竖向数字带。要显示数字 d,就给数字带设置 transform: translateY(calc(-1em * d)),过渡时间 900ms,缓动为 cubic-bezier(0.16, 1, 0.3, 1),并按位置每位增加 60ms 的 transition-delay,让数字从左到右依次停稳。使用 font-variant-numeric: tabular-nums 保证宽度不跳动,逗号等分隔符作为静态字符显示。添加一个“换个数字”按钮,随机生成一个 6 位数。把真实数值放在容器的 aria-label 中,并用 aria-live="polite" 播报变化。在 prefers-reduced-motion 下直接切换数字,不做滚动。说明文字为“每月访客”。

源代码

Demo.tsx
'use client'

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

const DIGITS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

export default function Odometer({ labels }: { labels: Record<string, string> }) {
  const [value, setValue] = useState(128406)
  const text = value.toLocaleString('en-US')
  let pos = 0

  return (
    <div className={s.wrap}>
      <span className={s.caption}>{labels.caption}</span>
      <span className={s.number} aria-label={text} aria-live="polite">
        {Array.from(text).map((ch, i) =>
          /\d/.test(ch) ? (
            <span key={i} className={s.window} aria-hidden="true">
              <span className={s.strip} style={{ transform: `translateY(${-Number(ch)}em)`, transitionDelay: `${pos++ * 60}ms` }}>
                {DIGITS.map((d) => (
                  <span key={d}>{d}</span>
                ))}
              </span>
            </span>
          ) : (
            <span key={i} aria-hidden="true">
              {ch}
            </span>
          ),
        )}
      </span>
      <button type="button" className={s.button} onClick={() => setValue(100000 + Math.floor(Math.random() * 900000))}>
        {labels.shuffle}
      </button>
    </div>
  )
}
demo.module.css
.wrap { display: grid; justify-items: center; gap: 10px; }

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

.number {
  display: flex;
  font: 600 2.6rem / 1 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  color: var(--stage-ink);
}

.window { display: inline-block; height: 1em; overflow: hidden; }

.strip {
  display: flex;
  flex-direction: column;
  transition: transform 900ms var(--ease-out);
}

.strip span { height: 1em; }

.button {
  height: 32px;
  padding: 0 12px;
  border: 1px solid var(--stage-rule);
  border-radius: 8px;
  background: var(--stage-surface);
  color: var(--stage-ink);
  font: 500 var(--text-sm) / 1 var(--font-body);
  white-space: nowrap;
  cursor: pointer;
}

.button:hover { border-color: var(--stage-accent); }
.button:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .strip { transition: none; }
}

投稿创意

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

你想投稿的是?

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