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);
}
transformkhông ảnh hưởng đến layout (không dịch chuyển phần tử khác).
Demo: Hover transition
<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
<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
<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