Skip to content

Instantly share code, notes, and snippets.

@aktaumag
Last active June 11, 2024 04:33
Show Gist options
  • Save aktaumag/1f221ff4774ffb81ad0a7bf97fab46bd to your computer and use it in GitHub Desktop.
Save aktaumag/1f221ff4774ffb81ad0a7bf97fab46bd to your computer and use it in GitHub Desktop.
Как внедрять картинки и ускорять их.
Всё о важности картинок и их ускорении
@aktaumag
Copy link
Author

aktaumag commented Dec 12, 2018

На GitHubе инструкция, которая хорошо работает

Подключаем скрипт (скачиваем или через CDN):
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/lazyload.min.js"></script>
или перед закрытием </body>

<script src="/js/lazyload.min.js"></script>
<script>
var myLazyLoad = new LazyLoad({
    elements_selector: ".lazy"
});
</script>

Загружаем какую-нибудь иконку loading.svg, которая будет символизировать загрузку и позволит пройти валидацию, так как обязательный параметр SRC должен быть.

Для прохождения всех валидаций картинка вставляется так, хотя по инструкции рекомендуется не писать параметр SRC
<img class="lazy" src="/images/loading.svg" data-src="/images/upload/img-name.jpeg" alt="что на картинке...">

Если не вставлять анимированную иконку как SRC параметр, то обязательно в стилях прописать такое:

img:not([src]) {
    visibility: hidden;
}

Если надо применить технологию к картинке, которая загружается как бекграунд, то делаем так:
<div class="lazy" data-bg="url(../img/44721746JJ_15_a.jpg)"></div>

Если применяем технологию к скрытым элементам, например к картинкам, которые находятся в выпадающем меню, то в скрипте, вызывающем открытие этого элемента, нужно прописать myLazyLoad.update();

@aktaumag
Copy link
Author

Оптимизация SVG файлов:
https://github.com/svg/svgo
онлайн - https://jakearchibald.github.io/svgomg/

@aktaumag
Copy link
Author

https://loading.io/ svg preloaders

@aktaumag
Copy link
Author

Новая технология loading="lazy" - уверен, что приживётся

@aktaumag
Copy link
Author

aktaumag commented Sep 8, 2020

к <img> и <iframe> добавлять атрибуты decoding="async" loading="lazy"

@aktaumag
Copy link
Author

aktaumag commented Sep 8, 2020

https://www.youtube.com/watch?v=F6KGcb6trXc

Самый важный атрибут картинки — ALT, должен вписываться в содержание страницы.
alt не должен равняться заголовку страницы или раздела/блока/секции, в которой находится картинка. Вообще не должен быть дублем другого текста страницы.
alt="" — если это alt пустой, то по сути картинка должна считаться системной (картинка для дизайна), но на самом деле так не происходит, поэтому все системные картинки лучше использовать в стилях как фон, но не через тег img.

Чем лучше качество картинки, тем больше вероятность попадания в топ. Хоть до 20.000px в ширину и высоту, но хватает того, чтобы картинки были лучше, чем у конкурентов.
Минимум: 1920 × 1080 пикселей

URL адрес картинки повторяет структуру самой страницы
Например, картинка для страницы https://wseo.kz/redirect/service/https должна лежать по адресу https://wseo.kz/redirect/service/https/pereadresaciya-na-https-protokol.jpg

пример:

<img 
    src="/path/img-1920.jpg"
    srcset="/path/img-640.jpg 640w,
            /path/img-920.jpg 920w,
            /path/img-1220.jpg 1220w"
    alt="Не заголовки, а описание того, что изображено на картинке. Текст любой длины, но учитываются первые 100 символов"
    decoding="async" 
    loading="lazy" 
/>

Картинка с тегом src не оптимизируется и содержит максимальное качество. Именно она и индексируется.
Оптимизируются картинки в атрибуте srcset, одна из них и будет отображаться.


Даже если картинки нет (не загрузится), то по дизайну блок под картинку должен сразу иметь нужную ширину и высоту!!!
При последующей загрузке картинки блоки не должны прыгать в размерах.


Картинка должна сочетаться с текстом и должна содержать образы того, о чём написано на странице.
Проверить можно здесь: https://cloud.google.com/vision

Картинка должна быть без водяного знака.
На картинке не должно быть текста.
Картинки должны содержать метатеги в формате IPTC для указания вашего авторства. (Byline/Author/Creator + Copyright Notice + Source)


Тег <picture> удобный, но в плане технической оптимизации он сильно увеличивает структуру DOM. Минимум в 3 раза для каждого изображения. Верхняя граница DOM дерева страницы = 1500 узлов... мак. глубина = 32 узла... ЧЕМ МЕНЬШЕ, ТЕМ ЛУЧШЕ.

Тег <figure> вообще не подходит для указания картинок, так как гласит о том, что это отдельный объект страницы, который следует понимать в отрыве от всего остального контента. Иными словами - не SEO картинка и не относится к содержанию страницы.

Картинки, указанные через стили, НЕ ИНДЕКСИРУЮТСЯ: background: url("/path/img.jpg");
Влияние на SEO оказывают только картинки в теге IMG, указанные в атрибуте SRC.
Поэтому, все дизайнерские элементы и иконки (рейтинг, корзина, телефоны, соцсети) должны быть через стили ::after, ::before или background:


Желательно важные картинки сайта внедрять сразу с разметкой

<div itemscope itemtype="http://schema.org/ImageObject">
  <img 
    src="/path/img-1920.jpg"
    itemprop="contentUrl" 
    srcset="/path/img-640.jpg 640w,
            /path/img-920.jpg 920w,
            /path/img-1220.jpg 1220w"
    alt="Не заголовки, а описание того, что изображено на картинке. Текст любой длины, но учитываются первые 100 символов"
    decoding="async" 
    loading="lazy" 
  />
  <meta itemprop="thumbnail" content="/path/img-640.jpg">
  <meta itemprop="thumbnail" content="/path/img-920.jpg">
  <meta itemprop="thumbnail" content="/path/img-1220.jpg">
  <!-- ну и если уместно, и в разметке уже есть данные, то можно дополнить ещё и этими -->
  <span itemprop="name">название картинки</span>
  <span itemprop="description">описание изображения</span>
  <span itemprop="caption">подпись к картинке</span>
</div>

Если картинка в SVG, то её указываем именно в srcset в единичном варианте для всех размеров, а для атрибута src создаём растровый вариант из этой картинки:

<img 
    src="/path/img-1920.jpg"
    srcset="/path/img.svg 100w"
    sizes="100vw"
    alt="Не заголовки, а описание того, что изображено на картинке. Текст любой длины, но учитываются первые 100 символов"
    decoding="async" 
    loading="lazy" 
/>

Особенно для логотипа компании на брендовых страницах.


https://responsivebreakpoints.com/
Пример нарезки картинок


ВИДЕО
Вместо iframe ставится картинка (превьюшка) и вешается обработчик по клику, который меняет эту картинку на iframe.
При этом всю нужную для SEO информацию мы передаём через микроразметку Shema.org для разметки видео.

@aktaumag
Copy link
Author

aktaumag commented Sep 24, 2020

Сейчас нативный loading="lazy" работает плохо, в том плане, что загружает картинки на 7 экранов вниз, а при плохом 3G соединении вообще на все 14... таких длинных сайтов то редко встретишь.
Поэтому ещё актуально использовать костыли
https://apoorv.pro/lozad.js/

<img style="width:100%;  max-width:300px;"
  class="demilazyload"
  alt="Demimurych Lazy Tester"
  src="itak.png"
  data-srcset="itak2.webp 100w"
  srcset="image.svg 100w"
  sizes="100vw"
  />

<script type="text/javascript" src="lozad.js"></script>
<script>
  lozad('.demilazyload').observe();
</script>

При таком подходе надо создать пустой SVG как заглушку и указывать у каждой картинки параметры ширины и высоты, чтобы не прыгал дизайн сайта при загрузке.

<svg xmlns="http://www.w3.org/2000/svg" version="1.1"></svg>
<style>
  img{
    max-width: 100%;
    width: auto;
    height: auto;
  }
  img.wsbgll {
    background-color: #3333331a;
  }
</style>

<img src="/img/picture.png"
  width="350"
  height="170"
  class="wslazy wsbgll"
  data-srcset="/img/picture.webp 100w"
  srcset="/img/empty.svg 100w"
  sizes="100vw"
  decoding="async"
  loading="lazy"
  alt="">

<script src="/js/lozad.js"></script>
<script>
  lozad('.wslazy').observe();
</script>

@aktaumag
Copy link
Author

WordPress

<?php
function ws_lazy_load($content){

    $include_noscript = false;

    // Normal image
    $matches = array();
    preg_match_all( '/<img[\s\r\n]+.*?>/is', $content, $matches );

    $search = array();
    $replace = array();

    $i = 0;
    foreach ( $matches[0] as $imgHTML ) {
        if (strpos($imgHTML, 'wsnolazy') === false && strpos($imgHTML, 'data-src') === false && ! preg_match( "/src=['\"]data:image/is", $imgHTML )) {
            $i++;
            $replaceHTML = $imgHTML;

            if ( preg_match( '/class=["\']/i', $replaceHTML ) ) {
                $replaceHTML = preg_replace( '/class=(["\'])(.*?)["\']/is', 'class=$1wslazy $2$1', $replaceHTML );
            } else {
                $replaceHTML = preg_replace( '/<img/is', '<img class="wslazy"', $replaceHTML );
            }

            if ( !preg_match( '/sizes=["\']/i', $replaceHTML ) ) {
                $replaceHTML = preg_replace( '/ src=["\'](.*?)["\']/is', ' src="$1" srcset="/wp-content/themes/newTheme/assets/js/empty.svg 100w" data-srcset="$1 100w" sizes="100vw"', $replaceHTML );
            } else {
                $replaceHTML = preg_replace( '/<img(.*?) srcset=/is', '<img$1 srcset="/wp-content/themes/newTheme/assets/js/empty.svg 100w" data-srcset=', $replaceHTML );
            }

            if ( !preg_match( '/decoding=["\']/i', $replaceHTML ) ) {
                $replaceHTML = preg_replace( '/<img/is', '<img decoding="async"', $replaceHTML );
            }

            if ( !preg_match( '/loading=["\']/i', $replaceHTML ) ) {
                $replaceHTML = preg_replace( '/<img/is', '<img loading="lazy"', $replaceHTML );
            }

            if ( !preg_match( '/width=["\']/i', $replaceHTML ) && !preg_match( '/height=["\']/i', $replaceHTML ) ) {
                // получаем размеры ширины и высоты картинки
                if (preg_match( '/ src=["\'](.*?)["\']/is', $replaceHTML, $wssrc )){
                    $wssrcpath = $wssrc[1];
                    if(substr($wssrcpath, 0, 1) == '/') {
                        $wssrcpath = '.'.$wssrc[1];
                    }
                    elseif(substr($wssrcpath, 0, 4) == 'www.') {
                        $wssrcpath = '//'.$wssrc[1];
                    }

                    $imageparam = getimagesize(htmlspecialchars($wssrcpath));
                    if($imageparam){
                        $wswidth = $imageparam[0];
                        $wsheight = $imageparam[1];
                        $replaceHTML = preg_replace( '/<img/is', '<img width="'.$wswidth.'" height="'.$wsheight.'"', $replaceHTML );
                    }

                }
            }

            if ( $include_noscript ) {
                $replaceHTML .= '<noscript>' . $imgHTML . '</noscript>';
            }

            array_push( $search, $imgHTML );
            array_push( $replace, $replaceHTML );
        }
    }

    $search = array_unique( $search );
    $replace = array_unique( $replace );

    $content = str_replace( $search, $replace, $content );

    return $content;
}
add_filter( 'the_content', 'ws_lazy_load' );
?>

@aktaumag
Copy link
Author

Отложенная загрузка для Яндекс Карты.
Появляется если доскролить до нужного места, но на всякий случай есть ещё и триггеры на наведение мышкой, клик и пр.

<!-- размер контейнера под размер карты и цвет окраски в стиле сайта -->
<div id="wsmainmap" style="display: block; width: 100%; height: 680px;background-color: #0078881a;">

<!-- скрипт карты, которой даём нужное ID и переделываем SRC в DATA-SRC  -->
<script type="text/javascript" charset="utf-8" async id="map_wslazy" data-src="https://api-maps.yandex.ru/services/constructor/1.0/js/?um=constructor%3A982500c531973a06e0aa07174eafc6dbaabfdf0cf8785f8e326da852e1963c32&amp;width=100%25&amp;height=680&amp;lang=ru_RU&amp;scroll=true"></script>

</div>

<script>
        let map_container = document.getElementById('wsmainmap');
        let options_map = {
            once: true,
            passive: true,
            capture: true
        };
        map_container.addEventListener('click', start_lazy_map, options_map);
        map_container.addEventListener('mouseover', start_lazy_map, options_map);
        map_container.addEventListener('touchstart', start_lazy_map, options_map);
        map_container.addEventListener('touchmove', start_lazy_map, options_map);
        
        var iObserver = new IntersectionObserver(function(entries) {
            if (entries[0].isIntersecting === true) {
                start_lazy_map();
            }
        }, {threshold: [0]}); // от 0 до 1, % видимой части элемента на экране

        iObserver.observe(map_container);

        let map_loaded = false;
        function start_lazy_map() {
            if (!map_loaded) {
                let map_block = document.getElementById('map_wslazy');
                map_loaded = true;
                map_block.setAttribute('src', map_block.getAttribute('data-src'));
                map_block.removeAttribute('data-src');
                console.log('YMAP LOADED');
            }
        }
</script>

@aktaumag
Copy link
Author

aktaumag commented Jul 7, 2022

Источник: https://codepen.io/fe-nix/pen/PoQvxQa

let observer = new IntersectionObserver(
  function (entries) {
    for (let i in entries) {
      let el = entries[i].target;
      if (entries[i].isIntersecting === true) {
        if (el.dataset.srcset) {
          el.setAttribute("srcset", el.dataset.srcset);
        } else {
          el.removeAttribute("srcset");
        }
        el.addEventListener(
          "load",
          function () {
            el.classList.add("loaded");
          },
          { passive: true }
        );
      }
    }
  },
  { threshold: [0], rootMargin: "100px 0px 100px 0px" } // Здесь вы выбираете на сколько пикселей вверх право низ и лево будут элементы будут считаться внутри вьюпорта и начинут загружаться в текущем примере 100 пикселей сверху и снизу.
);

document.addEventListener(
  "DOMContentLoaded",
  function () {
    let mas = document.querySelectorAll(".lazy"); // Слектор для изображений, здесь используется .lazy
    for (let i = 0; i < mas.length; i++) {
      observer.observe(mas[i]);
    }
  },
  { passive: true }
);

Пример вставки:

<img src="https://perfscan.ru/static/gallery/img/320/002.jpg" srcset="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" alt="" width="320" height="180" decoding="async" class="lazy">

<img src="https://perfscan.ru/static/gallery/img/320/003.jpg" srcset="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" data-srcset="https://perfscan.ru/static/gallery/img/640/003.jpg 2x" alt="" width="320" height="180" decoding="async" class="lazy">

@aktaumag
Copy link
Author

aktaumag commented Sep 25, 2023

LazyLoad для загрузки картинок при помощи CSS background

<style>
.wsBgLazy{background-image: none !important;}
</style>

<div class="wsbglazy" data-bgimgurl="image.jpg"></div>
<div class="wsbglazy" data-bgurl="image.jpg"></div>
<div class="wsbglazy"></div> <!-- картинка прописана где-то в CSS файлах стилей -->

<script>
    const wsBgObserver = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
            if (entry.isIntersecting) {
                let wsEl = entry.target;
                if (wsEl.dataset.bgurl) {
                    wsEl.style.background = "url('"+wsEl.dataset.bgurl+"') 0 0 no-repeat";
                }
                else if(wsEl.dataset.bgimgurl) {
                    wsEl.style.backgroundImage = "url('"+wsEl.dataset.bgimgurl+"')";
                }
                wsEl.classList.remove("wsBgLazy");
                wsEl.classList.add("wsBgLoaded");
                wsBgObserver.unobserve(wsEl);
            }
        });
    });

    document.addEventListener(
        "DOMContentLoaded",
        function () {
            const wsBgMass = document.querySelectorAll(".wsBgLazy");
            wsBgMass.forEach((el) => {
                wsBgObserver.observe(el);
            });
        },
        { passive: true }
    );
</script>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment