2

Cách dùng Javascript sắp xếp hình ảnh theo cột

Xếp ảnh theo cột là một thiết kế rất hay gặp với các trang  có thiết kế dạng như gallery photo. Với ưu thế ảnh sắp xếp rất “ngăn nắp”, tiết kiệm diện tích hiển thị đồng nghĩa với hiển thị được nhiều thông tin hơn, bạn sẽ thường xuyên gặp vấn đề làm thế nào sắp xếp theo cột? Dùng CSS hay Javascript? Ở bài này mình sẽ hướng dẫn các bạn cách sử dụng Javascript sắp xếp hình ảnh theo cột.

Giải pháp

Chắc bạn nào cũng biết Pinterest.Com (chưa biết thì vào coi luôn nhé!). Trang này sử dụng layout dạng cột để fill các post vào trang. Trong một lần làm gallery, mình đã tìm hiểu cách sắp xếp theo dạng cột của Pinterest, và thú vị thay tìm được câu trả lời của chính người viết ra những đoạn code đó của Pinterest, Co-founder Evan Sharp. Giải pháp mà Evan đưa ra đơn giản hơn cách chúng ta tưởng tượng nhiều, và nó rất hiệu quả! (câu trả lời của Evan ở Quora.Com).

Tôi đã viết Pinterest script. Đây là cách cơ bản đoạn script ấy làm việc:

Trước tiên:

  • Định vị khối bọc container với position Absolute
  • Xác định chiều rộng của cột
  • Xác định khoảng cách giữa các cột

Chuẩn bị một mảng:

  • Lấy chiều rộng của container chứa pin (là các post của Pinterest); tính toán số lượng cột cần dựng để có thể fill trang web.
  • Tạo một mảng rỗng, với số lượng phần tử bằng số lượng cột. Dùng mảng này để lưu trữ chiều cao của mỗi cột khi chúng ta dựng layout. Chiều cao của cột 1 sẽ lưu tại array[0]

Duyệt qua tất cả các pin:

  • Tới pin nào thì chèn pin đó vào cột thấp nhất, để làm việc đó, ta cần tính:
  • Vị trí “left:” của pin === Cột thứ #i (index array) nhân với độ rộng cột + khoảng cách giữa các cột
  • Vị trí “top:” của pin === Gía trị lưu tại array[i] (chiều cao của cột thấp nhất) ngay tại thời điểm duyệt qua pin.
  • Cuối cùng, lưu lại chiều cao của cột đó (cập nhật giá trị tại array[i] ) bằng cách cộng thêm vào chiều cao của pin vừa thêm vào.

Kết quả là rất nhẹ nhàng. Trên Chrome, dựng trang với hơn 50 pin chỉ mất <10ms.

Quả thật mình đã vô cùng phấn khích khi đọc được những dòng do chính tác gỉả Pinterest giải thích cách nó hoạt động. Chắc các bạn cũng biết cảm giác “Ah ha!!!” cực kỳ thú vị, bạn vỡ lẽ ra một chân lý đơn giản mà mình không nhìn thấy được, mình rút gọn lại giải thích của Evan như thế này cho dễ nhớ: “Luôn đặt pin mới vào cột thấp nhất!” – pin ở đây là ảnh, box content hay thứ gì khác mà bạn muốn sắp xếp.

Và đây là đoạn code mình viết theo cách trên, để sắp xếp hình ảnh theo cột:

Dĩ nhiên chúng ta cần làm nhiều hơn để ảnh có thể sắp xếp đúng chứ không đơn giản gọi makeGrid() là đã chạy được.

Ở đây mình rút gọn lại để giải thích cho các bạn nhé:

  • var cols: Mình cố định luôn có 4 cột, nên không cần tính toán số lượng cột hay xác định chiều rộng cột như Evan nói.
  • MakeGrid(): xem trong hàm này các bạn thấy rõ ràng mình thực hiện lần lượt theo giải pháp Evan đưa ra:
    • Tìm cột thấp nhất (để chèn ảnh vào), thứ tự cột chính là index trong mảng cols` ở trên.
    • Xác định x,y theo cột tìm được
    • Chèn ảnh vào cột sau đó cập nhật lại chiều cao cột.
  • Hàm minPos(cols) là dùng để tìm ra index của cột có chiều cao thấp nhất trong mảng cols

DEMO

Dưới dây là demo, các bạn bấm vào tab JS, CSS để xem chi tiết source code:

Một số  lưu ý trong demo

  • Hàm mosaicGrid nhận vào hai tham số:
    • selector (string): parent container, chứa toàn bộ ảnh
    • target (string): ảnh, hoặc có thể là div, span
  • Do ảnh cần load về thì mới lấy được chiều cao, do đo cần có thời gian tải xong thì mới xử lý. Ở đây, mình dùng load event để đợi ảnh tải về xong thì mới tính toán và xếp ảnh vào cột. Do đó, nếu dùng code này vào sản phẩm thật của bạn, có thể lúc mới tải trang sẽ thấy ảnh nhảy lộn xộn hoặc có hiện tượng chớp, giật trên máy yếu.

Bình luận với Facebook

Hoàng Tựa

Yêu thích lập trình web và tạo ra những thứ đẹp đẽ.

2 Comments

  1. Font chữ nhỏ quá, mình nghĩ bạn nên đổi giao diện dùng font nào to cho dễ đọc hơn.

    • Cảm ơn bạn góp ý. Mình sẽ điều chỉnh cho kích thước font chữ lớn hơn!

Leave a Reply

Your email address will not be published. Required fields are marked *