@zoom-image/core
Version:
A core implementation of zoom image
198 lines (191 loc) • 6.92 kB
JavaScript
var ZoomImage = (function (exports) {
'use strict';
// ../../node_modules/.pnpm/@namnode+store@0.1.0/node_modules/@namnode/store/dist/chunk-TZNK2OF3.mjs
function f(o) {
let r = /* @__PURE__ */ new Set(), s = false, a = o, e, c = (t = {}) => {
e = { ...e, ...t }, i();
}, i = () => {
if (s)
return;
let t = false;
if (e) {
for (let n in e)
if (a[n] !== e[n]) {
t = true;
break;
}
}
t && (a = { ...a, ...e }, r.forEach((n) => n({ state: a, updatedProperties: e })), e = void 0);
};
return { subscribe: (t) => (r.add(t), () => {
r.delete(t);
}), cleanup: () => r.clear(), getState: () => a, setState: c, batch: (t) => {
s = true, t(), s = false, i();
} };
}
// 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 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/createZoomImageMove.ts
function createZoomImageMove(container, options = {}) {
let activePointerId = null;
const sourceImgElement = getSourceImage(container);
const finalOptions = {
zoomFactor: options.zoomFactor ?? 4,
zoomImageSource: options.zoomImageSource ?? sourceImgElement.src,
disabledContextMenu: options.disabledContextMenu ?? false
};
const { disabledContextMenu, zoomFactor, zoomImageSource } = finalOptions;
const store = f({
zoomedImgStatus: "idle"
});
const zoomedImg = document.createElement("img");
options.zoomImageProps?.alt && (zoomedImg.alt = options.zoomImageProps.alt);
options.zoomImageProps?.className && (zoomedImg.className = options.zoomImageProps.className);
zoomedImg.style.maxWidth = "none";
zoomedImg.style.position = "absolute";
zoomedImg.style.top = "0";
zoomedImg.style.left = "0";
if (disabledContextMenu) {
zoomedImg.style["-webkit-user-select"] = "none";
zoomedImg.style["-webkit-touch-callout"] = "none";
zoomedImg.oncontextmenu = () => false;
}
const checkValidPointer = (event) => {
return activePointerId && event.pointerId === activePointerId;
};
function handlePointerEnter(event) {
if (activePointerId === null) {
activePointerId = event.pointerId;
container.appendChild(zoomedImg);
zoomedImg.style.display = "block";
const zoomedImgWidth = sourceImgElement.clientWidth * zoomFactor;
const zoomedImgHeight = sourceImgElement.clientHeight * zoomFactor;
zoomedImg.style.width = `${zoomedImgWidth}px`;
zoomedImg.style.height = `${zoomedImgHeight}px`;
imageLoader.createZoomImage(zoomedImg, zoomImageSource, store);
processZoom(event);
event.pointerType !== "mouse" && disableScroll();
}
}
function handlePointerMove(event) {
if (checkValidPointer(event)) {
processZoom(event);
}
}
function resetZoomedImg(event) {
if (checkValidPointer(event)) {
container.contains(zoomedImg) && container.removeChild(zoomedImg);
zoomedImg.style.display = "none";
zoomedImg.style.transform = "none";
enableScroll();
activePointerId = null;
}
}
const calculatePositionX = (newPositionX) => {
const width = container.clientWidth;
if (newPositionX > 0)
return 0;
if (newPositionX + width * zoomFactor < width)
return -width * (zoomFactor - 1);
return newPositionX;
};
const calculatePositionY = (newPositionY) => {
const height = container.clientHeight;
if (newPositionY > 0)
return 0;
if (newPositionY + height * zoomFactor < height)
return -height * (zoomFactor - 1);
return newPositionY;
};
function processZoom(event) {
zoomedImg.style.display = "block";
const containerRect = container.getBoundingClientRect();
const zoomPointX = event.clientX - containerRect.left;
const zoomPointY = event.clientY - containerRect.top;
const currentPositionX = calculatePositionX(-zoomPointX * zoomFactor + zoomPointX);
const currentPositionY = calculatePositionY(-zoomPointY * zoomFactor + zoomPointY);
zoomedImg.style.transform = `translate(${currentPositionX}px, ${currentPositionY}px)`;
}
const controller2 = new AbortController();
const { signal: signal2 } = controller2;
container.addEventListener("pointerenter", handlePointerEnter, { signal: signal2 });
container.addEventListener("pointermove", handlePointerMove, { signal: signal2 });
container.addEventListener("pointerleave", resetZoomedImg, { signal: signal2 });
container.addEventListener(
"touchstart",
(event) => {
disabledContextMenu && event.preventDefault();
},
{ signal: signal2 }
);
return {
cleanup: () => {
controller2.abort();
container.contains(zoomedImg) && container.removeChild(zoomedImg);
store.cleanup();
},
subscribe: store.subscribe,
getState: store.getState
};
}
exports.createZoomImageMove = createZoomImageMove;
return exports;
})({});
//# sourceMappingURL=out.js.map
//# sourceMappingURL=createZoomImageMove.global.js.map