08Cánh hoa anh đào bay
18 hình elip rơi vừa xoay vừa lắc nhẹ theo translateX, chuyển động như cánh hoa bay trong gió.
Dùng choNền khuyến mãi mùa xuân, hero thương hiệu cưới và làm đẹp
rotate + swayanimation-delaynth-child 낙하시간

<main class="stage">
<div class="field" aria-hidden="true">
<span class="p" style="--i:0"></span>
<span class="p" style="--i:1"></span>
<span class="p" style="--i:2"></span>
<span class="p" style="--i:3"></span>
<span class="p" style="--i:4"></span>
<span class="p" style="--i:5"></span>
<span class="p" style="--i:6"></span>
<span class="p" style="--i:7"></span>
<span class="p" style="--i:8"></span>
<span class="p" style="--i:9"></span>
<span class="p" style="--i:10"></span>
<span class="p" style="--i:11"></span>
<span class="p" style="--i:12"></span>
<span class="p" style="--i:13"></span>
<span class="p" style="--i:14"></span>
<span class="p" style="--i:15"></span>
<span class="p" style="--i:16"></span>
<span class="p" style="--i:17"></span>
</div>
<p class="caption">Bộ Sưu Tập Xuân</p>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────
$duration: $dur-loop * 1.4; // 2.8s 기준
$easing: ease-in-out;
$color: $subject-blue; // 팔레트에 분홍이 없어 blue 로 대신한다 (사실.md 참고)
// ── 데모 무대 ───────────────────────────────
@include stage($stage-paper);
.field { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.caption {
position: relative; z-index: 1; margin: 0;
color: $subject-ink; font-weight: 700; font-size: clamp(16px, 5vw, 22px); line-height: 1; font-family: inherit;
}
.p {
position: absolute;
top: -10%;
width: 10px; height: 6px;
border-radius: 50%;
background: $color;
opacity: 0;
animation: petal $duration $easing infinite;
animation-delay: calc(var(--i) * -0.28s);
}
@for $i from 1 through 18 {
.p:nth-child(#{$i}) {
left: ((($i * 47) % 95)) * 1%;
animation-duration: $duration + ($i % 5) * .4s;
}
}
// 낙하 + 회전 + 좌우 흔들림, 전부 transform 안에서만
@keyframes petal {
0% { transform: translate(0, 0) rotate(0deg); opacity: 0; }
10% { opacity: .85; }
30% { transform: translate(18px, 110px) rotate(120deg); }
60% { transform: translate(-14px, 240px) rotate(240deg); }
100% { transform: translate(10px, 380px) rotate(360deg); opacity: .3; }
}
// ── 접근성: 회전·낙하를 멈추고 옅은 꽃잎만 남긴다 ────────────
@media (prefers-reduced-motion: reduce) {
.p { animation: none; opacity: .3; top: 45%; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ffffff;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.field {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
}
.caption {
position: relative;
z-index: 1;
margin: 0;
color: #17141a;
font-weight: 700;
font-size: clamp(16px, 5vw, 22px);
line-height: 1;
font-family: inherit;
}
.p {
position: absolute;
top: -10%;
width: 10px;
height: 6px;
border-radius: 50%;
background: #2f6df6;
opacity: 0;
animation: petal 2.8s ease-in-out infinite;
animation-delay: calc(var(--i) * -0.28s);
}
.p:nth-child(1) {
left: 47%;
animation-duration: 3.2s;
}
.p:nth-child(2) {
left: 94%;
animation-duration: 3.6s;
}
.p:nth-child(3) {
left: 46%;
animation-duration: 4s;
}
.p:nth-child(4) {
left: 93%;
animation-duration: 4.4s;
}
.p:nth-child(5) {
left: 45%;
animation-duration: 2.8s;
}
.p:nth-child(6) {
left: 92%;
animation-duration: 3.2s;
}
.p:nth-child(7) {
left: 44%;
animation-duration: 3.6s;
}
.p:nth-child(8) {
left: 91%;
animation-duration: 4s;
}
.p:nth-child(9) {
left: 43%;
animation-duration: 4.4s;
}
.p:nth-child(10) {
left: 90%;
animation-duration: 2.8s;
}
.p:nth-child(11) {
left: 42%;
animation-duration: 3.2s;
}
.p:nth-child(12) {
left: 89%;
animation-duration: 3.6s;
}
.p:nth-child(13) {
left: 41%;
animation-duration: 4s;
}
.p:nth-child(14) {
left: 88%;
animation-duration: 4.4s;
}
.p:nth-child(15) {
left: 40%;
animation-duration: 2.8s;
}
.p:nth-child(16) {
left: 87%;
animation-duration: 3.2s;
}
.p:nth-child(17) {
left: 39%;
animation-duration: 3.6s;
}
.p:nth-child(18) {
left: 86%;
animation-duration: 4s;
}
@keyframes petal {
0% {
transform: translate(0, 0) rotate(0deg);
opacity: 0;
}
10% {
opacity: 0.85;
}
30% {
transform: translate(18px, 110px) rotate(120deg);
}
60% {
transform: translate(-14px, 240px) rotate(240deg);
}
100% {
transform: translate(10px, 380px) rotate(360deg);
opacity: 0.3;
}
}
@media (prefers-reduced-motion: reduce) {
.p {
animation: none;
opacity: 0.3;
top: 45%;
}
}import type { CSSProperties } from "react";
import styles from "./SakuraPetals.module.scss";
export type SakuraPetalsProps = {
/** 애니메이션 기준 시간(ms) */
duration?: number;
/** CSS easing */
easing?: string;
/** 피사체 색 */
color?: string;
};
type Vars = CSSProperties & { "--i"?: number };
const COUNT = 18;
export default function SakuraPetals({
duration = 2800,
easing = "ease-in-out",
color = "#2f6df6",
}: SakuraPetalsProps) {
const vars = {
"--duration": `${duration}ms`,
"--easing": easing,
"--color": color,
} as CSSProperties;
return (
<div className={styles.field} style={vars} aria-hidden="true">
{Array.from({ length: COUNT }, (_, i) => (
<span key={i} className={styles.p} style={{ "--i": i } as Vars} />
))}
</div>
);
}08 Cánh hoa anh đào bay — Diện tích đo được 9.572% · khung hình chuyển động 23/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · 9 CSS Particle Backgrounds — dán 1 dòng là chạy
Bài viết chứa demo này
9 CSS Particle Backgrounds — dán 1 dòng là chạyCSS particle background là kỹ thuật rải hàng chục phần tử nhỏ — chấm, vạch, mảnh nhỏ — bằng CSS thuần rồi cho chúng tự c Xem tất cả demo trong bài này


