Toca el titular para descifrarlo otra vez

React + MotionText & Scroll

Entschlüsselte Headline

Die Buchstaben einer Headline beginnen als Rauschen und rasten nacheinander von links nach rechts ein, ohne dass sich das Layout verschiebt.

Einstellungen

1600ms

Code

import { Fragment, useCallback, useEffect, useRef, useState } from "react";
import { useInView, useReducedMotion } from "framer-motion";
import { RotateCcw } from "lucide-react";

interface ScrambleTextProps {
  text?: string;
  color?: string;
  /** Milisegundos hasta que se descifra entero. */
  duration?: number;
}

const NARROW = "il1!|:;/";
const UPPER = "ABDEFHKLNPRSTUXZ";
const LOWER = "abcdeghknopqsuvxyz#*";
const glyphSet = (c: string) => (/[ilíjtfr.,;:!¡'|]/.test(c) ? NARROW : /[A-ZÁÉÍÓÚÑ]/.test(c) ? UPPER : LOWER);
const glyph = (c: string, n: number) => {
  const set = glyphSet(c);
  return set.charAt(Math.floor(n * set.length) % set.length);
};
const noise = (text: string) => [...text].map((c, i) => (c === " " ? null : glyph(c, ((i * 7 + 3) % 10) / 10)));

export default function ScrambleText({
  text = "Weniger Rauschen. Mehr Signal.",
  color = "#ff375f",
  duration = 1600,
}: ScrambleTextProps) {
  const ref = useRef<HTMLButtonElement>(null);
  const seen = useInView(ref, { once: true, amount: 0.5 });
  const reduce = useReducedMotion();
  const raf = useRef(0);
  const [glyphs, setGlyphs] = useState<(string | null)[]>(() => noise(text));

  const run = useCallback(() => {
    cancelAnimationFrame(raf.current);
    const chars = [...text];
    const locks = chars.map((_, i) => (i / chars.length) * 0.75 + Math.random() * 0.25);
    const start = performance.now();
    let last = 0;
    const step = (now: number) => {
      const p = reduce ? 1 : Math.min(1, (now - start) / duration);
      if (now - last > 50 || p === 1) {
        last = now;
        setGlyphs(
          chars.map((c, i) =>
            c === " " || p >= (locks[i] ?? 0) ? null : glyph(c, Math.random()),
          ),
        );
      }
      if (p < 1) raf.current = requestAnimationFrame(step);
    };
    raf.current = requestAnimationFrame(step);
  }, [text, duration, reduce]);

  useEffect(() => {
    if (seen) run();
    return () => cancelAnimationFrame(raf.current);
  }, [seen, run]);

  const words = text.split(" ");
  let offset = 0;

  return (
    <div className="w-full max-w-md">
      <button
        ref={ref}
        type="button"
        onClick={run}
        aria-label={text}
        className="block w-full cursor-pointer text-left text-balance text-[44px] font-semibold leading-[1.02] tracking-[-0.045em] text-[#1d1d1f] dark:text-[#f5f5f7] sm:text-[50px]"
      >
        <span aria-hidden="true">
          {words.map((w, wi) => {
            const start = offset;
            offset += w.length + 1;
            return (
              <Fragment key={wi}>
                <span className="inline-block whitespace-nowrap">
                  {[...w].map((ch, ci) => {
                    const g = glyphs[start + ci];
                    return (
                      <span key={ci} className="relative inline-block">
                        <span className="transition-opacity duration-300" style={{ opacity: g ? 0 : 1 }}>
                          {ch}
                        </span>
                        {g ? (
                          <span className="absolute inset-0 flex justify-center" style={{ color }}>
                            {g}
                          </span>
                        ) : null}
                      </span>
                    );
                  })}
                </span>
                {wi < words.length - 1 ? " " : null}
              </Fragment>
            );
          })}
        </span>
      </button>
      <p className="mt-5 flex items-center gap-1.5 text-[13px] text-[#86868b]">
        <RotateCcw strokeWidth={1.5} className="h-3.5 w-3.5" />
        Tippe auf die Überschrift, um sie erneut zu entschlüsseln
      </p>
    </div>
  );
}

Prompt für KI

Füg ihn in ChatGPT, Claude oder Cursor ein und der Baustein wird mit diesen Einstellungen an dein Projekt angepasst, auch ohne React.

Mehr aus Text & Scroll

Wofür du ihn nutzen kannst

Ein Text-Scramble-Effekt passt zu Marken, die nah an Code oder Geheimnissen sind: eine Firma für IT-Sicherheit, das Portfolio einer Entwicklerin, die Launch-Seite einer API, eine Krypto-Wallet. Die Headline entschlüsselt sich, sobald sie ins Bild scrollt, und weil jeder Buchstabe seinen Platz behält, springt drumherum nichts.

Kurze Headlines mit drei bis sechs Wörtern wirken am besten. Halte die Dauer unter zwei Sekunden, sonst wartet man statt zu lesen. Ein Tipp spielt den Effekt erneut ab. Screenreader lesen sofort den echten Satz vor, nicht die Zufallszeichen.