06Bong bóng nổi lên
20 vòng tròn chỉ có viền nổi từ dưới lên trên qua translateY, vừa nổi vừa phồng to rồi nhỏ lại bằng scale trên đường đi.
Dùng choHero thương hiệu đồ uống hoặc rượu, nền giới thiệu công viên nước và spa
translateY 상승scale 맥동nth-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>
<span class="p" style="--i:18"></span>
<span class="p" style="--i:19"></span>
</div>
<p class="caption">Mát Lạnh Mùa Hè</p>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────
$duration: $dur-loop * 2; // 4s 기준
$easing: $ease-out;
$color: $subject-ink;
// ── 데모 무대 ───────────────────────────────
@include stage($stage-yellow);
.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;
bottom: -10%;
width: 10px; height: 10px;
border-radius: 50%;
border: 1.5px solid $color;
background: rgba($color, .1);
opacity: 0;
animation: rise $duration $easing infinite;
animation-delay: calc(var(--i) * -0.24s);
}
@for $i from 1 through 20 {
.p:nth-child(#{$i}) {
left: ((($i * 43) % 92)) * 1%;
$s: 6px + ($i % 5) * 2px;
width: $s; height: $s;
animation-duration: $duration + ($i % 4) * .5s;
}
}
@keyframes rise {
0% { transform: translateY(0) scale(.6); opacity: 0; }
12% { opacity: .8; }
55% { transform: translateY(-170px) scale(1.15); }
100% { transform: translateY(-360px) scale(.8); opacity: 0; }
}
// ── 접근성: 상승을 멈추고 제자리 거품만 남긴다 ────────────
@media (prefers-reduced-motion: reduce) {
.p { animation: none; opacity: .4; bottom: 20%; }
}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);
}
.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;
bottom: -10%;
width: 10px;
height: 10px;
border-radius: 50%;
border: 1.5px solid #17141a;
background: rgba(23, 20, 26, 0.1);
opacity: 0;
animation: rise 4s ease-out infinite;
animation-delay: calc(var(--i) * -0.24s);
}
.p:nth-child(1) {
left: 43%;
width: 8px;
height: 8px;
animation-duration: 4.5s;
}
.p:nth-child(2) {
left: 86%;
width: 10px;
height: 10px;
animation-duration: 5s;
}
.p:nth-child(3) {
left: 37%;
width: 12px;
height: 12px;
animation-duration: 5.5s;
}
.p:nth-child(4) {
left: 80%;
width: 14px;
height: 14px;
animation-duration: 4s;
}
.p:nth-child(5) {
left: 31%;
width: 6px;
height: 6px;
animation-duration: 4.5s;
}
.p:nth-child(6) {
left: 74%;
width: 8px;
height: 8px;
animation-duration: 5s;
}
.p:nth-child(7) {
left: 25%;
width: 10px;
height: 10px;
animation-duration: 5.5s;
}
.p:nth-child(8) {
left: 68%;
width: 12px;
height: 12px;
animation-duration: 4s;
}
.p:nth-child(9) {
left: 19%;
width: 14px;
height: 14px;
animation-duration: 4.5s;
}
.p:nth-child(10) {
left: 62%;
width: 6px;
height: 6px;
animation-duration: 5s;
}
.p:nth-child(11) {
left: 13%;
width: 8px;
height: 8px;
animation-duration: 5.5s;
}
.p:nth-child(12) {
left: 56%;
width: 10px;
height: 10px;
animation-duration: 4s;
}
.p:nth-child(13) {
left: 7%;
width: 12px;
height: 12px;
animation-duration: 4.5s;
}
.p:nth-child(14) {
left: 50%;
width: 14px;
height: 14px;
animation-duration: 5s;
}
.p:nth-child(15) {
left: 1%;
width: 6px;
height: 6px;
animation-duration: 5.5s;
}
.p:nth-child(16) {
left: 44%;
width: 8px;
height: 8px;
animation-duration: 4s;
}
.p:nth-child(17) {
left: 87%;
width: 10px;
height: 10px;
animation-duration: 4.5s;
}
.p:nth-child(18) {
left: 38%;
width: 12px;
height: 12px;
animation-duration: 5s;
}
.p:nth-child(19) {
left: 81%;
width: 14px;
height: 14px;
animation-duration: 5.5s;
}
.p:nth-child(20) {
left: 32%;
width: 6px;
height: 6px;
animation-duration: 4s;
}
@keyframes rise {
0% {
transform: translateY(0) scale(0.6);
opacity: 0;
}
12% {
opacity: 0.8;
}
55% {
transform: translateY(-170px) scale(1.15);
}
100% {
transform: translateY(-360px) scale(0.8);
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.p {
animation: none;
opacity: 0.4;
bottom: 20%;
}
}import type { CSSProperties } from "react";
import styles from "./BubblesRise.module.scss";
export type BubblesRiseProps = {
/** 애니메이션 기준 시간(ms) */
duration?: number;
/** CSS easing */
easing?: string;
/** 피사체 색 */
color?: string;
};
type Vars = CSSProperties & { "--i"?: number };
const COUNT = 20;
export default function BubblesRise({
duration = 4000,
easing = "cubic-bezier(.2,.8,.2,1)",
color = "#17141a",
}: BubblesRiseProps) {
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>
);
}06 Bong bóng nổi lên — Diện tích đo được 12.542% · 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


