Líné načítání obrázků ve vašem projektu angularJS pomocí rozhraní IntersectionObserver API

Nějakou dobu jsem se tedy pokoušel snížit počet posouvacích posluchačů ve svém projektu, dokud jsem nenarazil na rozhraní Intersection Observer API poté, co jsem provedl test majáku ve své webové aplikaci. Implementace byla tak jednoduchá a přímočará, že jsem se okamžitě pustil do její implementace.

Co je Intersection Observer

Intersection Observer API poskytuje způsob, jak asynchronně pozorovat změny v průniku cílového prvku s prvkem předka nebo s výřezem dokumentu nejvyšší úrovně.

Ve výchozím nastavení IntersectionObservers vypočítá, jak velká část cílového prvku překrývá (neboli „protíná“) viditelnou část stránky, známou také jako „výřez prohlížeče“.

Jak to mohu spustit?

Jak jsem řekl výše, nastavení je velmi jednoduché. Vše, co musíme udělat, je definovat nového pozorovatele křižovatky s funkcí zpětného volání a poté sledovat cílový prvek.

Příliš mnoho řečí, uvidíme!!

const observer = new IntersectionObserver(callback);
observer.observe(element)

Je to tak jednoduché, můžeme načíst více nových položek na DOM, načíst obrázek asynchronně a ještě mnohem více, jakmile se objeví cílový prvek. Podívejme se, jak vypadá událost pozorovatele.

Pojďme nastavit základní příklad a zaznamenat události do konzole. Nebudu zabíhat do podrobností o tom, co která hodnota na události dělá nebo znamená, podrobnosti si můžete přečíst zde.

const observer = new IntersectionObserver(callback);
const img = document.getElementById('lazy-img');
observer.observe(img);

function callback(changes){
    console.log(changes)
    changes.forEach(change => {
        console.log(change)
    })
}

Pozorovatel obvykle vrací seznam objektů IntersectionObserverEntry obsahující metadata o změnách cílového prvku.

Pokud chceme zkontrolovat, zda je prvek ve výřezu plně viditelný, naše zpětné volání observeru bude vypadat takto:

const observer = new IntersectionObserver(callback);
const img = document.getElementById('lazy-img');
observer.observe(img);

function callback(changes){
    console.log(changes)
    changes.forEach(change => {
        if(change.intersectionRatio >= 1){
            img.classList.add('visible')
        }
        else{
            img.classList.add('not-visible')
        }
    })
}

Jak už můžeme líně načítat angularJS??

Klidně tam! Jak se k tomu dostat.

Abychom to přidali do našeho projektu angularJS, vytvoříme jednoduchou direktivu, která přidá náš img src, když vstoupí do výřezu. Protože náš obrázek zatím nebude mít atribut src, můžeme k němu přidat výšku a styly barvy pozadí.

img{
    height: 60px;
    background: grey;
}

pak by náš javascriptový kód měl vypadat takto:

angular
    .module('lazy', [])
    .directive('lazyLoad', lazyLoad)

function lazyLoad(){
    return {
        restrict: 'A',
        link: function(scope, element, attrs){
            const observer = new IntersectionObserver(loadImg)
            const img = angular.element(element)[0];
            observer.observe(img)

            function loadImg(changes){
                changes.forEach(change => {
                    if(change.intersectionRatio > 0){
                        change.target.src = 'boy.jpg'
                    }
                })
            }    

        }
    }
}

Naše HTML je další. Mělo by to připomínat toto:

<body ng-app="lazy">
    <div>
        <img alt="" class="" lazy-load>
    </div> 
</body>

A to je vše!! Je zřejmé, že to není dostatečně elegantní, můžete jít ještě o krok dále přidáním kopie vašeho obrázku s nízkým rozlišením a poté její nahrazením kopií s vysokým rozlišením, když se obrázek objeví. Mohli bychom také přidat médium, jako je rozostření, na obrázek a poté jej odhalit, když se objeví.

Nahraďte náš současný kód následujícím níže, aby byl proces o něco elegantnější.

CSS


.img-blur{
    filter: blur(10px);
}

img{
    height: 60px;
    background: gray;
}

HTML

<body ng-app="lazy">
    <div>
        <img src="low-res.jpg" class="img-blur" alt="Lazy load" lazy-load>
    </div>
<body>

JS


angular
    .module('lazy', [])
    .directive('lazyLoad', lazyLoad)

function lazyLoad(){
    return {
        restrict: 'A',
        link: function(scope, element, attrs){
            const observer = new IntersectionObserver(loadImg)
            const img = angular.element(element)[0];
            observer.observe(img)

            function loadImg(changes){
                changes.forEach(change => {
                    if(change.intersectionRatio > 0){
                        change.target.src = 'boy.jpg';
                        change.target.classList.remove('img-blur');
                    }
                })
            }    

        }
    }
}

A je to, máme dostatečně slušný proces líného načítání našich obrázků pomocí IntersectionObserver API a direktiv angular. Jsem si jistý, že existuje několik způsobů, jak tento proces vylepšit, a své nápady můžete napsat do sekce komentářů níže.