Bài viết trình bày cách triển khai hiệu ứng hộp đèn (lightbox) sử dụng jQuery với plugin tùy chỉnh. Dưới đây là mã nguồn chi tiết:
Mã HTML cơ bản để hiển thị gallery ảnh:
<html>
<head>
<meta charset="UTF-8">
<title>Hiệu ứng hộp đèn jQuery</title>
<link rel="stylesheet" href="css/lightbox-style.css">
<script src="js/jquery-3.6.0.min.js"></script>
<script src="js/lightbox-plugin.js"></script>
</head>
<body>
<div class="hinh-anh">
<!-- href chứa ảnh lớn, src là ảnh thu nhỏ -->
<a href="images/large1.jpg" title="Hình A">
<img src="images/thumb1.jpg">
</a>
<a href="images/large2.jpg" title="Hình B">
<img src="images/thumb2.jpg">
</a>
<a href="images/large3.jpg" title="Hình C">
<img src="images/thumb3.jpg">
</a>
<a href="images/large4.jpg" title="Hình D">
<img src="images/thumb4.jpg">
</a>
</div>
<script>
$('.hinh-anh').initLightbox({
itemSelector: 'a',
animationSpeed: 350
});
</script>
</body>
</html>
Plugin lightbox-plugin.js được viết lại như sau:
(function ($) {
$.initLightbox = function ($container, config) {
this.cauHinh = $.extend(true, {}, $.initLightbox.defaultSettings, config);
this.$phanTuCha = $container;
this.$modal = null;
this.$cacHinh = null;
this.viTriHienTai = 0;
this.khoiTao();
};
$.initLightbox.defaultSettings = {
giaoDien: 'lightbox',
itemSelector: 'a',
nutQuayLui: '←',
nutTiepTheo: '→',
thongBaoLoad: 'Đang tải...',
nutDong: '×',
tocDoFade: 350,
mucZIndex: 999,
tuLapLai: true,
thuocTinhChuThich: 'title',
mauHinh: function ($phanTu, cauHinh, cbXuLy) {
return $('<img>').attr('src', $phanTu.attr('href')).addClass(cauHinh.giaoDien + '-noi-dung').on('load', cbXuLy);
}
};
$.initLightbox.capNhatCauHinh = function (cauHinhMoi) {
$.extend(true, $.initLightbox.defaultSettings, cauHinhMoi);
};
$.extend($.initLightbox.prototype, {
khoiTao: function () {
var _this = this;
return this.$phanTuCha.on('click.lightbox', this.cauHinh.itemSelector, function (e) {
e.preventDefault();
_this.hienThi(this);
});
},
hienThi: function (phanTu) {
var _this = this;
this.$cacHinh = this.cauHinh.itemSelector ? this.$phanTuCha.find(this.cauHinh.itemSelector) : this.$phanTuCha;
var chiSo = isNaN(phanTu) ? this.$cacHinh.index(phanTu) : phanTu;
this.$modal = $('<div>').addClass(this.cauHinh.giaoDien).hide().css('zIndex', this.cauHinh.mucZIndex)
.append($('<a>').attr('href', '#').html(this.cauHinh.nutDong).addClass('nut-dong'))
.append($('<a>').attr('href', '#').html(this.cauHinh.nutQuayLui).addClass('nut-quay-lui'))
.append($('<a>').attr('href', '#').html(this.cauHinh.nutTiepTheo).addClass('nut-tiep-theo'))
.append($('<div>').addClass('noi-dung'))
.append($('<div>').addClass('chu-thich').append($('<p>')))
.appendTo('body')
.fadeIn(this.cauHinh.tocDoFade)
.on('click', '.nut-dong', function (e) { e.preventDefault(); _this.ẩn(); })
.on('click', '.nut-tiep-theo', function (e) { e.preventDefault(); _this.tiep(); })
.on('click', '.nut-quay-lui', function (e) { e.preventDefault(); _this.quayLui(); });
$(document).on('swipeLeft', function () { _this.tiep(); })
.on('swipeRight', function () { _this.quayLui(); })
.on('keydown', function (e) {
switch (e.keyCode) {
case 27: _this.ẩn(); break;
case 37: _this.quayLui(); break;
case 39: _this.tiep(); break;
}
});
this.diChuyenDen(chiSo);
return this.$phanTuCha;
},
ẩn: function () {
var _this = this;
this.$modal.fadeOut(this.cauHinh.tocDoFade, function () {
_this.$modal.remove();
_this.$modal = null;
$(document).off('.lightbox');
});
return this.$phanTuCha;
},
diChuyenDen: function (chiSo) {
var _this = this, $hinh = $(this.$cacHinh[chiSo]);
this.viTriHienTai = chiSo;
var chuThich = $hinh.attr(this.cauHinh.thuocTinhChuThich);
this.$modal.find('.chu-thich').toggle(chuThich).find('p').text(chuThich);
var $noiDung = this.$modal.find('.noi-dung');
$noiDung.html('<div class="thong-bao">' + this.cauHinh.thongBaoLoad + '</div>');
var $anh = this.cauHinh.mauHinh($hinh, this.cauHinh, function () {
$noiDung.empty().append(this);
});
if (this.$cacHinh.length <= 1 || !this.cauHinh.tuLapLai) {
this.$modal.find('.nut-quay-lui').toggle(chiSo > 0);
this.$modal.find('.nut-tiep-theo').toggle(chiSo < this.$cacHinh.length - 1);
}
return this.$phanTuCha;
},
quayLui: function () {
return this.diChuyenDen(this.viTriHienTai === 0 ? this.$cacHinh.length - 1 : this.viTriHienTai - 1);
},
tiep: function () {
return this.diChuyenDen(this.viTriHienTai === this.$cacHinh.length - 1 ? 0 : this.viTriHienTai + 1);
}
});
$.fn.initLightbox = function (cauHinh) {
return this.each(function () {
var $phanTu = $(this);
$phanTu.data('lightbox', new $.initLightbox($phanTu, cauHinh));
});
};
})(jQuery);
File CSS hỗ trợ hiệu ứng:
.lightbox {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
z-index: 999;
}
.lightbox * {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.noi-dung {
position: absolute;
top: 10%;
left: 10%;
width: 80%;
height: 80%;
}
.noi-dung img {
max-width: 100%;
max-height: 100%;
border: 4px solid #fff;
border-radius: 2px;
box-shadow: 0 0 15px #000;
}
.chu-thich {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
background: rgba(0,0,0,0.6);
text-align: center;
padding: 8px 0;
}
.chu-thich p {
max-width: 60%;
margin: 0 auto;
color: #fff;
font-size: 14px;
}
.nut-dong, .nut-quay-lui, .nut-tiep-theo {
position: absolute;
z-index: 1000;
background: rgba(0,0,0,0.5);
color: #fff;
font-size: 24px;
text-align: center;
line-height: 40px;
width: 40px;
height: 40px;
border-radius: 50%;
cursor: pointer;
transition: all 0.3s ease;
}
.nut-dong:hover, .nut-quay-lui:hover, .nut-tiep-theo:hover {
background: rgba(0,0,0,0.8);
transform: scale(1.3);
}
.nut-dong {
right: 15px;
top: 15px;
}
.nut-tiep-theo {
right: 15px;
top: 50%;
transform: translateY(-50%);
}
.nut-quay-lui {
left: 15px;
top: 50%;
transform: translateY(-50%);
}
.thong-bao {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #fff;
font-size: 20px;
}