07Vẽ đường tiến trình theo scroll(scroll())
Cuộn bên trong hộp chứa timeline thì scaleY của thanh dọc bám theo vị trí cuộn nhờ animation-timeline: scroll(nearest block), vẽ đúng phần đã đi qua mà không cần JavaScript.
Dùng choThanh tiến trình đọc cho bài dài, timeline cột bên trang tài liệu
animation-timeline: scroll()scaleYJS 0줄

<main class="stage">
<div class="ts07-box">
<span class="ts07-rail is-demo"></span>
<div class="ts07-content">
<div class="ts07-item">09:00</div>
<div class="ts07-item">09:20</div>
<div class="ts07-item">10:00</div>
<div class="ts07-item">10:40</div>
<div class="ts07-item">11:10</div>
</div>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$duration: $dur-loop;
$easing: linear;
$color: $subject-ink; // 주황 무대 위 전경은 잉크 — 크림은 3.11:1
@include stage($stage-orange);
.ts07-box {
position: relative;
width: min(220px, 80vw);
height: clamp(120px, 62vh, 200px);
overflow-y: auto;
padding: $sp-2 0 $sp-2 $sp-6;
box-sizing: border-box;
background: rgba(255, 247, 230, .25); // 잉크 글자를 받는 상자 — 먹 틴트(4.32:1) 대신 크림 틴트 6.98:1
border-radius: $r-sm;
}
.ts07-content { position: relative; }
.ts07-rail {
position: absolute;
left: $sp-2;
top: 0;
width: 3px;
height: 100%;
background: $color;
border-radius: $r-xs;
transform-origin: top; // 규칙8: 스크롤 진행선 위 기준 채움
transform: scaleY(0);
}
// 실제 사용 코드: 상자를 안에서 스크롤하면 JS 없이 진행률만큼 그려진다
@supports (animation-timeline: scroll()) {
.ts07-rail:not(.is-demo) {
animation: ts07-draw linear both;
animation-timeline: scroll(nearest block);
}
}
// 미지원 브라우저(Firefox·Safari): 다 그려진 최종 상태로 고정
@supports not (animation-timeline: scroll()) {
.ts07-rail:not(.is-demo) { transform: scaleY(1); }
}
@keyframes ts07-draw { from { transform: scaleY(0); } to { transform: scaleY(1); } }
.ts07-item {
padding: $sp-2 0;
color: $color;
font: 600 13px/1 -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
border-bottom: 1px solid rgba(23, 20, 26, .14);
}
// 데모 루프: 실제 스크롤 대신 2초 주기로 진행률을 흉내(캡처용)
.ts07-rail.is-demo { animation: ts07-draw-loop $duration $easing infinite; }
@keyframes ts07-draw-loop { 0% { transform: scaleY(0); } 100% { transform: scaleY(1); } }
@media (prefers-reduced-motion: reduce) {
.ts07-rail, .ts07-rail.is-demo { animation: none !important; transform: scaleY(1) !important; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ff4d1f;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.ts07-box {
position: relative;
width: min(220px, 80vw);
height: clamp(120px, 62vh, 200px);
overflow-y: auto;
padding: 8px 0 8px 24px;
box-sizing: border-box;
background: rgba(255, 247, 230, 0.25);
border-radius: 8px;
}
.ts07-content {
position: relative;
}
.ts07-rail {
position: absolute;
left: 8px;
top: 0;
width: 3px;
height: 100%;
background: #17141a;
border-radius: 4px;
transform-origin: top;
transform: scaleY(0);
}
@supports (animation-timeline: scroll()) {
.ts07-rail:not(.is-demo) {
animation: ts07-draw linear both;
animation-timeline: scroll(nearest block);
}
}
@supports not (animation-timeline: scroll()) {
.ts07-rail:not(.is-demo) {
transform: scaleY(1);
}
}
@keyframes ts07-draw {
from {
transform: scaleY(0);
}
to {
transform: scaleY(1);
}
}
.ts07-item {
padding: 8px 0;
color: #17141a;
font: 600 13px/1 -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
border-bottom: 1px solid rgba(23, 20, 26, 0.14);
}
.ts07-rail.is-demo {
animation: ts07-draw-loop 2s linear infinite;
}
@keyframes ts07-draw-loop {
0% {
transform: scaleY(0);
}
100% {
transform: scaleY(1);
}
}
@media (prefers-reduced-motion: reduce) {
.ts07-rail, .ts07-rail.is-demo {
animation: none !important;
transform: scaleY(1) !important;
}
}import { CSSProperties } from "react";
import styles from "./ScrollProgressLine.module.scss";
type Vars = CSSProperties & { "--color"?: string };
export interface ScrollProgressLineProps {
color?: string;
times?: string[];
}
// animation-timeline: scroll() 는 브라우저 지원이 갈린다 — Firefox·Safari 는 아직 없다.
// 지원 브라우저에서는 이 상자를 안에서 스크롤하면 JS 없이 옆 선이 진행률만큼 그려진다.
export default function ScrollProgressLine({
color = "#17141a",
times = ["09:00", "09:20", "10:00", "10:40", "11:10"],
}: ScrollProgressLineProps) {
const style: Vars = { "--color": color };
return (
<div className={styles.box} style={style}>
<span className={styles.rail} />
<div className={styles.content}>
{times.map((t) => (
<div key={t} className={styles.item}>{t}</div>
))}
</div>
</div>
);
}07 Vẽ đường tiến trình theo scroll(scroll()) — Diện tích đo được 0.373% · khung hình chuyển động 23/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · 9 css timeline animation không JS, copy-paste
Bài viết chứa demo này
9 css timeline animation không JS, copy-pasteCss timeline animation là cách nối nhiều bước bằng đường kẻ với vòng tròn để bày ra theo thứ tự. Chín mẫu dưới đây không Xem tất cả demo trong bài này


