文字解码

悬停时,每个字会快速闪过随机字符,再从左到右依次定格,像屏幕在解码。

  • JS
  • 文字
  • 悬停
  • 收录于

AI 提示词

用 React 写一个不依赖第三方库的文字解码效果,在悬停和键盘聚焦时播放。保存目标文字,开始时运行约 700ms 的 requestAnimationFrame 循环。每一帧中,进度位置之前的字显示最终文字,之后的字显示从字符池“动效光影节奏速度弧线弹性渐变层次空间时间”中随机选出的汉字。空格保持为空格。组件卸载或开始新一轮时取消循环。中文字宽一致,因此宽度不会跳动;把真实文字放在 aria-label 中,避免屏幕阅读器读出乱码。在 prefers-reduced-motion 下直接显示最终文字,不做解码。文字为“信号正在解码”。

源代码

Demo.tsx
'use client'

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

const DURATION = 700

export default function TextScramble({ labels }: { labels: Record<string, string> }) {
  const target = labels.text
  const pool = Array.from(labels.glyphs)
  const [shown, setShown] = useState(target)
  const frame = useRef(0)

  useEffect(() => () => cancelAnimationFrame(frame.current), [])

  const run = () => {
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return
    cancelAnimationFrame(frame.current)
    const chars = Array.from(target)
    const start = performance.now()
    const tick = (now: number) => {
      const progress = Math.min((now - start) / DURATION, 1)
      const locked = Math.floor(progress * chars.length)
      setShown(
        chars
          .map((c, i) => (i < locked || c === ' ' ? c : pool[Math.floor(Math.random() * pool.length)]))
          .join(''),
      )
      if (progress < 1) frame.current = requestAnimationFrame(tick)
    }
    frame.current = requestAnimationFrame(tick)
  }

  return (
    <span className={s.text} tabIndex={0} aria-label={target} onPointerEnter={run} onFocus={run}>
      <span aria-hidden="true">{shown}</span>
    </span>
  )
}
demo.module.css
.text {
  padding: 8px 12px;
  border-radius: var(--radius-sm);
  font: 600 var(--text-2xl) / 1.2 var(--font-mono);
  letter-spacing: -0.01em;
  color: var(--stage-ink);
  white-space: nowrap;
  cursor: default;
}

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

投稿创意

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

你想投稿的是?

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