diff --git a/assets/scripts/modules/Scroll.js b/assets/scripts/modules/Scroll.js index c8c4e56..3ac9856 100644 --- a/assets/scripts/modules/Scroll.js +++ b/assets/scripts/modules/Scroll.js @@ -1,4 +1,5 @@ import { module } from 'modujs'; +import { lazyLoadImage } from '../utils/image'; import LocomotiveScroll from 'locomotive-scroll'; export default class extends module { @@ -22,16 +23,17 @@ export default class extends module { }) } - toggleLazy(args) { - let src = this.getData('lazy', args.obj.el) - if (src.length) { - if (args.obj.el.tagName === 'IMG') { - args.obj.el.src = src - } else { - args.obj.el.style.backgroundImage = `url('${src}')` - } - this.setData('lazy', '', args.obj.el) - } + /** + * Lazy load + * See '../utils/image' + * Recommended to wrap your image in `.c-lazy`. `-lazy-loaded` modifier will be applied on both parent and children + * + * @param {obj} | Locomotive Scroll object + */ + lazyLoad(args) { + lazyLoadImage(args.obj.target, null, () => { + //callback + }) } destroy() { diff --git a/assets/scripts/utils/image.js b/assets/scripts/utils/image.js new file mode 100644 index 0000000..90f01b7 --- /dev/null +++ b/assets/scripts/utils/image.js @@ -0,0 +1,79 @@ +import { queryClosestParent } from './html'; + +const LAZY_LOADED_IMAGES = [] + +export function loadImage(url, options = {}) { + return new Promise((resolve, reject) => { + const $img = new Image(); + if (options.crossOrigin) $img.crossOrigin = options.crossOrigin; + + const loadCallback = () => { + resolve({ + element: $img, + ...getImageMetadata($img), + }); + } + + if($img.decode) { + $img.src = url + $img.decode().then(loadCallback).catch(e => { + reject(e) + }) + } else { + $img.onload = loadCallback + $img.onerror = (e) => { + reject(e); + }; + $img.src = url + } + }); +} + +export function getImageMetadata($img) { + return { + url: $img.src, + width: $img.naturalWidth, + height: $img.naturalHeight, + ratio: $img.naturalWidth / $img.naturalHeight, + }; +} + +/** + * Lazy load images + * + * @param {node} | $el + * @param {string} | url + * @param {function} | callback + */ +export async function lazyLoadImage($el, url, callback = () => {}) { + let src = url ? url : $el.dataset.src + + let loadedImage = LAZY_LOADED_IMAGES.find(image => image.url === src) + + if (!loadedImage) { + loadedImage = await loadImage(src) + if (!loadedImage.url) return; + LAZY_LOADED_IMAGES.push(loadedImage) + } + + if($el.src === src) { + return + } + + if ($el.tagName == "IMG") { + $el.src = loadedImage.url; + } else { + $el.style.backgroundImage = `url(${loadedImage.url})`; + } + + requestAnimationFrame(() => { + let lazyParent = queryClosestParent($el, '.c-lazy') + if(lazyParent) { + lazyParent.classList.add('-lazy-loaded') + lazyParent.style.backgroundImage = '' + } + $el.classList.add('-lazy-loaded') + + callback?.() + }) +}