@ribajs/shopify
Version:
Shopify extension for Riba.js
120 lines (107 loc) • 3.44 kB
text/typescript
import { Binder } from "@ribajs/core";
import { getViewportDimensions, throttle } from "@ribajs/utils";
import { imgUrlFormatter } from "../formatters/img-url.formatter.js";
const PX_OFFSET = 10;
const OVERWRITE_ORIGINAL_SRC = true;
/**
* shopify-img
* Loads an shopify image with the exact size for the current `img` element
* without the need to get the right size manually over the `img_url` filter / formatter.
* The image source path is set by the `srcset` and `sizes` attributes to make them responsive.
*/
export class ShopifyImgBinder extends Binder<string, HTMLImageElement> {
static key = "shopify-img";
private initialSrc?: string;
private oldImageWidth = (PX_OFFSET + 1) * -1;
private resizeObserver?: ResizeObserver;
private setSrcset(width: number) {
// Max width
if (width > 5760) {
width = 5760;
}
let currentSrcset = this.el.srcset;
let currentSizes = this.el.sizes;
if (!imgUrlFormatter.read) {
throw new Error("Shopify imgUrlFormatter read method is missing!");
}
const vw = getViewportDimensions().w;
const filterScale = Math.round(window.devicePixelRatio || 1);
const filterSize = width + "x";
const newSrc = imgUrlFormatter.read(
this.initialSrc,
filterSize,
filterScale,
undefined,
undefined,
this.el,
);
if (typeof currentSrcset === "string" && currentSrcset.length > 0) {
currentSrcset = currentSrcset + ", ";
} else {
currentSrcset = "";
}
if (typeof currentSizes === "string" && currentSizes.length > 0) {
currentSizes = currentSizes + ", ";
} else {
currentSizes = "";
}
const newSrcset = `${currentSrcset}${newSrc} ${width}w`;
const newSizes = `${currentSizes} (width: ${vw}px) ${width}px`;
this.el.srcset = newSrcset;
this.el.sizes = newSizes;
if (OVERWRITE_ORIGINAL_SRC) {
this.el.src = newSrc;
}
}
private _onResize() {
const currentImageWidth = this.el.offsetWidth;
const currentSrcset = this.el.srcset;
if (
this.oldImageWidth + PX_OFFSET < currentImageWidth &&
currentImageWidth > 0 &&
!currentSrcset.includes(`${currentImageWidth}w`)
) {
this.setSrcset(currentImageWidth);
this.oldImageWidth = currentImageWidth;
}
}
private onResize = throttle(this._onResize.bind(this));
bind(el: HTMLImageElement) {
this.initialSrc = el.src;
if (window.ResizeObserver) {
this.resizeObserver = new ResizeObserver((entries) => {
entries.forEach(() => {
this.onResize();
});
});
this.resizeObserver.observe(el);
} else {
window.addEventListener("resize", this.onResize);
}
}
unbind(el: HTMLImageElement) {
window.removeEventListener("resize", this.onResize);
if (this.resizeObserver && this.resizeObserver.unobserve) {
this.resizeObserver.unobserve(el);
}
}
routine(el: HTMLImageElement, src: string) {
this.el = el;
if (!imgUrlFormatter.read) {
throw new Error("Shopify imgUrlFormatter read method is missing!");
}
if (src) {
this._onResize();
setTimeout(this.onResize, 200);
// Set src attribute if it is not set statically
if (
!this.initialSrc ||
typeof this.initialSrc !== "string" ||
this.initialSrc.length <= 0
) {
this.initialSrc = src;
el.src = src;
}
}
}
}