11.10.2019, 14:52
|
|
Профессор
|
|
Регистрация: 13.04.2018
Сообщений: 232
|
|
Ленивая загрузка изображений без 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 не сработает по какой-либо причине?
Если у вас другой вариант, буду рад.
|
|
11.10.2019, 15:01
|
|
Профессор
|
|
Регистрация: 18.05.2011
Сообщений: 1,207
|
|
|
|
11.10.2019, 16:26
|
|
Профессор
|
|
Регистрация: 13.04.2018
Сообщений: 232
|
|
рони,
вроде сделал как написано, но что-то не работает
Песочница
|
|
11.10.2019, 17:58
|
|
Профессор
|
|
Регистрация: 27.05.2010
Сообщений: 33,126
|
|
madeas,
смотреть пример можно тут https://lazy-load-demo.netlify.com если не открывается, vpn в помощь.
|
|
11.10.2019, 19:11
|
|
Профессор
|
|
Регистрация: 27.05.2010
Сообщений: 33,126
|
|
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>
Последний раз редактировалось рони, 11.10.2019 в 19:48.
|
|
11.10.2019, 21:05
|
|
Профессор
|
|
Регистрация: 13.04.2018
Сообщений: 232
|
|
Вот я и пытаюсь воссоздать пример с нетлифи. Но в этом примере вся загвоздка в том, что указание путей и их размеры указываются в отдельном 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:
срабатывает при ресайзе рабочей области
Последний раз редактировалось madeas, 11.10.2019 в 21:34.
|
|
11.10.2019, 22:15
|
|
Профессор
|
|
Регистрация: 27.05.2010
Сообщений: 33,126
|
|
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>
|
|
11.10.2019, 22:27
|
|
Профессор
|
|
Регистрация: 27.05.2010
Сообщений: 33,126
|
|
madeas,
строки 39 - 42 должны содержать реальную ссылку на "заглушку" , картинку в 1 пиксель например.
эту ссылку потом нужно заменить в строке 101 и 104 за счёт массива sizes (убрано в макете)
|
|
|
|