Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 11.10.2019, 14:52
Аватар для madeas
Профессор
Отправить личное сообщение для madeas Посмотреть профиль Найти все сообщения от madeas
 
Регистрация: 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 не сработает по какой-либо причине?
Если у вас другой вариант, буду рад.
Ответить с цитированием
  #2 (permalink)  
Старый 11.10.2019, 15:01
Аватар для destus
Профессор
Отправить личное сообщение для destus Посмотреть профиль Найти все сообщения от destus
 
Регистрация: 18.05.2011
Сообщений: 1,207

madeas,
Intersection Observer API
https://blog.bitsrc.io/lazy-loading-...pi-5a913ee226d
Ответить с цитированием
  #3 (permalink)  
Старый 11.10.2019, 15:12
Аватар для рони
Профессор
Отправить личное сообщение для рони Посмотреть профиль Найти все сообщения от рони
 
Регистрация: 27.05.2010
Сообщений: 33,068

madeas,
1 более универсальный.
и было бы неплохо удалять картинки из очереди обработки, а в конце очереди и саму обработку картинок.
по теме
https://css-tricks.com/tips-for-roll...-lazy-loading/
Ответить с цитированием
  #4 (permalink)  
Старый 11.10.2019, 16:26
Аватар для madeas
Профессор
Отправить личное сообщение для madeas Посмотреть профиль Найти все сообщения от madeas
 
Регистрация: 13.04.2018
Сообщений: 232

рони,
вроде сделал как написано, но что-то не работает

Песочница
Ответить с цитированием
  #5 (permalink)  
Старый 11.10.2019, 17:56
Аватар для рони
Профессор
Отправить личное сообщение для рони Посмотреть профиль Найти все сообщения от рони
 
Регистрация: 27.05.2010
Сообщений: 33,068

madeas,
смотрите код полностью тут
https://github.com/philhawksworth/ro...src/js/lazy.js

пути к картинкам у вас будут свои.
Ответить с цитированием
  #6 (permalink)  
Старый 11.10.2019, 17:58
Аватар для рони
Профессор
Отправить личное сообщение для рони Посмотреть профиль Найти все сообщения от рони
 
Регистрация: 27.05.2010
Сообщений: 33,068

madeas,
смотреть пример можно тут https://lazy-load-demo.netlify.com если не открывается, vpn в помощь.
Ответить с цитированием
  #7 (permalink)  
Старый 11.10.2019, 19:11
Аватар для рони
Профессор
Отправить личное сообщение для рони Посмотреть профиль Найти все сообщения от рони
 
Регистрация: 27.05.2010
Сообщений: 33,068

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.
Ответить с цитированием
  #8 (permalink)  
Старый 11.10.2019, 21:05
Аватар для madeas
Профессор
Отправить личное сообщение для madeas Посмотреть профиль Найти все сообщения от madeas
 
Регистрация: 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.
Ответить с цитированием
  #9 (permalink)  
Старый 11.10.2019, 22:15
Аватар для рони
Профессор
Отправить личное сообщение для рони Посмотреть профиль Найти все сообщения от рони
 
Регистрация: 27.05.2010
Сообщений: 33,068

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>
Ответить с цитированием
  #10 (permalink)  
Старый 11.10.2019, 22:27
Аватар для рони
Профессор
Отправить личное сообщение для рони Посмотреть профиль Найти все сообщения от рони
 
Регистрация: 27.05.2010
Сообщений: 33,068

madeas,
строки 39 - 42 должны содержать реальную ссылку на "заглушку" , картинку в 1 пиксель например.
эту ссылку потом нужно заменить в строке 101 и 104 за счёт массива sizes (убрано в макете)
Ответить с цитированием
Ответ


Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Аплоадер изображений без перезагрузки страницы lepeed Общие вопросы Javascript 21 04.08.2014 21:25
Загрузка изображений на сервер Demonuka AJAX и COMET 3 14.05.2014 20:19
Загрузка изображений imediasun1 Элементы интерфейса 4 01.02.2014 23:10
динамическая загрузка ковра из изображений Yzgramor Общие вопросы Javascript 1 02.10.2013 14:07
Загрузка аватарки без перезагрузки страницы kez jQuery 35 22.05.2009 11:12