06スクランブルデコード
ランダムな文字が高速で入れ替わり、左から一文字ずつ本当の文字に固定されていきます。暗号が解読されるように見えます。
使いどころ読み込み完了メッセージ、ハッキング・セキュリティ系LP
setIntervalJS 12줄monospace

<main class="stage">
<p class="sd-text" id="sd" aria-live="polite">DECODE</p>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$duration: $dur-loop;
$easing: $ease-out;
$color: $subject-ink;
@include stage($stage-yellow);
.sd-text {
margin: 0;
font: 800 clamp(28px, 9vw, 44px)/1.2 "Space Mono", "Courier New", monospace;
letter-spacing: .06em;
color: $color;
}
// 이 데모는 CSS keyframes 가 아니라 짧은 JS(setInterval)로 글자를 무작위로 바꾼다 —
// 그래서 reduced-motion 은 CSS 가 아니라 스크립트 안의 matchMedia 체크로 처리한다 (index.html 참고)
@media (prefers-reduced-motion: reduce) {
.sd-text { color: $color; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ffd23f;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.sd-text {
margin: 0;
font: 800 clamp(28px, 9vw, 44px)/1.2 "Space Mono", "Courier New", monospace;
letter-spacing: 0.06em;
color: #17141a;
}
@media (prefers-reduced-motion: reduce) {
.sd-text {
color: #17141a;
}
}var target = "DECODE";
var glyphs = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var el = document.getElementById("sd");
var reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
function tick(locked) {
if (reduced) { el.textContent = target; return; }
var out = "";
for (var i = 0; i < target.length; i++) {
out += i < locked ? target[i] : glyphs[Math.floor(Math.random() * glyphs.length)];
}
el.textContent = out;
}
var frame = 0;
setInterval(function () {
frame++;
var locked = Math.max(0, Math.floor((frame % 26) / 3) - 2);
tick(Math.min(locked, target.length));
}, 60);import { useEffect, useRef, useState } from "react";
import styles from "./ScrambleDecode.module.scss";
export interface ScrambleDecodeProps {
text?: string;
interval?: number;
}
const GLYPHS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// 실사용: 뷰포트에 들어오면 무작위 글자가 바뀌다가 왼쪽부터 한 번씩 진짜 글자로 잠긴다. 다 풀리면 멈춘다.
export default function ScrambleDecode({ text = "DECODE", interval = 40 }: ScrambleDecodeProps) {
const ref = useRef<HTMLParagraphElement>(null);
const [display, setDisplay] = useState(text);
useEffect(() => {
const el = ref.current;
if (!el) return;
let timer: ReturnType<typeof setInterval> | null = null;
const io = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting || timer) return;
io.unobserve(entry.target);
const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduced) { setDisplay(text); return; }
let frame = 0;
timer = setInterval(() => {
frame += 1;
const locked = Math.min(Math.max(0, Math.floor(frame / 3) - 2), text.length);
let out = "";
for (let i = 0; i < text.length; i++) {
out += i < locked ? text[i] : GLYPHS[Math.floor(Math.random() * GLYPHS.length)];
}
setDisplay(out);
if (locked >= text.length && timer) { clearInterval(timer); timer = null; }
}, interval);
});
}, { threshold: 0.3 });
io.observe(el);
return () => { io.disconnect(); if (timer) clearInterval(timer); };
}, [text, interval]);
return (
<p ref={ref} className={styles.text} aria-live="polite">{display}</p>
);
}06 スクランブルデコード — 計測 累積面積 2.665% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · 【コピペOK】css テキスト アニメーション 9選
このデモが属する記事
【コピペOK】css テキスト アニメーション 9選css テキスト アニメーション とは、見出しや文章が画面に現れるときに一緒に動く演出のことです。見出しを一気に引き立てる一瞬のつくり方を9つ集めました。そのうち1つは、暗号が解けるように文字がそろいます。 この記事のデモをすべて見る

