Kéo thả border để thay đổi kích thước khối giao diện
Bài đăng này đã không được cập nhật trong 5 năm
Giới thiệu
Điều đầu tiên mọi người nghĩ đến khi xử lí những vấn đề kiểu như vậy là sẽ thử tìm xem có thư viện nào đơn giản mà mượt mà, và rồi "bùm" chỉ cần gõ npm install ... hay copy source trên github về import vào code.
Mình cũng đã từng như vậy, tuy nhiên tìm một hồi vẫn không thấy có lib/pack nào ưng ý cả nên đã quyết định tự code cho dễ controll =))
Một giao diện có nhiều khung nhìn chứa nhiều nội dung thay đổi với kích thước nội dung không lường trước là một vấn đề cần có giải pháp linh hoạt phía client.
Không ai muốn phần view mình focus và lại bị fix cứng kích thước trong khi chỗ khác có thể vẫn còn trống hoác cả. Nên qua bài viết này mình sẽ đưa ra 1 hướng giải quyết cho điều này.
Chuẩn bị và những lưu ý
- Một khối giao diện ( bằng thẻ div chẳng hạn)
- Đảm bảo rằng đường viền (border) của nó đủ lớn (dày khoảng 5px là thoải mái rồi) để thao tác trở nên mượt mà, mặc định đường viền chỉ dày 1px nên focus chuột vào rất khó.
- Nếu đang để giá trị css cố định cho min-width, max-width thì lưu ý tránh miền độ rộng mà bạn muốn kéo nhả kích thước.
- Để test chức năng thì tốt nhất là bạn đừng nhét nó vào 1 giao diện quá phức tạp, bị ảnh hưởng bởi z-index, position,... khi đã hiểu vấn đề rồi thì cứ thế mà triển thôi !
Bắt tay vào code
Khởi tạo một số biến toàn cục
var minWidth = 400, //độ rộng tối thiểu
MARGINS = 3, //ước lượng độ rộng border/2
clicked = null, // lưu trạng thái đánh dấu các cạnh được kéo thả, ở đây mình chỉ làm với cạnh phải. Các cạnh khác làm tương tự
onRightEdge = false, // trạng thái cho cạnh phải
container = null, // khối giao diện
pointerToEdge = 0, // khoảng cách từ con trỏ đến cạnh trái
redraw = false; // flag để render lại độ rộng
Trích xuất Dom muốn thao tác
container = document.getElementById('container');
Lắng nghe sự kiện
container.addEventListener('mousedown', this.onMouseDown); // lắng nghe sự kiện nhấn chuột
document.addEventListener('mousemove', this.onMove); // lắng nghe sự kiện kéo chuột
document.addEventListener('mouseup', this.onUp); // lắng nghe sự kiện nhả chuột
this.animate(); // kiểm tra và vẽ lại liên tục, gọi ngay khi page của bạn sẵn sàng
Nếu muốn hoạt động trên mobile, sự kiện cần bắt như sau:
container.addEventListener('touchstart', onMouseDown);
document.addEventListener('touchmove', onMove);
document.addEventListener('touchend', onUp);
this.animate();
//Tự thay tên function cho đẹp nhế
Việc bắt sự kiện gì cho DOM nào khá quan trọng, với mỗi sự kiện cần xác định được nó xảy ra khi focus vào vùng của DOM nào. Như trên khi nhấn chuột/tay vào border thì vùng focus là container, nhưng khi di chuyển và nhả ra thì vùng thay đổi đến phải là document.
Sự kiện onMouseDown
onMouseDown = (e) => {
this.detect(e); //Phát hiện, cập nhật trạng thái các cạnh
clicked = {
onRightEdge: onRightEdge,
};
e.preventDefault(); //đố biết cái này là gì đếy
}
Hàm phát hiện, cập nhật trạng thái các cạnh
Như đã nói, mình chỉ check cạnh phải, các cạnh khác làm tương tự !
detect(e) {
let b = container.getBoundingClientRect();
pointerToEdge = e.clientX - b.left;
let realWidth = b.width - MARGINS;
onRightEdge = pointerToEdge >= realWidth;
}
Sự kiện onMove
onMove = (e) => {
this.detect(e);
redraw = true; //Khi di chuyển thì luôn cho phép vẽ lại giao diện
}
Sự kiện onUp
onUp = (e) => {
clicked = null; //Bỏ mọi trạng thái kéo thả của các cạnh, về mặc định.
this.detect(e);
}
Hàm vẽ lại kích thước
animate = () => {
requestAnimationFrame(this.animate); //Cái này nó giống như setTimeOut với thời gian cực nhỏ, liên tục check
if (!redraw) return;
redraw = false;
if (clicked) {
let width = parseInt(container.style.width);
if (clicked.onRightEdge) {
if (Math.max(pointerToEdge, minWidth) > (window.innerWidth - 500 )) { //Cho giới hạn độ rộng chứ không kéo tràn viền page
container.style.width = (window.innerWidth - 500 ) + 'px';
} else {
container.style.width = Math.max(pointerToEdge, minWidth) + 'px';
}
}
return;
}
//Thay đổi định dạng con trỏ khi tới cạnh phải
if (onRightEdge) {
container.style.cursor = 'ew-resize';
} else {
container.style.cursor = 'default';
}
}
Kết
Bài viết khá đơn giản, ae mà cần trôn được thì khỏi cần tìm option của thư viện nha !
All rights reserved