Author: | aderaaij |
---|---|
Views Total: | 3,882 views |
Official Page: | Go to website |
Last Update: | October 3, 2017 |
License: | MIT |
Preview:

Description:
This is a vanilla JavaScript lightbox slider where images will open in a responsive, fullscreen lightbox with the ability to scroll through all images.
More features:
- Image lazy load.
- Configurable animation speed.
- Arrows navigation.
- Keyboard/Touch events supported.
How to use it:
Create a thumbnail gallery from a normal html list as follows:
<ul> <li class="gallery__item"> <a class="gallery__itemLink" href="1.jpg" data-rel="aiLightbox"> <img class="gallery__itemThumb" src="thumb-1.jpg"> </a> </li> <li class="gallery__item"> <a class="gallery__itemLink" href="1.jpg" data-rel="aiLightbox"> <img class="gallery__itemThumb" src="thumb-2.jpg"> </a> </li> <li class="gallery__item"> <a class="gallery__itemLink" href="1.jpg" data-rel="aiLightbox"> <img class="gallery__itemThumb" src="thumb-3.jpg"> </a> </li> <li class="gallery__item"> <a class="gallery__itemLink" href="1.jpg" data-rel="aiLightbox"> <img class="gallery__itemThumb" src="thumb-4.jpg"> </a> </li> ... </ul>
The primary CSS styles for the lightbox slider.
.gallery ul { list-style: none; margin: 0; padding: 0; width: 100%; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: justify; -ms-flex-pack: justify; justify-content: space-between; -ms-flex-wrap: wrap; flex-wrap: wrap; } .gallery li { margin-bottom: 1em; border-radius: 3px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); -webkit-transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); } .gallery li:hover { box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22); } .gallery img { display: block; border-radius: 3px; } .m-lightbox { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(255, 255, 255, 0.9); z-index: 1; opacity: 0; -webkit-transform: scale(0.8); transform: scale(0.8); -webkit-transition: opacity 0.3s ease-out, -webkit-transform 0.3s ease-out; transition: opacity 0.3s ease-out, -webkit-transform 0.3s ease-out; transition: opacity 0.3s ease-out, transform 0.3s ease-out; transition: opacity 0.3s ease-out, transform 0.3s ease-out, -webkit-transform 0.3s ease-out; pointer-events: none; } .m-lightbox.is-active { opacity: 1; -webkit-transform: scale(1); transform: scale(1); z-index: 101; pointer-events: auto; } .m-lightbox__slider { list-style: none; margin: 0; padding: 0; width: 100vw; height: 100vh; } .m-lightbox__slide { position: absolute; top: 0; left: 0; width: 100vw; height: 100vh; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; -webkit-box-align: center; -ms-flex-align: center; align-items: center; } .m-lightbox__slide img { display: block; max-width: calc(100vw - 2em); max-height: 90vh; opacity: 0; -webkit-transition: opacity 0.3s ease; transition: opacity 0.3s ease; } @media (min-width: 768px) { .m-lightbox__slide img { max-width: calc(100vw - 116px); max-height: 90vh; } } .m-lightbox__slide.is-loaded.is-active img { opacity: 1; } .m-lightbox__slide.is-loaded.is-active .spinner { display: none; } .m-lightbox button { position: absolute; margin: 0; padding: 0; z-index: 102; background: transparent; border: none; cursor: pointer; } .m-lightbox__close { top: 1em; right: 1em; } .m-lightbox__nextPrev { top: 50%; -webkit-transform: translateY(-50%); transform: translateY(-50%); width: 42px; height: 42px; visibility: hidden; opacity: 0; -webkit-transform: scale(0.5); transform: scale(0.5); -webkit-transition: opacity 0.3s ease-out, -webkit-transform 0.3s ease-out; transition: opacity 0.3s ease-out, -webkit-transform 0.3s ease-out; transition: opacity 0.3s ease-out, transform 0.3s ease-out; transition: opacity 0.3s ease-out, transform 0.3s ease-out, -webkit-transform 0.3s ease-out; } .m-lightbox__nextPrev.is-active { visibility: hidden; -webkit-transform: scale(1); transform: scale(1); opacity: 1; } @media (min-width: 1024px) { .m-lightbox__nextPrev.is-active { visibility: visible; } } .m-lightbox__nextPrev svg { display: block; width: 100%; height: auto; } .m-lightbox__nextPrev--next { right: 1em; } .m-lightbox__nextPrev--prev { left: 1em; } .m-lightbox__nextPrev:hover { cursor: pointer; } .m-lightbox__counter { position: absolute; bottom: 1em; left: 50%; -webkit-transform: translateX(-50%); transform: translateX(-50%); color: #333; font-weight: 700; }
Style & animate the spinner while loading the large images.
.spinner { width: 40px; height: 40px; position: absolute; top: 50%; left: 50%; -webkit-transform: translateY(-50%) translateX(-50%); transform: translateY(-50%) translateX(-50%); } .spinner::before, .spinner::after { content: ''; width: 100%; height: 100%; border-radius: 50%; background-color: #333; opacity: 0.6; position: absolute; top: 0; left: 0; -webkit-animation: sk-bounce 2.0s infinite ease-in-out; animation: sk-bounce 2.0s infinite ease-in-out; } .spinner::after { -webkit-animation-delay: -1.0s; animation-delay: -1.0s; } @-webkit-keyframes sk-bounce { 0%, 100% { -webkit-transform: scale(0); transform: scale(0); } 50% { -webkit-transform: scale(1); transform: scale(1); } } @keyframes sk-bounce { 0%, 100% { -webkit-transform: scale(0); transform: scale(0); } 50% { -webkit-transform: scale(1); transform: scale(1); } }
Load the swipe.js library for touch swipe support.
<script src="Swipe.js"></script>
The main function.
'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; var _class, _temp, _initialiseProps; function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) {if (window.CP.shouldStopExecution(1)){break;} if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } window.CP.exitedLoop(1); return target; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Lightbox = (_temp = _class = function Lightbox(_ref) { var _ref$lazyload = _ref.lazyload; var lazyload = _ref$lazyload === undefined ? true : _ref$lazyload; var _ref$counter = _ref.counter; var counter = _ref$counter === undefined ? true : _ref$counter; var _ref$arrows = _ref.arrows; var arrows = _ref$arrows === undefined ? true : _ref$arrows; var _ref$slideSpeed = _ref.slideSpeed; var slideSpeed = _ref$slideSpeed === undefined ? 400 : _ref$slideSpeed; var options = _objectWithoutProperties(_ref, ['lazyload', 'counter', 'arrows', 'slideSpeed']); _classCallCheck(this, Lightbox); _initialiseProps.call(this); if (!options.selector) { console.error('Please add a valid css selector with the option "selector:"'); } else if (typeof options.selector !== 'string') { console.error(options.selector, 'is not a string but a(n) ' + _typeof(options.selector)); } else { this.selector = options.selector; this.lazyload = lazyload; this.counter = counter; this.arrows = arrows; this.slideSpeed = slideSpeed; this.links = Array.from(document.querySelectorAll('a' + options.selector)); this.offsets = []; this.nodes = {}; this.imageIndex = null; if (this.links.length > 0) { this.createLightbox(); this.createNodes(); this.eventListeners(this.links); } else { console.error('The selector \'' + this.selector + '\' did not yield results. Please make sure the selector is applied on an \'a\' element.'); } } }, _initialiseProps = function _initialiseProps() { var _this = this; this.goTo = function (num, event) { var _nodes = _this.nodes; var items = _nodes.items; var counter = _nodes.counter; var lightboxNode = _nodes.lightboxNode; if (_this.counter) { counter.innerHTML = num + 1 + '/' + _this.links.length; } var spinner = '<div class="spinner"></div>'; var img = items[num].querySelector('img'); if (_this.lazyload) { var src = img.getAttribute('data-src'); items[num].insertAdjacentHTML('beforeend', spinner); // Set image attribute img.setAttribute('src', src); // Add class to slide item when image is completely loaded. Must be in this order. var imgLoad = new Image(); imgLoad.onload = function () { items[num].classList.add('is-active'); items[num].classList.add('is-loaded'); }; imgLoad.src = src; } else { items[num].classList.add('is-active'); items[num].classList.add('is-loaded'); } // Change the offset for each slide based on its index and the current index. for (var i = 0; i < _this.offsets.length; i++) {if (window.CP.shouldStopExecution(2)){break;} var offset = _this.offsets[i] - num * 100; items[i].style.transform = 'translateX(' + offset + 'vw)'; // Add transition type based on which event was triggered if (event) { if (event.target.className === 'gallery__itemThumb') { items[i].style.transition = 'opacity 0.4s ease'; } else { items[i].style.transition = 'transform ' + _this.slideSpeed + 'ms ease-out'; } } } window.CP.exitedLoop(2); }; this.createNodes = function (links) { // Find all the lightbox nodes and add them to an object Object.assign(_this.nodes, { lightboxNode: document.querySelector('.m-lightbox'), items: Array.from(document.querySelectorAll('.m-lightbox__slide')), next: document.querySelector('.m-lightbox__nextPrev--next'), prev: document.querySelector('.m-lightbox__nextPrev--prev'), close: document.querySelector('.m-lightbox__close') }); Object.assign(_this.nodes, { counter: document.querySelector('.m-lightbox__counter') }); }; this.eventListeners = function (links) { var _nodes2 = _this.nodes; var lightboxNode = _nodes2.lightboxNode; var items = _nodes2.items; var next = _nodes2.next; var prev = _nodes2.prev; var close = _nodes2.close; links.forEach(function (item, index) { item.addEventListener('click', function (e) { e.preventDefault(); lightboxNode.classList.add('is-active'); document.body.style.overflow = 'hidden'; _this.imageIndex = index; _this.goTo(index, e); _this.setNav(index); }); }); next.addEventListener('click', function (e) { _this.goToNext(e); }); prev.addEventListener('click', function (e) { _this.goToPrev(e); }); close.addEventListener('click', function () { _this.closeBox(); }); document.onkeydown = function (e) { switch (e.keyCode) { case 37: _this.goToPrev(e); break; case 39: _this.goToNext(e); break; case 27: _this.closeBox(); break; }; }; items.forEach(function (item) { // https://gist.github.com/Tam/d44c87b3daeb07b15984ddc6127d4e34 new Swipe(item.querySelector('img'), function (e, direction) { e.preventDefault(); switch (direction) { case "up": // Handle Swipe Up break; case "down": // Handle Swipe Down break; case "left": _this.goToNext(e); break; case "right": _this.goToPrev(e); break; } }); }); }; this.setNav = function (index) { if (_this.arrows) { var _nodes3 = _this.nodes; var next = _nodes3.next; var prev = _nodes3.prev; if (index < _this.links.length - 1) { next.classList.add('is-active'); } if (index >= _this.links.length - 1) { next.classList.remove('is-active'); } if (index > 0) { prev.classList.add('is-active'); } if (index <= 0) { prev.classList.remove('is-active'); } } }; this.goToNext = function (e) { var items = _this.nodes.items; if (_this.imageIndex < items.length - 1) { _this.goTo(_this.imageIndex + 1, e); setTimeout(function () { items[_this.imageIndex - 1].classList.remove('is-active'); }, _this.slideSpeed); _this.imageIndex += 1; _this.setNav(_this.imageIndex); } }; this.goToPrev = function (e) { var items = _this.nodes.items; if (_this.imageIndex > 0) { _this.goTo(_this.imageIndex - 1, e); setTimeout(function () { items[_this.imageIndex + 1].classList.remove('is-active'); }, _this.slideSpeed); _this.imageIndex -= 1; _this.setNav(_this.imageIndex); } }; this.closeBox = function () { var _nodes4 = _this.nodes; var lightboxNode = _nodes4.lightboxNode; var items = _nodes4.items; lightboxNode.classList.remove('is-active'); document.body.style.overflow = 'auto'; setTimeout(function () { items.forEach(function (item) { return item.classList.remove('is-active'); }); }, _this.slideSpeed); }; this.renderImages = function (images) { var imagesLinks = images.map(function (item, index) { var offset = index * 100; _this.offsets.push(offset); var imageSrc = item.getAttribute('href'); return '\n <li class=\'m-lightbox__slide\' style=\'transform: translateX(' + offset + 'vw)\'>\n ' + (_this.lazyload ? '\n <img data-src=\'' + imageSrc + '\'/>\n ' : '\n <img src=\'' + imageSrc + '\'/>\n ') + '\n </li>\n '; }); return imagesLinks; }; this.createLightbox = function () { var lightbox = '\n <div class=\'m-lightbox\'>\n <div class=\'m-lightbox__controls\'>\n <button class=\'m-lightbox__close\'>\n <svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">\n <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>\n <path d="M0 0h24v24H0z" fill="none"/>\n </svg>\n </button>\n <button class=\'m-lightbox__nextPrev m-lightbox__nextPrev--prev\'>\n <svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">\n <path d="M0 0h24v24H0z" fill="none"/>\n <path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/>\n </svg>\n </button>\n <button class=\'m-lightbox__nextPrev m-lightbox__nextPrev--next\'>\n <svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">\n <path d="M0 0h24v24H0z" fill="none"/>\n <path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"/>\n </svg>\n </button>\n </div>\n <ul class=\'m-lightBox__slider\'>\n ' + _this.renderImages(_this.links).join('') + '\n </ul>\n <div class=\'m-lightbox__counter\'>\n </div>\n </div>\n '; document.body.insertAdjacentHTML('beforeend', lightbox); }; }, _temp);
Activate the lightbox slider with settings.
var lb = new Lightbox({ selector: '[data-rel="aiLightbox"]', // string lazyload: true, // boolean arrows: true, // boolean counter: true, // boolean slideSpeed: 500 });