Skip to content

Instantly share code, notes, and snippets.

@winks
Forked from egeozcan/index.html
Created October 18, 2024 14:44
Show Gist options
  • Save winks/5219114bd506f52df637c4aaa8d55eb8 to your computer and use it in GitHub Desktop.
Save winks/5219114bd506f52df637c4aaa8d55eb8 to your computer and use it in GitHub Desktop.
image viewer
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fullscreen Image Viewer</title>
<style>
body {
font-family: Arial, sans-serif;
}
#imageDisplay {
max-width: 80%;
max-height: 78vh;
height: auto;
margin: 20px auto;
border: 5px dashed #ddd;
padding: 10px;
cursor: pointer;
}
.btn {
display: inline-block;
padding: 10px;
background-color: #007bff;
color: white;
cursor: pointer;
margin: 10px;
}
.btn.danger {
background-color: #dc3545;
}
#buttons {
position: sticky;
top: 0;
display: block;
background: white;
user-select: none;
}
#container {
width: 80%;
margin: auto;
text-align: center;
}
#thumbnails {
display: flex;
overflow-x: auto;
padding: 10px;
margin-top: 20px;
justify-content: center;
}
.thumbnail {
margin: 0 5px;
cursor: pointer;
}
.thumbnail img {
width: 100px;
height: auto;
border: 2px solid transparent;
}
.thumbnail img.active {
border-color: blue;
}
[hidden] {
display: none !important;
}
</style>
</head>
<body>
<div id="container">
<div>
<input type="file" id="imageInput" multiple accept="image/*" style="display: none;">
<label for="imageInput" class="btn">Select Images</label>
</div>
<div id="buttons" hidden>
<button class="btn" onclick="previousImage()">Previous</button>
<button class="btn" onclick="nextImage()">Next</button>
<button class="btn danger" onclick="removeCurrentImage()">Remove Current Image</button>
</div>
<img id="imageDisplay" onclick="toggleFullscreen()">
<div id="imageIndex"></div>
<div id="thumbnails"></div>
</div>
<script>
let images = [];
let currentIndex = -1; // Start before the first index
let touchstartX = 0;
let touchendX = 0;
function checkSwipeDirection() {
if (touchstartX - touchendX > 100) nextImage();
if (touchendX - touchstartX > 100) previousImage();
}
document.addEventListener("touchstart", e => {
touchstartX = e.changedTouches[0].screenX;
});
document.addEventListener("touchend", e => {
touchendX = e.changedTouches[0].screenX;
checkSwipeDirection();
});
document.getElementById("imageInput").addEventListener("change", handleFiles);
document.getElementById("container").addEventListener("paste", handlePaste);
document.addEventListener("dragover", preventDefault);
document.addEventListener("drop", handleDrop);
document.addEventListener("keydown", handleKeydown);
function handleFiles(e) {
const files = e.target.files;
for (let i = 0; i < files.length; i++) {
if (files[i].type.startsWith("image/")) {
const fileReader = new FileReader();
fileReader.onload = (e) => {
images.push(e.target.result);
if (i === 0) displayImage(images.length - 1); // Display the last image added
addThumbnail(e.target.result, images.length - 1);
document.getElementById("buttons").removeAttribute("hidden");
};
fileReader.onerror = (e) => console.log(e.target.error);
fileReader.readAsDataURL(files[i]);
}
}
}
function handlePaste(e) {
const files = e.clipboardData.files;
handleFiles({ target: { files } });
}
function preventDefault(e) {
e.preventDefault();
}
function handleDrop(e) {
e.preventDefault();
if (!e.dataTransfer) {
console.log("DataTransfer is null, this drop event cannot be processed.");
return;
}
const files = e.dataTransfer.files;
if (files && files.length > 0) {
// Handle files from the user's computer
handleFiles({ target: { files } });
} else {
// Handle images dragged from other web pages
const htmlContent = e.dataTransfer.getData("text/html");
const match = htmlContent && htmlContent.match(/<img [^>]*src="([^"]+)"[^>]*>/i);
if (match && match[1]) {
images.push(match[1]);
displayImage(images.length - 1); // Display the last image added
addThumbnail(match[1], images.length - 1);
}
}
}
function removeThumbnail(index) {
const thumbnailsContainer = document.getElementById("thumbnails");
thumbnailsContainer.removeChild(thumbnailsContainer.children[index]);
}
function displayImage(index) {
const nothingToDisplay = images.length === 0;
if (nothingToDisplay) {
document.getElementById("buttons").setAttribute("hidden", "");
} else {
document.getElementById("buttons").removeAttribute("hidden");
}
currentIndex = nothingToDisplay ? -1 : index;
document.getElementById("imageDisplay").src = nothingToDisplay ? "" : images[index];
document.getElementById("imageIndex").innerText = nothingToDisplay ? "" : `Image ${index + 1} of ${images.length}`;
updateThumbnailActiveState();
}
function addThumbnail(imageSrc, index) {
const thumbnailWrapper = document.createElement("div");
const thumbnailsContainer = document.getElementById("thumbnails");
thumbnailWrapper.classList.add("thumbnail");
thumbnailWrapper.addEventListener("click", () => displayImage([...thumbnailsContainer.children].indexOf(thumbnailWrapper)));
const img = document.createElement("img");
img.src = imageSrc;
thumbnailWrapper.appendChild(img);
thumbnailsContainer.appendChild(thumbnailWrapper);
updateThumbnailActiveState();
}
function updateThumbnailActiveState() {
const thumbnails = document.querySelectorAll(".thumbnail img");
thumbnails.forEach((img, idx) => {
img.classList.remove("active");
if (idx === currentIndex) {
img.classList.add("active");
}
});
}
function nextImage() {
if (currentIndex === -1) {
return;
}
displayImage((currentIndex + 1) % images.length);
}
function previousImage() {
if (currentIndex === -1) {
return;
}
displayImage((currentIndex - 1 + images.length) % images.length);
}
function handleKeydown(e) {
if (e.key === "ArrowRight") {
nextImage();
} else if (e.key === "ArrowLeft") {
previousImage();
} else if (e.key === "Escape") {
exitFullscreen();
} else if (e.key === "Delete") {
removeCurrentImage();
} else if (e.key === "Enter") {
toggleFullscreen();
}
}
function removeCurrentImage() {
if (currentIndex === -1) {
return;
}
images.splice(currentIndex, 1);
removeThumbnail(currentIndex);
displayImage(currentIndex % images.length);
}
function toggleFullscreen() {
const imgDisplay = document.getElementById("imageDisplay");
if (!document.fullscreenElement) {
if (currentIndex === -1) {
return;
}
imgDisplay.requestFullscreen().catch(err => console.log(err));
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
}
}
}
function exitFullscreen() {
if (document.fullscreenElement) {
document.exitFullscreen();
}
}
// load existing images when the page loads from the leftover thumbnails
document.addEventListener("DOMContentLoaded", () => {
let activeIndex = 0;
const thumbnails = document.querySelectorAll(".thumbnail img");
thumbnails.forEach((img, idx) => {
images.push(img.src);
img.addEventListener("click", () => displayImage(idx));
if (img.classList.contains("active")) {
activeIndex = idx;
}
});
displayImage(activeIndex);
});
// change the image when the user uses scroll wheel
document.getElementById("imageDisplay").addEventListener("wheel", e => {
e.preventDefault();
if (e.deltaY > 0) {
nextImage();
} else {
previousImage();
}
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment