07Emoji react bật lên
Emoji trong huy hiệu tròn bật to quá cỡ rồi co lại đúng kích thước, đánh dấu khoảnh khắc vừa được nhấn.
Dùng choChat cho phép react emoji vào tin nhắn, nút react trên bài đăng cộng đồng
scale overshootcubic-bezier0.25초

<main class="stage">
<div class="wrap is-demo">
<div class="bubble">Chào buổi sáng!</div>
<span class="reaction" aria-hidden="true">👍</span>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────────
$duration: $dur-quick; // 이모지가 튀어 오르는 데 걸리는 시간
$easing: $ease-pop;
$color: $subject-cream; // 말풍선 색 (orange 무대라 cream)
@include stage($stage-orange);
.wrap { position: relative; display: inline-block; }
.bubble {
padding: 16px 24px;
border-radius: $r-card;
background: $color;
color: $stage-ink;
font-weight: 600; font-size: clamp(15px, 4.8vw, 18px); line-height: 1.4; font-family: inherit;
transform-origin: center;
.wrap.is-demo & { animation: bubble-settle-loop $dur-loop $easing infinite; }
}
.reaction {
position: absolute;
right: -16px;
bottom: -16px;
display: grid;
place-items: center;
width: 44px;
height: 44px;
border-radius: 50%;
background: #fff;
font-size: 22px;
box-shadow: $shadow-press;
transform: scale(0);
transform-origin: center;
.wrap.is-demo & { animation: reaction-pop-loop $dur-loop $easing infinite; }
}
// 눌린 순간 말풍선도 살짝 눌렸다 돌아오고, 이모지가 오버슈트로 튀어 오른다
@keyframes bubble-settle-loop {
0% { transform: scale(1); }
55% { transform: scale(1); }
66% { transform: scale(.96); }
76% { transform: scale(1); }
100% { transform: scale(1); }
}
@keyframes reaction-pop-loop {
0% { transform: scale(0); opacity: 0; }
55% { transform: scale(0); opacity: 0; }
70% { transform: scale(1.3); opacity: 1; }
80% { transform: scale(1); opacity: 1; }
96% { transform: scale(1); opacity: 1; }
100% { transform: scale(0); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
.wrap.is-demo .bubble, .wrap.is-demo .reaction { animation: none !important; }
.wrap.is-demo .reaction { transform: scale(1); opacity: 1; }
}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);
}
.wrap {
position: relative;
display: inline-block;
}
.bubble {
padding: 16px 24px;
border-radius: 16px;
background: #fff7e6;
color: #17141a;
font-weight: 600;
font-size: clamp(15px, 4.8vw, 18px);
line-height: 1.4;
font-family: inherit;
transform-origin: center;
}
.wrap.is-demo .bubble {
animation: bubble-settle-loop 2s cubic-bezier(0.2, 1.4, 0.4, 1) infinite;
}
.reaction {
position: absolute;
right: -16px;
bottom: -16px;
display: grid;
place-items: center;
width: 44px;
height: 44px;
border-radius: 50%;
background: #fff;
font-size: 22px;
box-shadow: 0 6px 12px rgba(23, 20, 26, 0.32);
transform: scale(0);
transform-origin: center;
}
.wrap.is-demo .reaction {
animation: reaction-pop-loop 2s cubic-bezier(0.2, 1.4, 0.4, 1) infinite;
}
@keyframes bubble-settle-loop {
0% {
transform: scale(1);
}
55% {
transform: scale(1);
}
66% {
transform: scale(0.96);
}
76% {
transform: scale(1);
}
100% {
transform: scale(1);
}
}
@keyframes reaction-pop-loop {
0% {
transform: scale(0);
opacity: 0;
}
55% {
transform: scale(0);
opacity: 0;
}
70% {
transform: scale(1.3);
opacity: 1;
}
80% {
transform: scale(1);
opacity: 1;
}
96% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(0);
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.wrap.is-demo .bubble, .wrap.is-demo .reaction {
animation: none !important;
}
.wrap.is-demo .reaction {
transform: scale(1);
opacity: 1;
}
}import type { CSSProperties, ReactNode } from "react";
import styles from "./ReactionEmojiPop.module.scss";
export type ReactionEmojiPopProps = {
children: ReactNode;
emoji?: string;
/** 이모지가 튀어 오르는 데 걸리는 시간 (기본 220ms) */
duration?: number;
easing?: string;
className?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string };
export default function ReactionEmojiPop({
children,
emoji = "👍",
duration = 220,
easing = "cubic-bezier(.2, 1.4, .4, 1)",
className = "",
}: ReactionEmojiPopProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing };
return (
<div className={`${styles.wrap} ${className}`} style={vars}>
<div className={styles.bubble}>{children}</div>
<span className={`${styles.reaction} ${styles.enter}`} aria-hidden="true">{emoji}</span>
</div>
);
}07 Emoji react bật lên — Diện tích đo được 3.597% · khung hình chuyển động 7/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · 9 CSS Chat Bubble Animation — dán 1 dòng là chạy
Bài viết chứa demo này
9 CSS Chat Bubble Animation — dán 1 dòng là chạyCSS chat bubble animation là hiệu ứng bong bóng chat chạy bằng CSS thuần khi tin nhắn xuất hiện hoặc được xem. Chín kiểu Xem tất cả demo trong bài này


