I am making an Image Viewer for my website for when on zoom and when on drag, it will stops at the edge of the window (will not pull more further) and all I tried wasn’t working as expected. This is the current I have
HTML
<div id="imageModal">
<div id="blackout"></div>
<span class="close"></span>
<div id="modalImage">
<div id="containerLimit">
<img src="">
</div>
</div>
</div>
CSS
/* Modal blackout background */
#imageModal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s ease, visibility 0.2s ease;
z-index: 240009;
}
#blackout {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #000000c0;
opacity: 0;
transition: opacity 0.2s ease;
}
.imgauto:hover, .imgautofull:hover {
cursor: pointer;
}
/* When the modal is active */
#imageModal.show #blackout {
opacity: 1;
}
#imageModal.show {
opacity: 1;
visibility: visible;
}
/* Modal content (image box) */
.modal-content {
position: relative;
border-radius: 10px;
max-width: 90%;
max-height: 90%;
display: flex;
justify-content: center;
align-items: center;
}
/* The full-size image */
#modalImage {
position: fixed;
z-index: 240010;
transform: scale(0.9);
transition: 0.2s ease;
margin: 50px;
display: flex;
align-items: center;
justify-content: center;
}
#modalImage img {
position: relative;
max-width: 80%;
max-height: 80vh;
transition: 0.2s ease;
border-radius: 0px;
}
#imageModal.show #modalImage {
transform: scale(1);
}
/* Close button */
.close {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
color: white;
z-index: 1;
}
.img-zoom {
cursor: grab;
transform: scale(2);
/*width: auto;*/
/*height: auto;*/
/*max-width: none;*/
}
.img-zoom:active {
cursor: grabbing;
}
#modalImage, #containerLimit {
position: fixed;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
}
#containerLimit img {
pointer-events: auto;
}
JS
// imgauto
document.addEventListener("DOMContentLoaded", function () {
const modal = document.getElementById("imageModal");
const modalImg = document.getElementById("modalImage");
const inImg = modalImg.querySelector("img");
const closeBtn = document.querySelector(".close");
// Select all images with the class 'imageToView'
const imageContainers = document.querySelectorAll(".imgauto, .imgautofull");
imageContainers.forEach(container => {
const img = container.querySelector("img"); // Get the image inside each div
if (!img) return; // Skip if no image inside the div
container.addEventListener("click", function () {
modal.style.display = "flex";
setTimeout(() => modal.classList.add("show"), 10);
inImg.src = img.src;
document.body.classList.add("modal-open");
});
});
closeBtn.addEventListener("click", function () {
closeModal();
});
modal.addEventListener("click", function (e) {
if (e.target === modal) {
closeModal();
}
});
let isDragging = false;
let zoomed = false;
function closeModal() {
modal.classList.remove("show");
inImg.classList.remove('img-zoom');
disableDrag("#modalImage img");
isDragging = false;
zoomed = false;
setTimeout(() => {
modal.style.display = "none";
document.body.classList.remove("modal-open");
}, 300); // Match the transition duration
}
// drag on zoom
let sX, sY;
const dragFound = 5; // pixels before it's considered drag
function updateContainer() {
const ww = window.innerWidth;
const wh = window.innerHeight;
console.log("-----------------------------------------");
console.log(`WINDOW: width: ${ww} height: ${wh}`);
// inImg.addEventListener("transitionend", updateContainer, { once: true });
// const rect = inImg.getBoundingClientRect();
// const bw = rect.width;
// const bh = rect.height;
// console.log(`IMAGE: width: ${bw} height: ${bh}`);
modalImg.style.width = ww + 'px';
modalImg.style.height = wh + 'px';
console.log(`MASK: width: ${ww} height: ${wh}`);
const cont = document.getElementById("containerLimit");
cont.style.width = ww + "px";
cont.style.height = wh + "px";
}
imageContainers.forEach(container => {
const img = container.querySelector("img"); // Get the image inside each div
if (!img) return; // Skip if no image inside the div
container.addEventListener("click", function () {
updateContainer();
});
});
// function enableDrag() {
// $(inImg).draggable({
// // containment: "#containerLimit",
// // scroll: false,
// // drag: function(event, ui) {
// // if (ui.position.top > 0) {
// // ui.position.top = 0;
// // }
// // var maxtop = ui.helper.parent().height() - ui.helper.height();
// // if ( ui.position.top < maxtop) {
// // ui.position.top = maxtop;
// // }
// // if ( ui.position.left > 0) {
// // ui.position.left = 0;
// // }
// // var maxleft = ui.helper.parent().width() - ui.helper.width();
// // if ( ui.position.left < maxleft) {
// // ui.position.left = maxleft;
// // }
// // }
// containment: "window"
// });
// }
function enableDrag(selector, maskSelector) {
let $img = $(selector);
let $mask = $(maskSelector);
if ($img.length === 0 || $mask.length === 0) {
console.error("Error: One or both of the elements were not found.");
return;
}
console.log("FUNCTION: [jQuery draggable] USE");
$img.css({top:0, left:0});
// disableDrag(selector); // prevent duplicates
// Cache dimensions to improve performance during drag
let maskWidth = $mask.width();
let maskHeight = $mask.height();
// let imgPos = $img.offset();
let imgWidth = $img.width();
let imgHeight = $img.height();
// console.log("mw, mh: ", maskWidth, maskHeight);
// console.log("ip: ", imgPos);
// console.log("iw, ih: ", imgWidth, imgHeight);
// Calculate bounds
let minLeft = maskWidth - imgWidth;
let maxLeft = 0;
let minTop = maskHeight - imgHeight;
let maxTop = 0;
// let x1 = (imgPos.left + maskWidth) - imgWidth;
// let y1 = (imgPos.top + maskHeight) - imgHeight;
// let x2 = imgPos.left;
// let y2 = imgPos.top;
// console.log("x1, y1: ", x1, y1);
// console.log("x2, y2: ", x2, y2);
$img.draggable({
drag: function(event, ui) {
// Constrain the position
ui.position.left = Math.min(maxLeft, Math.max(minLeft, ui.position.left));
ui.position.top = Math.min(maxTop, Math.max(minTop, ui.position.top));
}
});
// $img.draggable({
// containment: [x1, y1, x2, y2]
// });
}
function disableDrag(selector = "#inImg") {
let $img = $(selector);
if ($img.draggable("instance")) {
console.log("FUNCTION: [jQuery draggable] DESTROY");
$img.draggable("destroy");
} else {
console.log("FUNCTION: [jQuery draggable] No draggable instance found for", selector);
}
}
// Mouse events
modalImg.addEventListener("mousedown", function (e) {
sX = e.clientX;
sY = e.clientY;
isDragging = false;
});
modalImg.addEventListener("mousemove", function (e) {
if (e.buttons === 1) {
const currentX = e.clientX;
const currentY = e.clientY;
const distance = Math.sqrt(
Math.pow(currentX - sX, 2) + Math.pow(currentY - sY, 2)
);
if (distance > dragFound) {
isDragging = true;
enableDrag("#modalImage img", "#containerLimit");
console.log("MOUSE EVENT: Dragging");
}
}
});
modalImg.addEventListener("mouseup", function () {
if (!isDragging) {
// Toggle zoom on click
zoomed = !zoomed;
if (zoomed) {
inImg.classList.add("img-zoom");
// Enable draggable when zoomed
enableDrag("#modalImage img", "#containerLimit");
} else {
inImg.classList.remove("img-zoom");
// Disable draggable when zoomed out
disableDrag("#modalImage img");
// Reset to original position
inImg.style.left = "0px";
inImg.style.top = "0px";
inImg.style.transform = ""; // optional: clear inline transforms
}
requestAnimationFrame(() => updateContainer());
} else {
// Drag release – keep zoomed in
if (!zoomed) {
zoomed = true;
inImg.classList.add("img-zoom");
enableDrag("#modalImage img", "#containerLimit");
requestAnimationFrame(() => updateContainer());
}
}
isDragging = false;
});
// Update container size when window resizes
window.addEventListener("resize", () => {
updateContainer();
});
});
NOTE: I have made a comment on some of the code in JS because I might re-use it in some cases.
The current code I have is that when on zoom and when on drag, it will enable draggable. But the result is the draggable element stays still as it is.
And from my custom code, I’ve made a JSFiddle one that works fine (pre-scaled) is that I get the width and height from the window and the image, then get the overflowing values, then set the container to width = window width + overflow width * 2, height = window height + overflow height * 2 which it does work. But the problem is the .draggable() still uses the original size of the image resulting the image go past the containment
JSFiddle simulation
I’ve tried the stack overflow post (Asked on 2009) where they were creating an interface to allow a user to zoom in on an image and drag the zoomed version around within a clipping mask div.
From post
But when I applied the code to my very code, it gave me a reverse drag. Is there any way to make this possible?