Transitions và Animations

Transition và Animation

CSS cung cấp hai cơ chế chuyển động:

  • Transition: hiệu ứng mượt mà khi giá trị thuộc tính thay đổi (thường qua :hover)
  • Animation: chuỗi khung hình tự chạy với @keyframes

Transition

.btn {
  transition: property duration timing-function delay;
  /* Ví dụ: */
  transition: background-color 0.3s ease;
  transition: all 0.3s ease-out 0.1s;

  /* Nhiều thuộc tính */
  transition: background 0.3s ease, transform 0.2s ease;
}

Timing functions

Giá trị Đặc điểm
ease Bắt đầu chậm, nhanh ở giữa, chậm ở cuối (mặc định)
linear Tốc độ đều
ease-in Bắt đầu chậm, kết thúc nhanh
ease-out Bắt đầu nhanh, kết thúc chậm — tự nhiên nhất
ease-in-out Chậm ở cả hai đầu
cubic-bezier(x1,y1,x2,y2) Tùy chỉnh

@keyframes và Animation

@keyframes tên-animation {
  from { opacity: 0; transform: translateY(-10px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Hoặc dùng % */
@keyframes bounce {
  0%   { transform: translateY(0); }
  50%  { transform: translateY(-20px); }
  100% { transform: translateY(0); }
}

.element {
  animation: tên-animation 0.5s ease-out forwards;
  /* animation: name duration timing iteration direction fill-mode */
}

Các thuộc tính animation

Thuộc tính Giá trị phổ biến
animation-duration 0.3s, 1s
animation-iteration-count 1, infinite
animation-direction normal, reverse, alternate
animation-fill-mode none, forwards, backwards, both
animation-play-state running, paused

transform

.box {
  transform: rotate(45deg);
  transform: scale(1.2);
  transform: translate(10px, 20px);
  transform: skew(10deg, 5deg);
  /* Kết hợp */
  transform: rotate(15deg) scale(1.1) translateY(-5px);
}

transform không ảnh hưởng đến layout (không dịch chuyển phần tử khác).


Demo: Hover transition

Kết quả
<div class="demo-wrap">
  <h3>Hover vào các nút để thấy transition</h3>
  <div class="btn-row">
    <button class="btn btn-color">Màu sắc</button>
    <button class="btn btn-scale">Scale</button>
    <button class="btn btn-slide">Slide bg</button>
    <button class="btn btn-border">Border mở rộng</button>
  </div>

  <h3>Card với nhiều transition</h3>
  <div class="card-hover">
    <div class="card-icon">🎨</div>
    <h4>Hover vào card</h4>
    <p>Box-shadow, transform và border-color đều transition.</p>
  </div>
</div>
body { font-family: sans-serif; padding: 1rem; }
h3 { font-size: .9rem; color: #555; margin: 1rem 0 .4rem; }
.btn-row { display: flex; gap: 12px; flex-wrap: wrap; }

.btn {
  padding: 10px 20px; border: none; border-radius: 6px;
  cursor: pointer; font-size: 1rem; font-weight: bold;
}

/* Chuyển màu nền */
.btn-color { background: #4472c4; color: white; transition: background 0.3s ease; }
.btn-color:hover { background: #c55a11; }

/* Scale */
.btn-scale { background: #70ad47; color: white; transition: transform 0.2s ease-out; }
.btn-scale:hover { transform: scale(1.15); }

/* Slide background (dùng gradient) */
.btn-slide {
  background: linear-gradient(to right, #ed7d31 50%, #4472c4 50%);
  background-size: 200% 100%; background-position: right;
  color: white; transition: background-position 0.3s ease;
}
.btn-slide:hover { background-position: left; }

/* Border mở rộng */
.btn-border {
  background: transparent; color: #4472c4;
  border: 2px solid #4472c4;
  transition: padding 0.2s ease, letter-spacing 0.2s ease;
}
.btn-border:hover { padding: 10px 28px; letter-spacing: .1em; }

/* Card */
.card-hover {
  display: inline-block; width: 220px;
  background: white; border: 2px solid #ddd;
  border-radius: 12px; padding: 1.5rem; text-align: center;
  margin-top: 8px; cursor: pointer;
  transition: box-shadow 0.3s ease, transform 0.2s ease, border-color 0.3s ease;
}
.card-hover:hover { box-shadow: 0 8px 24px rgba(68,114,196,0.25); transform: translateY(-4px); border-color: #4472c4; }
.card-icon { font-size: 2rem; margin-bottom: .5rem; }
.card-hover h4 { margin: 0 0 .5rem; color: #4472c4; }
.card-hover p  { margin: 0; color: #666; font-size: .85rem; }

Demo: @keyframes animation

Kết quả
<div class="demo-wrap">
  <h3>Pulse (scale + opacity)</h3>
  <div class="circle pulse-circle"></div>

  <h3>Bounce (translateY)</h3>
  <div class="circle bounce-circle"></div>

  <h3>Color shift</h3>
  <div class="color-bar"></div>

  <h3>Slide in</h3>
  <div class="slide-box">Tôi trượt vào từ trái</div>
</div>
body { font-family: sans-serif; padding: 1rem; }
h3 { font-size: .9rem; color: #555; margin: 1rem 0 .4rem; }

.circle {
  width: 60px; height: 60px; border-radius: 50%;
  background: #4472c4; display: flex; align-items: center; justify-content: center;
  color: white; font-size: 1.5rem;
}

/* Pulse */
@keyframes pulse {
  0%   { transform: scale(1); opacity: 1; }
  50%  { transform: scale(1.2); opacity: 0.7; }
  100% { transform: scale(1); opacity: 1; }
}
.pulse-circle { animation: pulse 1.5s ease-in-out infinite; }

/* Bounce */
@keyframes bounce {
  0%, 100% { transform: translateY(0); }
  40%       { transform: translateY(-24px); }
  60%       { transform: translateY(-12px); }
}
.bounce-circle { background: #ed7d31; animation: bounce 1s ease infinite; }

/* Color shift */
@keyframes colorshift {
  0%   { background: #4472c4; }
  33%  { background: #ed7d31; }
  66%  { background: #70ad47; }
  100% { background: #4472c4; }
}
.color-bar { width: 200px; height: 20px; border-radius: 10px; animation: colorshift 3s linear infinite; }

/* Slide in */
@keyframes slideIn {
  from { transform: translateX(-100%); opacity: 0; }
  to   { transform: translateX(0); opacity: 1; }
}
.slide-box {
  background: #70ad47; color: white; padding: 10px 16px; border-radius: 6px;
  display: inline-block; font-weight: bold;
  animation: slideIn 0.8s ease-out both;
}

Demo: CSS loading spinner

Kết quả
<div class="demo-wrap">
  <h3>Spinner kiểu vòng tròn</h3>
  <div class="spinner"></div>

  <h3>Spinner dots</h3>
  <div class="dots">
    <span></span><span></span><span></span>
  </div>

  <h3>Progress bar</h3>
  <div class="progress-wrap"><div class="progress-bar"></div></div>
</div>
body { font-family: sans-serif; padding: 1rem; }
h3 { font-size: .9rem; color: #555; margin: 1rem 0 .4rem; }

/* Spinner */
@keyframes spin { to { transform: rotate(360deg); } }
.spinner {
  width: 48px; height: 48px;
  border: 5px solid #e0e0e0;
  border-top-color: #4472c4;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

/* Dots */
@keyframes dot-bounce {
  0%, 80%, 100% { transform: scale(0); opacity: .4; }
  40%            { transform: scale(1); opacity: 1; }
}
.dots { display: flex; gap: 8px; align-items: center; }
.dots span {
  width: 14px; height: 14px; border-radius: 50%; background: #4472c4;
  animation: dot-bounce 1.4s ease-in-out infinite;
}
.dots span:nth-child(1) { animation-delay: 0s; }
.dots span:nth-child(2) { animation-delay: 0.2s; }
.dots span:nth-child(3) { animation-delay: 0.4s; }

/* Progress bar */
@keyframes loading { from { width: 0; } to { width: 100%; } }
.progress-wrap { background: #e0e0e0; border-radius: 8px; overflow: hidden; height: 12px; width: 240px; }
.progress-bar  { height: 100%; background: linear-gradient(to right, #4472c4, #70ad47); animation: loading 2s ease-in-out infinite alternate; }

Bình luận