Không canvas, không thư viện, không mẹo vặt rối rắm.
Cách tiếp cận đúng cho hiệu ứng gõ chữ
Trình duyệt vẽ lại giao diện theo từng frame. Nếu logic gõ chữ chạy lệch nhịp đó, animation sẽ giật ngay. Vì vậy, thay vì dùng nhiều setTimeout lồng nhau, script dưới đây bám sát vòng lặp render của trình duyệt để kiểm soát tốc độ gõ, xóa và khoảng dừng một cách rõ ràng.
HTML gọn nhẹ
<span id="typewriter"></span>
Một phần tử duy nhất để hiển thị nội dung, tránh dư thừa DOM.
CSS cho con trỏ gõ chữ
#typewriter::after {
content: '|';
margin-left: 4px;
animation: cursor-blink 1s steps(2) infinite;
}
@keyframes cursor-blink {
0% { opacity: 1; }
50% { opacity: 0; }
}
Con trỏ chỉ nên nhấp nháy nhẹ, không cần hiệu ứng phức tạp.
JavaScript: Script Typewriter phiên bản mượt
const messages = [
'Thiết kế web hiện đại',
'Hiệu ứng mượt, không thư viện',
'Tập trung trải nghiệm người dùng'
];
const el = document.getElementById('typewriter');
let msgIndex = 0;
let charIndex = 0;
let deleting = false;
let lastFrame = 0;
let waitUntil = 0;
const TYPE_SPEED = 90;
const DELETE_SPEED = 50;
const PAUSE_AFTER_TYPE = 1200;
const PAUSE_AFTER_DELETE = 400;
function loop(time) {
if (time < waitUntil) {
requestAnimationFrame(loop);
return;
}
const speed = deleting ? DELETE_SPEED : TYPE_SPEED;
if (time - lastFrame < speed) {
requestAnimationFrame(loop);
return;
}
lastFrame = time;
const text = messages[msgIndex];
if (!deleting) {
charIndex++;
el.textContent = text.slice(0, charIndex);
if (charIndex === text.length) {
deleting = true;
waitUntil = time + PAUSE_AFTER_TYPE;
}
} else {
charIndex--;
el.textContent = text.slice(0, charIndex);
if (charIndex === 0) {
deleting = false;
msgIndex = (msgIndex + 1) % messages.length;
waitUntil = time + PAUSE_AFTER_DELETE;
}
}
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
Script này chỉ dùng một vòng lặp duy nhất, không tạo timer chồng chéo và không bao giờ render sẵn toàn bộ chuỗi.
Vì sao script này không bị giật
- Đồng bộ trực tiếp với render loop của trình duyệt
- Không dùng
setTimeoutlặp liên tục - Kiểm soát rõ ràng trạng thái gõ, xóa và tạm dừng
- Chỉ cập nhật DOM khi cần thiết
Khi trình duyệt quyết định vẽ frame mới, script mới cho phép thay đổi nội dung. Đây là điểm khác biệt lớn nhất.
Điều chỉnh cho từng trường hợp sử dụng
- Hero section: tăng
PAUSE_AFTER_TYPE - Banner quảng cáo: giảm
TYPE_SPEED - Trang giới thiệu: có thể bỏ bước xóa chữ
Không có cấu hình chung cho mọi giao diện.
Lưu ý khi đưa vào dự án thật
- Không nên để chạy vô hạn trên mọi trang
- Nên dừng khi element ra khỏi viewport
- Mobile yếu nên giảm tốc độ hoặc tắt hẳn
Kết luận
Typewriter effect trông tự nhiên không đến từ hiệu ứng màu mè, mà đến từ việc tôn trọng nhịp render của trình duyệt. Với cách tiếp cận này, hiệu ứng gõ chữ chạy mượt, không nhảy, không lộ kỹ thuật và đủ ổn để dùng lâu dài.
Viết ít nhưng đúng, đó mới là thứ giữ được trải nghiệm.
Bình luận