Tổng quan
PhotoSwipe là thư viện gallery ảnh JavaScript nhẹ, hỗ trợ cảm ứng và hoạt động mượt trên di động lẫn desktop. Người dùng có thể:
- Vuốt chuyển ảnh, chụm/zoom, kéo ảnh theo quán tính
- Đóng bằng kéo dọc, phóng to bằng double-tap/chuột
- Toàn màn hình, phím tắt, lịch sử URL và deep-linking
- Chú thích, bộ đếm, chia sẻ mạng xã hội, tải lười và tiền tải ảnh
Phù hợp cho: website bán hàng, portfolio, tạp chí/blog, landing page sản phẩm, tài liệu trực quan.
Tính năng nổi bật
- Hỗ trợ cảm ứng: pinch-to-zoom, swipe, momentum scrolling
- Điều khiển bàn phím: mũi tên trái/phải, phím Esc
- Giao diện linh hoạt: caption, counter, preloader, share, fullscreen, zoom, close, arrows
- Deep-linking và lịch sử: #&gid=…&pid=…, back/forward tương thích
- Tối ưu hiệu năng: requestAnimationFrame, preloading slide trước/sau, progressive image
- Khả năng truy cập: aria-hidden, focus management, bàn phím
- Tùy biến mạnh: hooks sự kiện, template UI, animation duration, scale mode
Cấu trúc DOM tối thiểu
- pswp: khung lightbox
- pswp__bg: nền mờ
- pswpscroll-wrap > pswpcontainer > pswp__item (3 khung xoay vòng)
- UI: caption, counter, buttons (share, zoom, fullscreen, arrows, close), preloader
Gợi ý: tạo phần tử .pswp trong HTML (ẩn), ảnh/thumbnail ở ngoài, khởi tạo bằng JavaScript khi người dùng bấm.
Cài đặt nhanh
- Thêm CSS và JS của PhotoSwipe và UI mặc định
- Đánh dấu dữ liệu ảnh (kích thước hoặc msrc)
- Khởi tạo khi click thumbnail
Ví dụ đánh dấu một item cho mảng slides:
- src: URL ảnh lớn
- w, h: kích thước gốc
- msrc: thumbnail nhỏ (tối ưu LCP)
- title: caption hiển thị
Khởi tạo cơ bản
- Lắng nghe click vào thumbnail
- Xây dựng mảng items {src, w, h, msrc, title}
- Truyền options (ví dụ getThumbBoundsFn để zoom từ thumbnail)
Pseudo-code:
- Lấy danh sách thumbnail
- map => items
- Khởi tạo PhotoSwipe(template, PhotoSwipeUI_Default, items, options).init()
Các tùy chọn phổ biến để tinh chỉnh
- Giao diện:
- closeEl, captionEl, fullscreenEl, zoomEl, shareEl, counterEl, arrowEl, preloaderEl
- timeToIdle, timeToIdleOutside: tự ẩn UI khi không tương tác
- indexIndicatorSep: ký tự phân tách bộ đếm
- Trải nghiệm:
- loop: lặp vô hạn
- spacing: khoảng cách giữa slide
- showHideOpacity: fade khi mở/đóng
- clickToCloseNonZoomable, tapToClose, tapToToggleControls
- pinchToClose, closeOnVerticalDrag, closeOnScroll
- Hình ảnh:
- preload: [trước, sau] (ví dụ [1,1])
- forceProgressiveLoading: ép tải progressive
- scaleMode: ‘fit’ hoặc ‘orig’
- maxSpreadZoom: giới hạn zoom tối đa
- Điều hướng:
- escKey, arrowKeys: bật/tắt phím
- Lịch sử & deep-link:
- history: bật URL hash
- galleryUID: định danh gallery
- Khởi động:
- index: ảnh bắt đầu
- getThumbBoundsFn: trả về vị trí/kích thước thumbnail để tạo hiệu ứng zoom mượt
- Chia sẻ:
- shareButtons: danh sách nút share (facebook, twitter, pinterest, download)
- getImageURLForShare, getPageURLForShare, getTextForShare: tùy biến dữ liệu share
Gợi ý tối ưu:
- Bật showHideOpacity khi ảnh nhẹ/ít, tắt khi ảnh lớn để mượt hơn
- Dùng getThumbBoundsFn để tạo animation zoom-in/out đẹp từ thumbnail
Sự kiện quan trọng (hooks)
- beforeChange, afterChange: thay slide
- imageLoadComplete: ảnh đã tải xong
- doubleTap: double-tap phóng to/thu nhỏ
- initialZoomIn, initialZoomInEnd, initialZoomOut, initialZoomOutEnd: vòng đời mở/đóng
- close, destroy: dọn tài nguyên
- preventDragEvent: kiểm soát kéo khi click vào vùng tương tác (caption, link)
Ứng dụng:
- Ghi log analytics mỗi lần afterChange
- Lazy render caption nâng cao sau imageLoadComplete
- Đồng bộ UI tùy biến qua beforeChange
Tối ưu hiệu năng cho di động
- Cung cấp msrc (thumbnail) riêng, ảnh lớn chỉ tải khi mở lightbox
- Dùng preload [1,1] đủ dùng cho hầu hết trường hợp
- Đặt w/h đúng để tránh layout shift
- Bật progressive loading cho mạng chậm
- Giảm show/hide animation duration nếu thiết bị yếu/cũ
- Dọn interval/timeouts khi destroy
SEO hình ảnh trong bối cảnh gallery
- Dùng alt mô tả giàu ngữ nghĩa cho ảnh/thumbnail bên ngoài lightbox
- Sử dụng caption (title) như nội dung hỗ trợ ngữ cảnh
- Tối ưu tên file, thêm structured data (ImageObject) ở trang ảnh khi phù hợp
- Dùng srcset/sizes để trình duyệt chọn ảnh phù hợp màn hình
Khả năng truy cập (Accessibility)
- Quản lý focus khi mở/đóng, tránh kẹt focus
- aria-hidden cho vùng nền ngoài lightbox
- Phím Esc để đóng, mũi tên điều hướng
- Tương phản đủ cho caption, nút điều khiển
Deep-linking và lịch sử
- URL dạng #&gid=1&pid=3
- Hỗ trợ back/forward, onHashChange cập nhật slide
- galleryUID khác nhau cho nhiều gallery trên cùng trang
Lưu ý:
- Cần đồng bộ chỉ số pid với items
- Khi thay đổi hash bên ngoài, nên điều hướng lại gallery tương ứng
Xử lý lỗi ảnh
- errorMsg: template hiển thị khi ảnh lỗi (thay %url%)
- Fallback: hiển thị placeholder hoặc thông điệp thay thế
- Ghi log để phát hiện URL hỏng
Mẹo và thực hành tốt
- Giảm số lượng ảnh trong một gallery lớn; chia nhóm logic
- Tối ưu ảnh gốc (WebP/AVIF nếu có fallback), nén lossless cho thumbnail
- Trì hoãn khởi tạo PhotoSwipe tới khi người dùng tương tác (giảm JS khởi động)
- Vô hiệu các nút UI không dùng để tối giản
- Thử nghiệm thực tế: tốc độ vuốt, thời lượng animation, độ nhạy pinch/drag
Câu hỏi thường gặp
- Hỏi: Có cần đặt kích thước w/h cho từng ảnh?
- Đáp: Nên đặt để PhotoSwipe tính bố cục và tránh nhảy layout.
- Hỏi: Có thể dùng cho video?
- Đáp: PhotoSwipe tập trung ảnh; video cần giải pháp mở rộng hoặc tích hợp riêng.
- Hỏi: Làm thế nào để mở ảnh theo id từ URL?
- Đáp: Bật history, parse hash (#&gid=…&pid=…), gọi goTo(pid-1) khi init.
- Hỏi: Tùy biến giao diện?
- Đáp: Bật/tắt phần tử UI qua options; ghi đè CSS, lắng nghe sự kiện để gắn hành vi mới.