06Mũi tên tự vẽ đường
Nét SVG hình mũi tên hiện ra như được vẽ từ đầu đến cuối, rồi lại bị tẩy đi, cứ thế lặp lại.
Dùng choĐường dẫn muốn nhấn mạnh, mũi tên chỉ tay onboarding
SVGstroke-dashoffsetpathLength

<main class="stage">
<svg class="ds-svg" viewBox="0 0 100 60" aria-hidden="true">
<path class="ds-path-trail" pathLength="100" d="M10,30 L80,30 L58,10 M80,30 L58,50" />
<path class="ds-path" pathLength="100" d="M10,30 L80,30 L58,10 M80,30 L58,50" />
<circle class="ds-tip" cx="80" cy="30" r="3" />
</svg>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$duration: 2.2s;
$easing: $ease-out;
$color: $subject-ink;
@include stage($stage-yellow);
.ds-svg {
width: min(220px, 70vw);
height: auto;
overflow: visible;
}
.ds-path {
fill: none;
stroke: $color;
stroke-width: 4;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 100; // pathLength="100" 이라 %처럼 다룬다
animation: ds-draw $duration $easing infinite;
}
// 트레일 파티클 — 흐린 사본이 살짝 지연되어 뒤따라 그려진다
.ds-path-trail {
fill: none;
stroke: $color;
stroke-width: 4;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 100;
opacity: .22;
animation: ds-draw $duration $easing 90ms infinite;
}
// 화살촉 오버슛 — 다 그려지는 순간(58%) 원이 고무줄처럼 커졌다 66%에서 정착한다
.ds-tip {
fill: $color;
opacity: 0;
transform-origin: 80px 30px;
transform: scale(0);
}
@keyframes ds-tip-pop {
0%, 50% { opacity: 0; transform: scale(0); }
58% { opacity: 1; transform: scale(1.6); }
66% { opacity: 1; transform: scale(1); }
78%, 100% { opacity: 0; transform: scale(0); }
}
.ds-tip { animation: ds-tip-pop $duration $easing infinite; }
// 자동 재생: 화살표가 처음부터 끝까지 그려졌다가 잠깐 멈춘 뒤 반대쪽으로 지워지며 한 바퀴 돈다
@keyframes ds-draw {
0% { stroke-dashoffset: 100; }
55% { stroke-dashoffset: 0; }
80% { stroke-dashoffset: 0; }
100% { stroke-dashoffset: -100; }
}
@media (prefers-reduced-motion: reduce) {
.ds-path { animation: none !important; stroke-dashoffset: 0 !important; }
.ds-path-trail { animation: none !important; opacity: 0 !important; }
.ds-tip { animation: none !important; opacity: 0 !important; transform: scale(0) !important; }
}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);
}
.ds-svg {
width: min(220px, 70vw);
height: auto;
overflow: visible;
}
.ds-path {
fill: none;
stroke: #17141a;
stroke-width: 4;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 100;
animation: ds-draw 2.2s ease-out infinite;
}
.ds-path-trail {
fill: none;
stroke: #17141a;
stroke-width: 4;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 100;
opacity: 0.22;
animation: ds-draw 2.2s ease-out 90ms infinite;
}
.ds-tip {
fill: #17141a;
opacity: 0;
transform-origin: 80px 30px;
transform: scale(0);
}
@keyframes ds-tip-pop {
0%, 50% {
opacity: 0;
transform: scale(0);
}
58% {
opacity: 1;
transform: scale(1.6);
}
66% {
opacity: 1;
transform: scale(1);
}
78%, 100% {
opacity: 0;
transform: scale(0);
}
}
.ds-tip {
animation: ds-tip-pop 2.2s ease-out infinite;
}
@keyframes ds-draw {
0% {
stroke-dashoffset: 100;
}
55% {
stroke-dashoffset: 0;
}
80% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: -100;
}
}
@media (prefers-reduced-motion: reduce) {
.ds-path {
animation: none !important;
stroke-dashoffset: 0 !important;
}
.ds-path-trail {
animation: none !important;
opacity: 0 !important;
}
.ds-tip {
animation: none !important;
opacity: 0 !important;
transform: scale(0) !important;
}
}import { CSSProperties } from "react";
import styles from "./SvgDrawnArrow.module.scss";
export interface SvgDrawnArrowProps {
duration?: string;
easing?: string;
color?: string;
}
export default function SvgDrawnArrow({
duration = "2.2s",
easing = "ease-out",
color = "#17141a",
}: SvgDrawnArrowProps) {
const style = { "--duration": duration, "--easing": easing, "--color": color } as CSSProperties;
return (
<svg className={styles.svg} viewBox="0 0 100 60" aria-hidden="true" style={style}>
<path className={styles.pathTrail} pathLength={100} d="M10,30 L80,30 L58,10 M80,30 L58,50" />
<path className={styles.path} pathLength={100} d="M10,30 L80,30 L58,10 M80,30 L58,50" />
<circle className={styles.tip} cx={80} cy={30} r={3} />
</svg>
);
}06 Mũi tên tự vẽ đường — Diện tích đo được 2.082% · khung hình chuyển động 18/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · 9 css arrow animation — dán một dòng là chạy
Bài viết chứa demo này
9 css arrow animation — dán một dòng là chạycss arrow animation là mũi tên tự chuyển động trên trang: hover thì thân tên dài ra, hoặc nó nảy lên như giục bạn bấm nú Xem tất cả demo trong bài này

