Ленивая загрузка изображений без jQ
Добрый день!
Делаю сайт без библиотек, все на нативном js. Необходимо добавить ленивую загрузку изображений. Нашел три работающих и в то же время простых варианта загрузки: Изображения в таком формате <figure> <img data-src="https://source.unsplash.com/random/500x300?1" src="https://via.placeholder.com/500x300"> </figure> где, src - placeholder, data-src - изображение 1 вариант function isVisible(elem) { let coords = elem.getBoundingClientRect(), windowHeight = document.documentElement.clientHeight, topVisible = coords.top > 0 && coords.top < windowHeight, bottomVisible = coords.bottom < windowHeight && coords.bottom > 0; return topVisible || bottomVisible; } function isVisible(elem) { let coords = elem.getBoundingClientRect(); let windowHeight = document.documentElement.clientHeight; let extendedTop = -windowHeight; let extendedBottom = 2 * windowHeight; // top visible || bottom visible let topVisible = coords.top > extendedTop && coords.top < extendedBottom; let bottomVisible = coords.bottom < extendedBottom && coords.bottom > extendedTop; return topVisible || bottomVisible; } function showVisible() { for (let img of document.querySelectorAll('img')) { let realSrc = img.dataset.src; if (!realSrc) continue; if (isVisible(img)) { img.src = realSrc; img.dataset.src = ''; } } } window.addEventListener('scroll', showVisible); showVisible(); 2 вариант showPicture(); function showPicture() { let scrolled = window.pageYOffset || document.documentElement.scrollTop; let pics = document.querySelectorAll('[data-src]'); for (i = 0, lengthPics = pics.length; i < lengthPics; i++) { var elemTop = pics[i].getBoundingClientRect().top; if (scrolled + document.documentElement.clientHeight > elemTop) { pics[i].setAttribute('src', pics[i].getAttribute('data-src')); pics[i].removeAttribute("data-src"); } } } window.onscroll = showPicture; 3 вариант window.scrollBy(0, 1); let images = document.querySelectorAll('img'); window.onscroll = function(e) { for (var i = 0; i < images.length; i++) { if (images[i].getBoundingClientRect().top + pageYOffset < document.documentElement.scrollTop + document.documentElement.clientHeight - 100) { images[i].src = images[i].getAttribute('data-src'); } } } Подскажите, какой вариант лучше выбрать и какие могут быть минусы? Не будет ли проблем, если js не сработает по какой-либо причине? Если у вас другой вариант, буду рад. |
|
madeas,
1 более универсальный. и было бы неплохо удалять картинки из очереди обработки, а в конце очереди и саму обработку картинок. по теме https://css-tricks.com/tips-for-roll...-lazy-loading/ |
|
madeas,
смотрите код полностью тут https://github.com/philhawksworth/ro...src/js/lazy.js пути к картинкам у вас будут свои. |
madeas,
смотреть пример можно тут https://lazy-load-demo.netlify.com если не открывается, vpn в помощь. |
lazyLoad упрощёный макет
madeas,
<!DOCTYPE html> <html> <head> <title>Untitled</title> <meta charset="utf-8"> <style type="text/css"> body{ display: flex; flex-direction: column; } img{ width: 300px; height: 350px; background-color: #D3D3D3; opacity: 0.2; transition: .8s; } img.open{ opacity: 1; } </style> </head> <body> <img class="lazy" src="" data-src="https://picsum.photos/300/350/?random&r=1" alt=""> <img class="lazy" src="" data-src="https://picsum.photos/300/350/?random&r=2" alt=""> <img class="lazy" src="" data-src="https://picsum.photos/300/350/?random&r=3" alt=""> <img class="lazy" src="" data-src="https://picsum.photos/300/350/?random&r=4" alt=""> <img class="lazy" src="" data-src="https://picsum.photos/300/350/?random&r=5" alt=""> <img class="lazy" src="" data-src="https://picsum.photos/300/350/?random&r=6" alt=""> <img class="lazy" src="" data-src="https://picsum.photos/300/350/?random&r=7" alt=""> <script> function lazyLoad(elements) { elements.forEach(image => { if (image.intersectionRatio > 0) { const img = image.target; img.addEventListener('load', _ => { img.classList.add("open"); }, false); // set the src attribute to trigger a load img.src = img.dataset.src; // stop observing this element. Our work here is done! observer.unobserve(img); }; }); }; var observer = new IntersectionObserver(lazyLoad, { // where in relation to the edge of the viewport, we are observing rootMargin: "100px", // how much of the element needs to have intersected // in order to fire our loading function threshold: 1.0 }); var lazyImages = document.querySelectorAll('img.lazy'); lazyImages.forEach(img => { observer.observe(img); }); </script> </body> </html> |
Вот я и пытаюсь воссоздать пример с нетлифи. Но в этом примере вся загвоздка в том, что указание путей и их размеры указываются в отдельном vue.component + json файле.
Т.е. в этой части: function loadImage(picture) { let sources = picture.children, loadingPath = "images/tiny", sizes = ["large","medium","small"]; for(var s = 0; s < sources.length; s++) { if (sources[s].hasAttribute("srcset")) { updateAttributeURL(sources[s], "srcset", loadingPath, "images/"+sizes[s] ); } else { updateAttributeURL(sources[s], "src", loadingPath, "images/"+sizes[s] ); } sources[s].addEventListener('load', image => { image.target.closest("picture").classList.remove("lazy-initial") }, false); } } Я убрал пути, но что-то не срабатывает. Подскажите, что упустил? UPD: срабатывает при ресайзе рабочей области |
vue lazyLoad
madeas,
<!DOCTYPE html> <html> <head> <title>Untitled</title> <meta charset="utf-8"> <style type="text/css"> .grid{ display: flex; flex-direction: column; } picture img{ width: 300px; height: 350px; background-color: #FF00FF; opacity: 1; transition: .8s 2s; } picture.lazy-initial .img{ opacity: 0.2; } </style> </head> <body translate="no"> <div id="app"> <responsive :list="list"></responsive> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script> <script id="rendered-js"> Vue.component("responsive", { props: ["list"], template: ` <div class="grid"> <template v-for="(f, index) in list" :key="index"> <picture class="lazy lazy-initial"> <source :srcset="f.large" media="(min-width: 62em)"> <source :srcset="f.medium" media="(min-width: 48em)"> <source :srcset="f.small" media="(min-width: 36em)"> <img :alt="f.caption" :data-src="f.img" src > </picture> </template> </div>` }); new Vue({ el: "#app", created() { this.list = JSON.parse(` [ { "large": "190x127", "medium": "350x234", "small": "520x347", "img": "330x220" }, { "large": "190x127", "medium": "350x234", "small": "520x347", "img": "330x220" }, { "large": "190x127", "medium": "350x234", "small": "520x347", "img": "330x220" }, { "large": "190x127", "medium": "350x234", "small": "520x347", "img": "330x220" }, { "large": "190x127", "medium": "350x234", "small": "520x347", "img": "330x220" } ] `); } }); // Update the image source on elements in the picture element function loadImage(picture) { var sources = picture.children; var loadingPath = "https://via.placeholder.com/"; for(var s=0; s<sources.length; s++) { // remove the lazy-initial class when the full image is loaded to unblur sources[s].addEventListener('load', image => { image.target.closest("picture").classList.remove("lazy-initial") }, false); // update the src or srcset urls if (sources[s].hasAttribute("srcset")) { sources[s].setAttribute("srcset",loadingPath + sources[s].getAttribute("srcset")); } else { sources[s].src = loadingPath + sources[s].dataset.src ; } } } // Stop observing this image and load its source function lazyLoad(elements) { elements.forEach(item => { if (item.intersectionRatio > 0) { observer.unobserve(item.target); loadImage(item.target); }; }); }; // Set up the intersection observer to detect when to define // and load the real image source var options = { rootMargin: "100px", threshold: 1.0 }; var observer = new IntersectionObserver(lazyLoad, options); // Watch for all pictures with a "lazy" class var pictures = document.querySelectorAll('picture.lazy'); pictures.forEach(pic => { observer.observe(pic); }); </script> </body> </html> |
madeas,
строки 39 - 42 должны содержать реальную ссылку на "заглушку" , картинку в 1 пиксель например. эту ссылку потом нужно заменить в строке 101 и 104 за счёт массива sizes (убрано в макете) |
Часовой пояс GMT +3, время: 05:26. |