@zoom-image/core
Version:
A core implementation of zoom image
223 lines (217 loc) • 7.77 kB
JavaScript
;
var store = require('@namnode/store');
// src/createZoomImageHover.ts
// src/imageLoader.ts
var THRESHOLD = 50;
var makeImageLoader = () => {
const createZoomImage = (img, src, store) => {
if (img.src === src)
return;
img.src = src;
let complete = false;
img.onload = () => {
complete = true;
store.setState({ zoomedImgStatus: "loaded" });
};
img.onerror = () => {
complete = true;
store.setState({ zoomedImgStatus: "error" });
};
setTimeout(() => {
if (!complete)
store.setState({ zoomedImgStatus: "loading" });
}, THRESHOLD);
};
return {
createZoomImage
};
};
var imageLoader = makeImageLoader();
// src/utils.ts
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
function preventDefault(event) {
event.preventDefault();
}
var keySet = /* @__PURE__ */ new Set(["ArrowUp", "ArrowRight", "ArrowDown", "ArrowLeft"]);
function preventDefaultForScrollKeys(event) {
if (keySet.has(event.key)) {
preventDefault(event);
return false;
}
}
var controller = new AbortController();
var signal = controller.signal;
function disableScroll() {
window.addEventListener("DOMMouseScroll", preventDefault, { signal });
window.addEventListener("wheel", preventDefault, { passive: false, signal });
window.addEventListener("touchmove", preventDefault, { passive: false, signal });
window.addEventListener("keydown", preventDefaultForScrollKeys, { signal });
}
function enableScroll() {
controller?.abort();
}
function getSourceImage(container) {
if (!container) {
throw new Error("Please specify a container for the zoom image");
}
const sourceImgElement = container.querySelector("img");
if (!sourceImgElement) {
throw new Error("Please place an image inside the container");
}
return sourceImgElement;
}
// src/createZoomImageHover.ts
function createZoomImageHover(container, options) {
const controller2 = new AbortController();
const { signal: signal2 } = controller2;
const sourceImgElement = getSourceImage(container);
const zoomedImgWrapper = document.createElement("div");
zoomedImgWrapper.style.overflow = "hidden";
const zoomedImg = zoomedImgWrapper.appendChild(document.createElement("img"));
zoomedImg.alt = options.zoomImageProps?.alt || "";
zoomedImg.style.maxWidth = "none";
zoomedImg.style.display = "none";
const zoomLens = container.appendChild(document.createElement("div"));
zoomLens.style.display = "none";
let sourceImageElementWidth = 0;
let sourceImageElementHeight = 0;
const finalOptions = {
zoomImageSource: options.zoomImageSource || sourceImgElement.src,
zoomLensClass: options.zoomLensClass || "",
zoomTargetClass: options.zoomTargetClass || "",
customZoom: options.customZoom,
scale: options.scale || 2,
zoomTarget: options.zoomTarget,
zoomLensScale: options.zoomLensScale || 1,
disableScrollLock: options.disableScrollLock || false
};
const {
scale,
zoomImageSource,
customZoom,
zoomLensClass,
zoomTarget,
zoomLensScale,
zoomTargetClass,
disableScrollLock
} = finalOptions;
const store$1 = store.createStore({
zoomedImgStatus: "idle",
enabled: true
});
let offset = getOffset(sourceImgElement);
function getOffset(element) {
const elRect = element.getBoundingClientRect();
return { left: elRect.left, top: elRect.top };
}
function getLimitX(value) {
return sourceImageElementWidth - value;
}
function getLimitY(value) {
return sourceImageElementHeight - value;
}
function zoomLensLeft(left) {
const minX = zoomLens.clientWidth / 2;
return clamp(left, minX, getLimitX(minX)) - minX;
}
function zoomLensTop(top) {
const minY = zoomLens.clientHeight / 2;
return clamp(top, minY, getLimitY(minY)) - minY;
}
function processZoom(event) {
let offsetX;
let offsetY;
let backgroundX;
let backgroundY;
if (offset) {
offsetX = zoomLensLeft(event.clientX - offset.left);
offsetY = zoomLensTop(event.clientY - offset.top);
backgroundX = offsetX * scale / zoomLensScale;
backgroundY = offsetY * scale / zoomLensScale;
zoomedImg.style.transform = "translate(" + -backgroundX + "px," + -backgroundY + "px)";
zoomLens.style.cssText += "transform:translate(" + offsetX + "px," + offsetY + "px);";
}
}
async function handlePointerEnter() {
imageLoader.createZoomImage(zoomedImg, zoomImageSource, store$1);
zoomedImg.style.display = "block";
zoomLens.style.display = "block";
if (zoomTargetClass) {
const classes = zoomTargetClass.split(" ");
classes.forEach((className) => zoomTarget.classList.add(className));
}
if (!disableScrollLock)
disableScroll();
}
function handlePointerLeave() {
zoomedImg.style.display = "none";
zoomLens.style.display = "none";
if (zoomTargetClass) {
const classes = zoomTargetClass.split(" ");
classes.forEach((className) => zoomTarget.classList.remove(className));
}
if (!disableScrollLock)
enableScroll();
}
function handleScroll() {
offset = getOffset(sourceImgElement);
}
async function setup() {
if (zoomLensClass) {
zoomLens.className = zoomLensClass;
} else {
zoomLens.style.background = "rgba(238, 130, 238, 0.5)";
}
container.addEventListener("pointerdown", processZoom, { signal: signal2 });
container.addEventListener("pointermove", processZoom, { signal: signal2 });
container.addEventListener("pointerenter", handlePointerEnter, { signal: signal2 });
container.addEventListener("pointerleave", handlePointerLeave, { signal: signal2 });
window.addEventListener("scroll", handleScroll, { signal: signal2 });
container.addEventListener("touchend", enableScroll, { signal: signal2 });
zoomTarget.appendChild(zoomedImgWrapper);
await new Promise((resolve) => setTimeout(resolve, 1));
const containerRect = container.getBoundingClientRect();
sourceImageElementWidth = containerRect.width;
sourceImageElementHeight = containerRect.height;
if (customZoom) {
zoomedImgWrapper.style.width = customZoom.width + "px";
zoomedImgWrapper.style.height = customZoom.height + "px";
} else {
zoomedImgWrapper.style.width = sourceImageElementWidth + "px";
zoomedImgWrapper.style.height = sourceImageElementHeight + "px";
}
zoomedImg.width = sourceImageElementWidth * scale / zoomLensScale;
zoomedImg.height = sourceImageElementHeight * scale / zoomLensScale;
const sourceImageRect = sourceImgElement.getBoundingClientRect();
const fromLeft = sourceImageRect.left - containerRect.left;
const fromTop = sourceImageRect.top - containerRect.top;
zoomTarget.style.pointerEvents = "none";
zoomLens.style.position = "absolute";
zoomLens.style.left = fromLeft + "px";
zoomLens.style.top = fromTop + "px";
zoomLens.style.width = customZoom.width / scale * zoomLensScale + "px";
zoomLens.style.height = customZoom.height / scale * zoomLensScale + "px";
}
setup();
return {
cleanup: () => {
controller2.abort();
container.contains(zoomLens) && container.removeChild(zoomLens);
if (zoomTarget && zoomTarget.contains(zoomedImgWrapper)) {
zoomTarget.removeChild(zoomedImgWrapper);
return;
}
container.contains(zoomedImgWrapper) && container.removeChild(zoomedImgWrapper);
},
subscribe: store$1.subscribe,
getState: store$1.getState,
setState: (newState) => {
store$1.setState(newState);
}
};
}
exports.createZoomImageHover = createZoomImageHover;
//# sourceMappingURL=out.js.map
//# sourceMappingURL=createZoomImageHover.js.map