@pi0/framework7
Version:
Full featured mobile HTML framework for building iOS & Android apps
206 lines (188 loc) • 5.84 kB
JavaScript
import $ from 'dom7';
import Utils from '../../utils/utils';
const Lazy = {
destroy(pageEl) {
const $pageEl = $(pageEl).closest('.page');
if (!$pageEl.length) return;
if ($pageEl[0].f7DestroyLazy) {
$pageEl[0].f7DestroyLazy();
}
},
init(pageEl) {
const app = this;
const $pageEl = $(pageEl).closest('.page').eq(0);
// Lazy images
const lazyLoadImages = $pageEl.find('.lazy');
if (lazyLoadImages.length === 0 && !$pageEl.hasClass('lazy')) return;
// Placeholder
const placeholderSrc = app.params.lazy.placeholder;
if (placeholderSrc !== false) {
lazyLoadImages.each((index, lazyEl) => {
if ($(lazyEl).attr('data-src') && !$(lazyEl).attr('src')) $(lazyEl).attr('src', placeholderSrc);
});
}
// load image
const imagesSequence = [];
let imageIsLoading = false;
function onImageComplete(lazyEl) {
if (imagesSequence.indexOf(lazyEl) >= 0) {
imagesSequence.splice(imagesSequence.indexOf(lazyEl), 1);
}
imageIsLoading = false;
if (app.params.lazy.sequential && imagesSequence.length > 0) {
imageIsLoading = true;
app.lazy.loadImage(imagesSequence[0], onImageComplete);
}
}
function lazyHandler() {
app.lazy.load($pageEl, (lazyEl) => {
if (app.params.lazy.sequential && imageIsLoading) {
if (imagesSequence.indexOf(lazyEl) < 0) imagesSequence.push(lazyEl);
return;
}
imageIsLoading = true;
app.lazy.loadImage(lazyEl, onImageComplete);
});
}
function attachEvents() {
$pageEl.on('lazy', lazyHandler);
$pageEl.on('scroll', lazyHandler, true);
$pageEl.find('.tab').on('tab:mounted tab:show', lazyHandler);
app.on('resize', lazyHandler);
}
function detachEvents() {
$pageEl.off('lazy', lazyHandler);
$pageEl.off('scroll', lazyHandler, true);
$pageEl.find('.tab').off('tab:mounted tab:show', lazyHandler);
app.off('resize', lazyHandler);
}
// Store detach function
$pageEl[0].f7DestroyLazy = detachEvents;
// Attach events
attachEvents();
// Run loader on page load/init
lazyHandler();
},
isInViewport(lazyEl) {
const app = this;
const rect = lazyEl.getBoundingClientRect();
const threshold = app.params.lazy.threshold || 0;
return (
rect.top >= (0 - threshold) &&
rect.left >= (0 - threshold) &&
rect.top <= (app.height + threshold) &&
rect.left <= (app.width + threshold)
);
},
loadImage(imageEl, callback) {
const app = this;
const $imageEl = $(imageEl);
const bg = $imageEl.attr('data-background');
const src = bg || $imageEl.attr('data-src');
if (!src) return;
function onLoad() {
$imageEl.removeClass('lazy').addClass('lazy-loaded');
if (bg) {
$imageEl.css('background-image', `url(${src})`);
} else {
$imageEl.attr('src', src);
}
if (callback) callback(imageEl);
$imageEl.trigger('lazy:loaded');
app.emit('lazyLoaded', $imageEl[0]);
}
function onError() {
$imageEl.removeClass('lazy').addClass('lazy-loaded');
if (bg) {
$imageEl.css('background-image', `url(${app.params.lazy.placeholder || ''})`);
} else {
$imageEl.attr('src', app.params.lazy.placeholder || '');
}
if (callback) callback(imageEl);
$imageEl.trigger('lazy:error');
app.emit('lazyError', $imageEl[0]);
}
const image = new window.Image();
image.onload = onLoad;
image.onerror = onError;
image.src = src;
$imageEl.removeAttr('data-src').removeAttr('data-background');
// Add loaded callback and events
$imageEl.trigger('lazy:load');
app.emit('lazyLoad', $imageEl[0]);
},
load(pageEl, callback) {
const app = this;
let $pageEl = $(pageEl);
if (!$pageEl.hasClass('page')) $pageEl = $pageEl.parents('.page').eq(0);
if ($pageEl.length === 0) {
return;
}
$pageEl.find('.lazy').each((index, lazyEl) => {
const $lazyEl = $(lazyEl);
if ($lazyEl.parents('.tab:not(.tab-active)').length > 0) {
return;
}
if (app.lazy.isInViewport(lazyEl)) {
if (callback) callback(lazyEl);
else app.lazy.loadImage(lazyEl);
}
});
},
};
export default {
name: 'lazy',
params: {
lazy: {
placeholder: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEXCwsK592mkAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==',
threshold: 0,
sequential: true,
},
},
create() {
const app = this;
Utils.extend(app, {
lazy: {
init: Lazy.init.bind(app),
destroy: Lazy.destroy.bind(app),
loadImage: Lazy.loadImage.bind(app),
load: Lazy.load.bind(app),
isInViewport: Lazy.isInViewport.bind(app),
},
});
},
on: {
pageInit(page) {
const app = this;
if (page.$el.find('.lazy').length > 0 || page.$el.hasClass('lazy')) {
app.lazy.init(page.$el);
}
},
pageAfterIn(page) {
const app = this;
if (page.$el.find('.lazy').length > 0 || page.$el.hasClass('lazy')) {
app.lazy.init(page.$el);
}
},
pageBeforeRemove(page) {
const app = this;
if (page.$el.find('.lazy').length > 0 || page.$el.hasClass('lazy')) {
app.lazy.destroy(page.$el);
}
},
tabMounted(tabEl) {
const app = this;
const $tabEl = $(tabEl);
if ($tabEl.find('.lazy').length > 0 || $tabEl.hasClass('lazy')) {
app.lazy.init($tabEl);
}
},
tabBeforeRemove(tabEl) {
const app = this;
const $tabEl = $(tabEl);
if ($tabEl.find('.lazy').length > 0 || $tabEl.hasClass('lazy')) {
app.lazy.destroy($tabEl);
}
},
},
};