Image Viewer Directive
The v-image-view
component is used to display images with optional thumbnails.
Usage
View Code
vue
<script setup>
import { ref } from "vue";
const images = ref([
"https://images.pexels.com/photos/2334005/pexels-photo-2334005.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
"https://images.pexels.com/photos/4895059/pexels-photo-4895059.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
"https://images.pexels.com/photos/4952563/pexels-photo-4952563.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
]);
</script>
<template>
<div class="md:space-x-2 flex flex-col md:flex-row space-y-2 md:space-y-0">
<button
class="px-4 py-2 bg-indigo-600 text-white rounded-xl"
v-image-view.thumbnails="images"
>
With thumbnails
</button>
<button
class="px-4 py-2 bg-indigo-600 text-white rounded-xl"
v-image-view="images"
>
Without thumbnails
</button>
<button
class="px-4 py-2 bg-indigo-600 text-white rounded-xl"
v-image-view.loop="images"
>
Loop Images
</button>
</div>
</template>
<style>
/* ... (other styles remain unchanged) */
</style>
Arguments
The v-image-view
directive does not accept any arguments.
Modifiers
The v-image-view
takes the following modifiers:
Modifier | Description |
---|---|
thumbnails | The thumbnails will be displayed. |
loop | The images will repeat in a loop. |
Source Code
js
const closeIcon = `<svg class='closeIconSvg' xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="m12 13.4l-4.9 4.9q-.275.275-.7.275t-.7-.275q-.275-.275-.275-.7t.275-.7l4.9-4.9l-4.9-4.9q-.275-.275-.275-.7t.275-.7q.275-.275.7-.275t.7.275l4.9 4.9l4.9-4.9q.275-.275.7-.275t.7.275q.275.275.275.7t-.275.7L13.4 12l4.9 4.9q.275.275.275.7t-.275.7q-.275.275-.7.275t-.7-.275z"/></svg>`;
const prevIcon = `<svg class='prevIconSvg' xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M14.71 6.71a.996.996 0 0 0-1.41 0L8.71 11.3a.996.996 0 0 0 0 1.41l4.59 4.59a.996.996 0 1 0 1.41-1.41L10.83 12l3.88-3.88c.39-.39.38-1.03 0-1.41z"/></svg>`;
function createButton(className, icon) {
const button = document.createElement("button");
button.classList.add(className);
button.innerHTML = icon;
return button;
}
export const vImageView = {
mounted(el, binding) {
const images = binding.value;
const thumbnails = binding.modifiers.thumbnails;
const loop = binding.modifiers.loop;
el.addEventListener("click", () => {
const currentIndex = { value: 0 };
let fullscreenElement;
let imgContainer;
createFullscreenElement();
function createFullscreenElement() {
fullscreenElement = document.createElement("div");
fullscreenElement.classList.add("fullscreen");
// Container for images
imgContainer = document.createElement("div");
imgContainer.classList.add("img-container");
fullscreenElement.appendChild(imgContainer);
createImage();
if (thumbnails) {
createThumbnailBar();
}
document.body.appendChild(fullscreenElement);
// Close fullscreen on backdrop click
fullscreenElement.addEventListener("click", (event) => {
if (event.target === fullscreenElement) {
document.body.removeChild(fullscreenElement);
}
});
}
function createImage() {
const img = document.createElement("img");
img.src = images[currentIndex.value];
img.classList.add("fullscreen-image");
imgContainer.appendChild(img);
// Create navigation buttons inside the img-container
createNavigationButtons(img, currentIndex);
}
function createThumbnailBar() {
const thumbnailBar = document.createElement("div");
thumbnailBar.classList.add("thumbnail-bar");
const thumbnailContainer = document.createElement("div");
thumbnailContainer.classList.add("thumbnail-container");
thumbnailBar.appendChild(thumbnailContainer);
images.forEach((image, index) => {
const thumbnail = createThumbnail(image, index);
thumbnailContainer.appendChild(thumbnail);
});
fullscreenElement.appendChild(thumbnailBar);
}
function createThumbnail(image, index) {
const thumbnail = document.createElement("div");
thumbnail.classList.add("thumbnail");
thumbnail.innerHTML = `<img src="${image}" alt="Thumbnail ${index}" />`;
thumbnail.addEventListener("click", () => {
currentIndex.value = index;
updateImageVisibility();
});
return thumbnail;
}
function createNavigationButtons(img, currentIndex) {
// Create close button
const closeButton = createButton("close-button", closeIcon);
closeButton.addEventListener("click", () => {
document.body.removeChild(fullscreenElement);
});
imgContainer.appendChild(closeButton);
// Create navigation buttons
const nextButton = createButton("next-button", prevIcon);
nextButton.addEventListener("click", () => {
currentIndex.value = loop
? (currentIndex.value + 1) % images.length
: Math.min(currentIndex.value + 1, images.length - 1);
updateImageVisibility();
});
imgContainer.appendChild(nextButton);
const prevButton = createButton("prev-button", prevIcon);
prevButton.addEventListener("click", () => {
currentIndex.value = loop
? (currentIndex.value - 1 + images.length) % images.length
: Math.max(currentIndex.value - 1, 0);
updateImageVisibility();
});
imgContainer.appendChild(prevButton);
}
function updateImageVisibility() {
imgContainer.querySelector(".fullscreen-image").src =
images[currentIndex.value];
}
});
},
};