A super small libary for lazy loading images for Angular apps with zero dependencies
Visit this site: https://tjoskar.github.io/ng-lazyload-image/fade-in-image
The browser you are targeting need to have support of WeakMap
and String.prototype.includes
. If you need to support an older browser (like IE) you will need to include polyfill for WeakMap
and String.prototype.includes
(see https://github.com/zloirock/core-js for example).
Make sure to inclue a pollyfill for IntersectionObserver if you need to target IE: https://github.com/w3c/IntersectionObserver/tree/master/polyfill
To install the package, just run:
$ npm install ng-lazyload-image
or the following if you are using yarn
$ yarn add ng-lazyload-image
Include the library in your module (see app.module.ts):
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { LazyLoadImageModule } from 'ng-lazyload-image'; // <-- import it
import { AppComponent } from './app.component';
@NgModule({
declarations: [ AppComponent ],
imports: [ BrowserModule, LazyLoadImageModule ], // <-- and include it
bootstrap: [ AppComponent ]
})
export class MyAppModule {}
ng-lazyload-image
is using a scroll listener by default so you dont need to do anything if you want to continue using the scroll as event emitter.
Make sure to inclue a pollyfill for IntersectionObserver if you need to target IE: https://github.com/w3c/IntersectionObserver/tree/master/polyfill
You can easily swtich from scroll listener to IntersectionObserver by using the intersectionObserverPreset
:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { LazyLoadImageModule, intersectionObserverPreset } from 'ng-lazyload-image'; // <-- include intersectionObserverPreset
import { AppComponent } from './app.component';
@NgModule({
declarations: [ AppComponent ],
imports: [
BrowserModule,
LazyLoadImageModule.forRoot({
preset: intersectionObserverPreset // <-- tell LazyLoadImage that you want to use IntersectionObserver
})
],
bootstrap: [ AppComponent ]
})
export class MyAppModule {}
See hooks below for more information.
A simple usecase is to use a img
-tag and give it the image to lazyload to [lazyLoad]
and an optional default image to [defaultImage]
. The default image will be shown while the "real" image is getting loaded.
Example:
import { Component } from '@angular/core';
@Component({
selector: 'image',
template: `
<img [defaultImage]="defaultImage" [lazyLoad]="image">
`
})
class ImageComponent {
defaultImage = 'https://www.placecage.com/1000/1000';
image = 'https://images.unsplash.com/photo-1443890923422-7819ed4101c0?fm=jpg';
}
It also supports background images, by using backgroundImage
:
@Component({
selector: 'image',
template: `
<div [defaultImage]="defaultImage" [lazyLoad]="image"></div>
`
})
class ImageComponent {
defaultImage = 'https://www.placecage.com/1000/1000';
image = 'https://images.unsplash.com/photo-1443890923422-7819ed4101c0?fm=jpg';
}
If using responsive images in a plain <img>
tag, you'll need to set the useSrcset
attribute to true
:
@Component({
selector: 'image',
template: `
<img [defaultImage]="defaultImage"
[lazyLoad]="images"
[useSrcset]="true">
`
})
class ImageComponent {
defaultImage = 'https://www.placecage.com/1000/1000';
images = `https://images.unsplash.com/photo-1434725039720-aaad6dd32dfe?fm=jpg 700w,
https://images.unsplash.com/photo-1437818628339-19ded67ade8e?fm=jpg 1100w`;
}
If using responsive images in a <picture>
tag, set the default <img>
tag as usual with lazyLoad
etc. attributes.
You can use attr.lazyLoad
, attr.defaultImage
and attr.errorImage
attributes for <source>
elements.
There's no need to set useSrcset
for <source>
elements, as srcset
is used by default.
A simple example for a <picture>
tag:
@Component({
selector: 'image',
template: `
<picture>
<source media="(min-width: {{ screen_lg }})" [attr.defaultImage]="defaultImage" [attr.lazyLoad]="image2">
<source media="(min-width: {{ screen_md }})" [attr.defaultImage]="defaultImage" [attr.lazyLoad]="image3">
<img [defaultImage]="defaultImage" [lazyLoad]="image1">
</picture>
`
})
class ImageComponent {
screen_lg = '1200px';
screen_md = '992px';
defaultImage = 'https://www.placecage.com/1000/1000';
image1 = 'https://images.unsplash.com/photo-1422004707501-e8dad229e17a?fm=jpg';
image2 = 'https://images.unsplash.com/photo-1439931444800-9bcc83f804a6?fm=jpg';
image3 = 'https://images.unsplash.com/photo-1417128281290-30a42da46277?fm=jpg';
}
You can load image async or change the url on the fly, eg.
<img [lazyLoad]="image$ | async">
Sometimes you want to get more controll over the scroll event. scrollObservable
lets you create your own scroll observable.
import { merge, fromEvent } from 'rxjs'
...
constructor() {
this.scroll$ = merge(
fromEvent(window, 'scroll'),
fromEvent(someDivRef, 'scroll')
);
}
<img [scrollObservable]="scroll$" ... >
If you are using Ionic 2 and don't want to use IntersectionObserver, you may need to include your own scroll observable or change the scroll target. For instans if you want to have multiple scroll targets:
@Component({
selector: 'page-image',
template: `
<ion-content #container padding>
<img [defaultImage]="https://www.placecage.com/1000/1000" [lazyLoad]="lazyLoadImage" [scrollObservable]="container.ionScroll" />
</ion-content>
`
})
export class AboutPage {
lazyLoadImage = 'https://hd.unsplash.com/photo-1431400445088-1750c997c6b5';
}
In case of using ion-slides in Ionic 2+, you can include your own scroll observable as below.
@Component({
selector: 'page-image',
template: `
<ion-content #container padding>
<img [defaultImage]="https://www.placecage.com/1000/1000" [lazyLoad]="lazyLoadImage" [scrollObservable]="container.ionSlideWillChange" />
</ion-content>
`
})
export class AboutPage {
lazyLoadImage = 'https://hd.unsplash.com/photo-1431400445088-1750c997c6b5';
}
Type: string
Example: https://images.unsplash.com/photo-1443890923422-7819ed4101c0?fm=jpg
The image to be lazy loaded. This image will replace the default image (defaultImage
).
<img [defaultImage]="'https://www.placecage.com/1000/1000'" [lazyLoad]="'https://hd.unsplash.com/photo-1431400445088-1750c997c6b5'">
Type: string
Example: https://www.placecage.com/1000/1000
Path to default image. This image will be loaded right away.
You can also use src
attribute for img tag to define default image:
<img src="https://www.placecage.com/1000/1000" [lazyLoad]="lazyLoadImage" />
or background-image
property for non-image tags:
<div style="background-image: url('https://www.placecage.com/1000/1000');" [lazyLoad]="lazyLoadImage"></div>
Type: string
Example: https://i.imgur.com/XkU4Ajf.png
An image to be loaded if failing to load lazyLoad
. Will load the default image (defaultImage
) if absent.
<img [defaultImage]="someDefaultImage" [lazyLoad]="imageToLazyLoad" [errorImage]="imageToShowOnError">
Type: number
Example: 100
Default: 0
Number of px a image should be loaded before it is in view port
<img [defaultImage]="someDefaultImage" [lazyLoad]="imageToLazyLoad" offset="100">
Type: Element
Example: document.getElementById('my-scroll-container')
Default: window
You will need to set this property if you are using a scroll container and do not propagate the scroll event to window.
Type: Observable
Example: Observable.fromEvent(myScrollContainer, 'scroll')
You can pass your own observable if you need more control over the flow. Can be useful if integrating with other frameworks like ionic.
Type: boolean
Example: true
You can set this to true
if you need to lazy load images with srcset
attribute, instead of src
.
<source>
tags are set to use srcset
by default, so you don't need to set this option additionaly.
Type: Function: (success: boolean) => void
Example: <img [lazyLoad]="lazyLoadImage" (onLoad)="myCallbackFunction($event)">
You can pass a callback function, which will be called when the image is loaded.
It is possible to hook into the loading process by create your own functions.
For example, let's say you want to fetch an image with some custom headers. If so, you can create a custom hook to fetch the image:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { LazyLoadImageModule, intersectionObserverPreset, LoadImageProps } from 'ng-lazyload-image';
import { AppComponent } from './app.component';
function loadImage({ imagePath }: LoadImageProps): Promise<string> {
return await fetch(imagePath, {
headers: {
Authorization: 'Bearer ...'
}
}).then(res => res.blob()).then(blob => URL.createObjectURL(blob));
}
@NgModule({
declarations: [ AppComponent ],
imports: [
BrowserModule,
LazyLoadImageModule.forRoot({ loadImage })
],
bootstrap: [ AppComponent ]
})
export class MyAppModule {}
The following hooks are supported:
Should return an observable that emits a new value every time ng-lazyload-image
should check if the image is in viewport.
Eg.
import { Attributes } from 'ng-lazyload-image';
// This will trigger an event every second
function getObservable(attributes: Attributes) {
return interval(1000);
}
See intersection-listener.ts for example.
Function to check if the element is vissible.
Eg.
import { IsVisibleProps } from 'ng-lazyload-image';
function isVisible({ event, element, scrollContainer, offset }: IsVisibleProps<SomeEvent>) {
// `event` is form `getObservable`
return isElementInViewport(element, scrollContainer, offset);
}
Function to load the image. It must return a path to the image (it can however be async, like the example below and/or return a observable).
import { LoadImageProps } from 'ng-lazyload-image';
function loadImage({ imagePath }: LoadImageProps) {
return await fetch(imagePath, {
headers: {
Authorization: 'Bearer ...'
}
}).then(res => res.blob()).then(blob => URL.createObjectURL(blob));
}
If you don't want to load the image but instead let the browser load it for you, then you can just return the imagePath (We will however not know if the image can't be loaded and the error image will not be used):
function loadImage({ imagePath }: LoadImageProps) {
return [ imagePath ];
}
A function to set the image url to the DOM.
Eg.
import { SetLoadedImageProps } from 'ng-lazyload-image';
function setLoadedImage({ element, imagePath, useSrcset }: SetLoadedImageProps) {
// `imagePath` comes from `loadImage`
element.src = imagePath;
}
This function will be called if the lazy image cant be loaded.
Eg.
import { SetErrorImageProps } from 'ng-lazyload-image';
function setErrorImage({ element, errorImagePath, useSrcset }: SetErrorImageProps) {
element.src = errorImagePath;
}
This function will be called on setup. Can be usefull for (re)setting css-classes and setting the default image.
This function will be called every time an attrebute is changing.
Eg.
import { Attributes } from 'ng-lazyload-image';
function setup(atter: Attributes) {
// Do something
}
This function will be called on teardown. Can be usefull for setting css-classes.
Eg.
import { Attributes } from 'ng-lazyload-image';
function finally(atter: Attributes) {
// Do something
}
Preset can be usefull when you want to set multible of the functions above.
eg.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { LazyLoadImageModule, intersectionObserverPreset } from 'ng-lazyload-image';
import { AppComponent } from './app.component';
@NgModule({
declarations: [ AppComponent ],
imports: [
BrowserModule,
LazyLoadImageModule.forRoot({
preset: intersectionObserverPreset
})
],
bootstrap: [ AppComponent ]
})
export class MyAppModule {}
If you want to use the intersectionObserverPreset
but overwride on of the functions, you can easily do that:
LazyLoadImageModule.forRoot({
preset: intersectionObserverPreset,
finally: ({ element }) => console.log('The image is loaded', element)
})
Q How can I manually trigger the loading of images?
A See: tjoskar#197
Q Does this library work with ionic or some other wrapper for Angular?
A Yes, but ionic and some other library wraps the whole document inside an other div so you might need to create your own scroll listener. https://github.com/tjoskar/ng-lazyload-image/issues?utf8=%E2%9C%93&q=is%3Aissue+Ionic
Q How can I add a transition effect between the default image and the lazy loaded image?
A See: tjoskar#300
Q I can't get it to work with electron. Can you help me?
A Make sure you uses the right file path.
Q I'm getting the error: NullInjectorError: No provider for ElementRef!
A See: tjoskar#390
Q I can't get it to work. Can you help me?
A Sure, create an issue and describe your issue in as much detail as possible.
See the contributing guide