Hammer.js
Hammer.js

Amazing Hammer.js Project: Build a Super Fun Image Carousel with Touch, Zoom & Pan!33 min read

  Reading time 48 minutes

Hey there, future web wizard! Have you ever scrolled through images on your phone or tablet and wished you could do more than just swipe? Like, what if you could zoom in on a tiny detail, or drag the image around to see every corner? Well, guess what? You absolutely can, right in your web browser! And the best part? It’s not as hard as you might think.

Today, we’re going to embark on an amazing project: building an image carousel that you can control with your fingers (or your mouse, if you’re on a computer!) using a cool JavaScript library called Hammer.js. We’ll learn how to add exciting features like touch-swiping to move between images, pinching to zoom in and out, and even dragging the zoomed image around. This is going to be a super fun way to make your websites truly interactive in 2025!

Before we dive into the exciting world of code, let’s quickly understand what we’re building.

An image carousel is like a slideshow of pictures on a website. Instead of just seeing one picture, you can click buttons or swipe to see the next one. Think of it like flipping through a photo album, but on your screen!

Now, when we add Hammer.js to the mix, we make this carousel super interactive. Hammer.js is a fantastic little JavaScript library that helps us understand “touch gestures.” These are the things you do with your fingers on a touchscreen, like:

  • Swiping: Sliding your finger across the screen to move to the next image.
  • Pinching: Using two fingers to zoom in or out on an image.
  • Panning (or dragging): Moving a zoomed-in image around with one finger to see different parts of it.

So, our Hammer.js image carousel will be a photo album that you can control with simple touch gestures, making it feel very natural and easy to use, just like your favorite apps!

You might be thinking, “Can’t I do this with just plain JavaScript?” And the answer is, “Yes, you can!” But it would be much, much harder. Here’s why Hammer.js is our superhero for this image carousel project:

  • Simplicity: Hammer.js makes it super easy to detect complex touch gestures. Without it, you’d have to write a lot of complicated code to figure out if someone is swiping, pinching, or panning. Hammer.js handles all that messy stuff for us!
  • Cross-Browser Compatibility: It works well across different web browsers and devices, so your image carousel will look and feel great whether someone is using Chrome on an Android phone or Safari on an iPhone.
  • Built for Touch: It’s specifically designed for touch interactions, making it perfect for creating mobile-friendly and intuitive web experiences.

Every great project starts with a good foundation. For our Hammer.js image carousel, we’ll need three main parts:

  1. HTML (the skeleton): This is where we’ll set up the basic layout for our carousel and the places where our images will go.
  2. CSS (the style): This will make our carousel look nice and organized.
  3. JavaScript (the brain): This is where all the magic happens! We’ll use JavaScript, along with Hammer.js, to make our carousel interactive.

First, let’s create our index.html file. This file will contain the basic structure of our web page. Don’t worry about understanding every single line right now; we’ll break it down.

HTML

<!DOCTYPE <strong>html</strong>>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hammer.js Image Carousel with Controlled Dragging</title>
    <script src="https://cdn.jsdelivr.net/npm/hammerjs@2.0.8/hammer.min.js"></script>
    <style>
        /* Our CSS will go here */
        body {
            margin: 0;
            font-family: Arial, sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            background-color: #f4f4f9;
        }

        .carousel {
            width: 80%;
            max-width: 600px;
            overflow: hidden;
            position: relative;
            border: 2px solid #ddd;
            border-radius: 8px;
            background: #fff;
        }

        .slides {
            display: flex;
            transition: transform 0.5s ease-in-out;
        }

        .slide {
            min-width: 100%;
            height: 300px;
            background-size: contain;
            background-repeat: no-repeat;
            background-position: center;
        }

        .buttons {
            position: absolute;
            top: 50%;
            width: 100%;
            display: flex;
            justify-content: space-between;
            transform: translateY(-50%);
        }

        .button {
            background-color: rgba(0, 0, 0, 0.5);
            color: white;
            border: none;
            padding: 10px 15px;
            cursor: pointer;
            border-radius: 50%;
        }

        .button:hover {
            background-color: rgba(0, 0, 0, 0.8);
        }

        #upload-container {
            margin-bottom: 20px;
        }

        #zoom-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.8);
            display: none; /* Hidden by default */
            justify-content: center;
            align-items: center;
            z-index: 10;
            overflow: hidden;
            cursor: grab;
        }

        #zoom-overlay img {
            transform-origin: center;
            transition: transform 0.3s ease;
            cursor: grab;
        }

        #zoom-overlay img:active {
            cursor: grabbing;
        }
    </style>
</head>

<body>
    <div id="upload-container">
        <input type="file" id="imageUploader" multiple accept="image/*">
        <button id="loadImages">Load Images</button>
    </div>

    <div class="carousel" id="carousel">
        <div class="slides" id="slides">
            </div>
        <div class="buttons">
            <button class="button" id="prev">‹</button>
            <button class="button" id="next">›</button>
        </div>
    </div>

    <div id="zoom-overlay">
        <img id="zoom-image" src="" alt="Zoomed Image">
    </div>

    <script>
        // All our JavaScript code for the Hammer.js image carousel will be here
    </script>
</body>

</html>

Let’s break down this HTML for our Hammer.js image carousel:

  • <head> section:
    • <!DOCTYPE html> and <html lang="en">: These are standard beginnings for any HTML page.
    • <meta charset="UTF-8">: Tells the browser how to read characters.
    • <meta name="viewport" content="width=device-width, initial-scale=1.0">: Super important for making your website look good on phones and tablets! It tells the browser to set the width of the page to the device’s screen width.
    • <title>Hammer.js Image Carousel with Controlled Dragging</title>: This is what appears in your browser tab.
    • <script src="https://cdn.jsdelivr.net/npm/hammerjs@2.0.8/hammer.min.js"></script>: This is crucial! This line brings the Hammer.js library into our project. We’re getting it from a CDN (Content Delivery Network), which is like a super-fast server that hosts common libraries.
    • <style> tags: This is where we’ll put all our CSS code. It’s inside the HTML file for now to keep things simple, but in bigger projects, you’d put this in a separate .css file.
  • <body> section:
    • <div id="upload-container">: This is where we’ll have a button to upload our own images, making our image carousel truly customizable.
      • <input type="file" id="imageUploader" multiple accept="image/*">: This is a special input that lets you pick files. multiple means you can pick more than one, and accept="image/*" means it will only show image files.
      • <button id="loadImages">Load Images</button>: A button to trigger loading the selected images.
    • <div class="carousel" id="carousel">: This is the main container for our image carousel.
      • <div class="slides" id="slides">: This div will hold all our individual images. We’ll add them here using JavaScript.
      • <div class="buttons">: This div holds the “previous” and “next” buttons for our image carousel.
        • <button class="button" id="prev">‹</button> and <button class="button" id="next">›</button>: Our navigation buttons.
    • <div id="zoom-overlay">: This is a special “overlay” that will pop up when we want to zoom in on an image. It’s hidden (display: none;) by default.
      • <img id="zoom-image" src="" alt="Zoomed Image">: This is where the zoomed-in image will be displayed.
    • <script> tags: This is where we’ll write all our JavaScript code. This code will make our HTML elements move, respond to clicks and touches, and bring our Hammer.js image carousel to life!

Our HTML is just the bare bones. Now, let’s add some style to make our Hammer.js image carousel look good! The CSS code is already inside the <style> tags in the HTML from the previous step.

Let’s quickly go over what the CSS does for our Hammer.js image carousel:

  • body: Centers everything on the page and sets a nice background color.
  • .carousel: This is the main box for our carousel. It sets its size, adds a border, and makes sure anything that goes outside its boundaries is hidden (overflow: hidden).
  • .slides: This is the container for all our images. display: flex makes them line up next to each other. transition: transform 0.5s ease-in-out; is important because it makes the slide movement smooth and not jumpy.
  • .slide: Each individual image in the carousel. min-width: 100% ensures each slide takes up the full width of the carousel. background-size: contain makes sure the whole image fits without cropping, and background-position: center centers the image.
  • .buttons: Positions the navigation buttons on the left and right of the carousel.
  • .button: Styles for the next/previous buttons – makes them round and semi-transparent.
  • #upload-container: Adds some space below the upload elements.
  • #zoom-overlay: This is the dark background that appears when you zoom in. It covers the whole screen (position: fixed, width: 100%, height: 100%). display: none keeps it hidden until we need it.
  • #zoom-overlay img: Styles the image inside the zoom overlay. transform-origin: center is key for zooming, making the image zoom from its middle. transition: transform 0.3s ease; makes the zoom animation smooth.
  • #zoom-overlay img:active: Changes the cursor when you’re dragging the zoomed image.

This is where the real fun begins! We’ll write JavaScript code to handle the image loading, carousel navigation, and, of course, the awesome touch gestures with Hammer.js for our image carousel.

The JavaScript code will go inside the <script> tags at the very bottom of your <body> section.

JavaScript

const slides = document.getElementById('slides');
const prevButton = document.getElementById('prev');
const nextButton = document.getElementById('next');
const imageUploader = document.getElementById('imageUploader');
const loadImagesButton = document.getElementById('loadImages');
const zoomOverlay = document.getElementById('zoom-overlay');
const zoomImage = document.getElementById('zoom-image');

let totalSlides = 0;
let currentIndex = 0;

// Variables for zoom and controlled drag
let scale = 1;
let posX = 0;
let posY = 0;
let startX = 0;
let startY = 0;
let isDragging = false;

// Function to update the slide position
function updateSlidePosition() {
    slides.style.transform = `translateX(-${currentIndex * 100}%)`;
}

// Navigate to the previous slide
function goToPreviousSlide() {
    if (currentIndex > 0) {
        currentIndex--;
    } else {
        currentIndex = totalSlides - 1; // Wrap around to the last slide
    }
    updateSlidePosition();
}

// Navigate to the next slide
function goToNextSlide() {
    if (currentIndex < totalSlides - 1) {
        currentIndex++;
    } else {
        currentIndex = 0; // Wrap around to the first slide
    }
    updateSlidePosition();
}

// Load images into the carousel
function loadImages() {
    const files = imageUploader.files;
    if (files.length === 0) {
        alert('Please select some images.');
        return;
    }

    slides.innerHTML = ''; // Clear existing slides
    totalSlides = files.length;

    Array.from(files).forEach(file => {
        const reader = new FileReader();
        reader.onload = function (e) {
            const slide = document.createElement('div');
            slide.classList.add('slide');
            slide.style.backgroundImage = `url('${e.target.result}')`;
            slide.addEventListener('click', () => zoomImagePreview(e.target.result));
            slides.appendChild(slide);
        };
        reader.readAsDataURL(file);
    });

    currentIndex = 0;
    updateSlidePosition();
}

// Show the zoom overlay
function zoomImagePreview(src) {
    zoomImage.src = src;
    zoomOverlay.style.display = 'flex'; // Show the overlay
    scale = 1; // Reset zoom
    posX = 0;  // Reset position
    posY = 0;
    updateZoom();
}

// Hide the zoom overlay
zoomOverlay.addEventListener('click', (e) => {
    if (e.target === zoomOverlay) { // Only close if clicking on the background, not the image
        zoomOverlay.style.display = 'none';
    }
});

// Update zoom and position for the zoomed image
function updateZoom() {
    zoomImage.style.transform = `translate(${posX}px, ${posY}px) scale(${scale})`;
}

// Handle mouse wheel for zooming
zoomOverlay.addEventListener('wheel', (e) => {
    e.preventDefault(); // Stop the page from scrolling
    scale += e.deltaY * -0.001; // Adjust zoom sensitivity
    scale = Math.max(scale, 0.1); // Prevent negative or zero scale
    updateZoom();
});

// Handle mouse drag to move the image (panning)
zoomImage.addEventListener('mousedown', (e) => {
    isDragging = true;
    startX = e.clientX - posX; // Calculate where the mouse clicked relative to the image's current position
    startY = e.clientY - posY;
});

zoomOverlay.addEventListener('mousemove', (e) => {
    if (!isDragging) return; // Only drag if the mouse button is down
    posX = e.clientX - startX; // Calculate new position based on mouse movement
    posY = e.clientY - startY;
    updateZoom(); // Update the image's position as you move the mouse
});

zoomOverlay.addEventListener('mouseup', () => {
    isDragging = false; // Stop dragging once mouse is released
});

zoomOverlay.addEventListener('mouseleave', () => {
    isDragging = false; // Stop dragging if mouse leaves the zoom area
});

// Event listeners for our Hammer.js image carousel buttons and file input
prevButton.addEventListener('click', goToPreviousSlide);
nextButton.addEventListener('click', goToNextSlide);
loadImagesButton.addEventListener('click', loadImages);

// Set up Hammer.js for swipe gestures on the main carousel
const carousel = document.getElementById('carousel');
const hammer = new Hammer(carousel); // Tell Hammer.js to listen to our carousel

// Detect swipe gestures for the Hammer.js image carousel
hammer.on('swipeleft', goToNextSlide);  // When swiped left, go to the next slide
hammer.on('swiperight', goToPreviousSlide); // When swiped right, go to the previous slide

// Set up Hammer.js for pinch and pan gestures on the zoom overlay image
const hammerZoom = new Hammer(zoomImage); // Tell Hammer.js to listen to our zoomed image

// Enable pinch and pan recognizers
hammerZoom.get('pinch').set({ enable: true });
hammerZoom.get('pan').set({ direction: Hammer.DIRECTION_ALL, enable: true });

let initialScale = 1; // To store the scale at the start of a pinch gesture
let initialPosX = 0; // To store the X position at the start of a pan gesture
let initialPosY = 0; // To store the Y position at the start of a pan gesture

hammerZoom.on('pinchstart', (e) => {
    initialScale = scale; // Remember the current scale
});

hammerZoom.on('pinchmove', (e) => {
    scale = initialScale * e.scale; // Update scale based on pinch movement
    updateZoom();
});

hammerZoom.on('panstart', (e) => {
    initialPosX = posX; // Remember the current X position
    initialPosY = posY; // Remember the current Y position
});

hammerZoom.on('panmove', (e) => {
    posX = initialPosX + e.deltaX; // Update X position based on pan movement
    posY = initialPosY + e.deltaY; // Update Y position based on pan movement
    updateZoom();
});

Let’s break down this JavaScript code for our Hammer.js image carousel step-by-step:

1. Getting Our Tools Ready (Variables and DOM Elements)

First, we grab all the important pieces from our HTML using document.getElementById(). These are like our handles to control different parts of the page.

  • slides, prevButton, nextButton, etc.: These are direct links to our HTML elements.
  • totalSlides, currentIndex: These are simple numbers to keep track of how many images we have and which one we’re currently looking at in our image carousel.
  • scale, posX, posY, startX, startY, isDragging: These variables are specifically for our zoom and pan feature. They help us remember how much we’ve zoomed, where the image is on the screen, and if we’re currently dragging it.
  • updateSlidePosition(): This is a core function for our image carousel. It uses a CSS trick (transform: translateX()) to shift the slides container left or right, showing a different image.
  • goToPreviousSlide() and goToNextSlide(): These functions simply change the currentIndex and then call updateSlidePosition() to show the correct image. They also make sure we “wrap around” to the beginning or end if we go past the last or first image.
  • loadImages(): This function is triggered when you click the “Load Images” button.
    • It checks if you’ve selected any files.
    • slides.innerHTML = '';: This clears out any old images in the carousel.
    • It then loops through each selected image file.
    • FileReader(): This is a special tool in JavaScript that lets us read the contents of files on your computer (like images).
    • reader.onload = function (e) { ... }: When the reader finishes reading an image, this function runs. It creates a new div for the slide, sets its background image to the loaded image, and adds it to our slides container.
    • slide.addEventListener('click', () => zoomImagePreview(e.target.result));: This is super important! It adds a click listener to each image in the carousel. When you click an image, it will call our zoomImagePreview function to open it in the zoom view.

4. The Amazing Zoom & Pan Feature

This is where things get really cool and where we add more interactivity to our Hammer.js image carousel!

  • zoomImagePreview(src): This function is called when you click an image in the carousel.
    • It sets the src of the zoomImage element to the clicked image.
    • zoomOverlay.style.display = 'flex';: This makes the hidden zoom overlay appear!
    • It resets scale, posX, and posY so that each new image starts at its original size and position in the zoom view.
  • Hiding the Zoom Overlay:
    • zoomOverlay.addEventListener('click', (e) => { ... });: This listens for clicks on the zoom overlay. If you click on the dark background around the image (not the image itself), it will hide the overlay.
  • updateZoom(): This function is very similar to updateSlidePosition(), but it applies the scale, posX, and posY values to the zoomImage. This makes the image zoom in/out and move around.
  • Mouse Wheel Zooming:
    • zoomOverlay.addEventListener('wheel', (e) => { ... });: This listens for your mouse scroll wheel. When you scroll, it changes the scale value and then calls updateZoom() to make the image bigger or smaller. e.preventDefault() stops the whole page from scrolling when you’re trying to zoom.
  • Mouse Dragging (Panning):
    • zoomImage.addEventListener('mousedown', ...): When you press your mouse button down on the zoomImage, we set isDragging to true and remember where you clicked (startX, startY).
    • zoomOverlay.addEventListener('mousemove', ...): As long as isDragging is true (meaning you’re holding the mouse button down and moving), we update posX and posY based on how much you’ve moved the mouse. Then, updateZoom() moves the image.
    • zoomOverlay.addEventListener('mouseup', ...) and zoomOverlay.addEventListener('mouseleave', ...): These set isDragging to false when you let go of the mouse button or move your mouse outside the zoom area, stopping the dragging.

5. Connecting Everything (Event Listeners)

  • prevButton.addEventListener('click', goToPreviousSlide);: When the “previous” button is clicked, our goToPreviousSlide function runs.
  • nextButton.addEventListener('click', goToNextSlide);: Same for the “next” button.
  • loadImagesButton.addEventListener('click', loadImages);: When the “Load Images” button is clicked, our loadImages function runs.

Finally, the star of the show for touch gestures!

  • For the Carousel Swiping:
    • const carousel = document.getElementById('carousel');: We get a reference to our main carousel container.
    • const hammer = new Hammer(carousel);: This line is where we “activate” Hammer.js on our carousel element. It tells Hammer.js to start listening for touch gestures on this specific part of our page.
    • hammer.on('swipeleft', goToNextSlide);: This is the magic! It says: “Hey Hammer.js, when you detect a ‘swipeleft’ gesture on the carousel, please run the goToNextSlide function.”
    • hammer.on('swiperight', goToPreviousSlide);: Similarly, for a right swipe, go to the previous slide.
  • For the Zoom Overlay Pinch and Pan:
    • const hammerZoom = new Hammer(zoomImage);: We create another Hammer.js instance, but this time, it listens to our zoomImage inside the overlay.
    • hammerZoom.get('pinch').set({ enable: true });: By default, some gestures in Hammer.js might be turned off. We explicitly enable the pinch gesture for our zoom image.
    • hammerZoom.get('pan').set({ direction: Hammer.DIRECTION_ALL, enable: true });: We also enable the pan gesture, allowing dragging in all directions.
    • hammerZoom.on('pinchstart', ...) and hammerZoom.on('pinchmove', ...): These handle the pinch-to-zoom. pinchstart remembers the current zoom level, and pinchmove calculates the new zoom level based on how much you’re pinching.
    • hammerZoom.on('panstart', ...) and hammerZoom.on('panmove', ...): These handle the pan (dragging) when zoomed in. panstart remembers the image’s current position, and panmove updates it as you drag your finger.

Now that you understand each piece, here’s the complete HTML and JavaScript code for your amazing Hammer.js Image Carousel with touch, zoom, and pan capabilities!

HTML

<!DOCTYPE <strong>html</strong>>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Amazing Hammer.js Project: Image Carousel with Touch, Zoom & Pan</title>
    <script src="https://cdn.jsdelivr.net/npm/hammerjs@2.0.8/hammer.min.js"></script>
    <style>
        body {
            margin: 0;
            font-family: Arial, sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            background-color: #f4f4f9;
            overflow: hidden; /* Prevent body scroll when zoom overlay is active */
        }

        .carousel {
            width: 80%;
            max-width: 600px;
            overflow: hidden;
            position: relative;
            border: 2px solid #ddd;
            border-radius: 8px;
            background: #fff;
        }

        .slides {
            display: flex;
            transition: transform 0.5s ease-in-out;
        }

        .slide {
            min-width: 100%;
            height: 300px;
            background-size: contain;
            background-repeat: no-repeat;
            background-position: center;
            cursor: pointer; /* Indicate that slides are clickable */
        }

        .buttons {
            position: absolute;
            top: 50%;
            width: 100%;
            display: flex;
            justify-content: space-between;
            transform: translateY(-50%);
            z-index: 1; /* Ensure buttons are above slides */
        }

        .button {
            background-color: rgba(0, 0, 0, 0.5);
            color: white;
            border: none;
            padding: 10px 15px;
            cursor: pointer;
            border-radius: 50%;
            font-size: 1.5em;
            display: flex;
            align-items: center;
            justify-content: center;
            width: 40px;
            height: 40px;
            margin: 0 10px;
        }

        .button:hover {
            background-color: rgba(0, 0, 0, 0.8);
        }

        #upload-container {
            margin-bottom: 20px;
            display: flex;
            gap: 10px;
            align-items: center;
        }

        #upload-container input[type="file"] {
            padding: 8px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }

        #upload-container button {
            background-color: #007bff;
            color: white;
            border: none;
            padding: 10px 15px;
            border-radius: 5px;
            cursor: pointer;
            font-size: 1em;
        }

        #upload-container button:hover {
            background-color: #0056b3;
        }

        #zoom-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.8);
            display: none; /* Hidden by default */
            justify-content: center;
            align-items: center;
            z-index: 10;
            overflow: hidden;
            cursor: grab;
        }

        #zoom-overlay img {
            max-width: 90%;
            max-height: 90%;
            object-fit: contain; /* Ensure image fits within overlay */
            transform-origin: center;
            transition: transform 0.3s ease; /* Smooth transition for zoom */
            cursor: grab;
        }

        #zoom-overlay img:active {
            cursor: grabbing;
        }
    </style>
</head>

<body>
    <div id="upload-container">
        <input type="file" id="imageUploader" multiple accept="image/*">
        <button id="loadImages">Load Images</button>
    </div>

    <div class="carousel" id="carousel">
        <div class="slides" id="slides">
            </div>
        <div class="buttons">
            <button class="button" id="prev">‹</button>
            <button class="button" id="next">›</button>
        </div>
    </div>

    <div id="zoom-overlay">
        <img id="zoom-image" src="" alt="Zoomed Image">
    </div>

    <script>
        const slides = document.getElementById('slides');
        const prevButton = document.getElementById('prev');
        const nextButton = document.getElementById('next');
        const imageUploader = document.getElementById('imageUploader');
        const loadImagesButton = document.getElementById('loadImages');
        const zoomOverlay = document.getElementById('zoom-overlay');
        const zoomImage = document.getElementById('zoom-image');

        let totalSlides = 0;
        let currentIndex = 0;

        // Variables for zoom and controlled drag
        let scale = 1;
        let posX = 0;
        let posY = 0;
        let startX = 0;
        let startY = 0;
        let isDragging = false;

        // Function to update the slide position for the Hammer.js image carousel
        function updateSlidePosition() {
            slides.style.transform = `translateX(-${currentIndex * 100}%)`;
        }

        // Navigate to the previous slide in the Hammer.js image carousel
        function goToPreviousSlide() {
            if (totalSlides === 0) return; // No slides to navigate
            if (currentIndex > 0) {
                currentIndex--;
            } else {
                currentIndex = totalSlides - 1; // Wrap around to the last slide
            }
            updateSlidePosition();
        }

        // Navigate to the next slide in the Hammer.js image carousel
        function goToNextSlide() {
            if (totalSlides === 0) return; // No slides to navigate
            if (currentIndex < totalSlides - 1) {
                currentIndex++;
            } else {
                currentIndex = 0; // Wrap around to the first slide
            }
            updateSlidePosition();
        }

        // Load images into the Hammer.js image carousel
        function loadImages() {
            const files = imageUploader.files;
            if (files.length === 0) {
                alert('Please select some images.');
                return;
            }

            slides.innerHTML = ''; // Clear existing slides
            totalSlides = files.length;

            Array.from(files).forEach(file => {
                const reader = new FileReader();
                reader.onload = function (e) {
                    const slide = document.createElement('div');
                    slide.classList.add('slide');
                    slide.style.backgroundImage = `url('${e.target.result}')`;
                    slide.addEventListener('click', () => zoomImagePreview(e.target.result));
                    slides.appendChild(slide);
                };
                reader.readAsDataURL(file);
            });

            currentIndex = 0;
            updateSlidePosition();
        }

        // Show the zoom overlay for the Hammer.js image carousel's zoom feature
        function zoomImagePreview(src) {
            zoomImage.src = src;
            zoomOverlay.style.display = 'flex';
            scale = 1;
            posX = 0;
            posY = 0;
            updateZoom();
        }

        // Hide the zoom overlay
        zoomOverlay.addEventListener('click', (e) => {
            if (e.target === zoomOverlay) {
                zoomOverlay.style.display = 'none';
            }
        });

        // Update zoom and position for the zoomed image
        function updateZoom() {
            zoomImage.style.transform = `translate(${posX}px, ${posY}px) scale(${scale})`;
        }

        // Handle mouse wheel for zooming
        zoomOverlay.addEventListener('wheel', (e) => {
            e.preventDefault();
            scale += e.deltaY * -0.001; // Adjust zoom sensitivity
            scale = Math.max(scale, 0.1); // Prevent negative or zero scale
            updateZoom();
        });

        // Handle mouse drag to move the image (without continuous panning)
        zoomImage.addEventListener('mousedown', (e) => {
            isDragging = true;
            startX = e.clientX - posX;
            startY = e.clientY - posY;
            zoomImage.style.cursor = 'grabbing'; // Change cursor to grabbing
        });

        zoomOverlay.addEventListener('mousemove', (e) => {
            if (!isDragging) return;
            posX = e.clientX - startX;
            posY = e.clientY - startY;
            updateZoom();
        });

        zoomOverlay.addEventListener('mouseup', () => {
            isDragging = false;
            zoomImage.style.cursor = 'grab'; // Change cursor back to grab
        });

        zoomOverlay.addEventListener('mouseleave', () => {
            isDragging = false;
            zoomImage.style.cursor = 'grab'; // Change cursor back if mouse leaves
        });

        // Event listeners for our Hammer.js image carousel
        prevButton.addEventListener('click', goToPreviousSlide);
        nextButton.addEventListener('click', goToNextSlide);
        loadImagesButton.addEventListener('click', loadImages);

        // Set up Hammer.js for swipe gestures on the main carousel
        const carousel = document.getElementById('carousel');
        const hammer = new Hammer(carousel);

        // Detect swipe gestures for the Hammer.js image carousel
        hammer.on('swipeleft', goToNextSlide);
        hammer.on('swiperight', goToPreviousSlide);

        // Set up Hammer.js for pinch and pan gestures on the zoom overlay image
        const hammerZoom = new Hammer(zoomImage);

        // Enable pinch and pan recognizers
        hammerZoom.get('pinch').set({ enable: true });
        hammerZoom.get('pan').set({ direction: Hammer.DIRECTION_ALL, enable: true });

        let initialScale = 1;
        let initialPosX = 0;
        let initialPosY = 0;

        hammerZoom.on('pinchstart', (e) => {
            initialScale = scale;
        });

        hammerZoom.on('pinchmove', (e) => {
            scale = initialScale * e.scale;
            updateZoom();
        });

        hammerZoom.on('panstart', (e) => {
            initialPosX = posX;
            initialPosY = posY;
        });

        hammerZoom.on('panmove', (e) => {
            posX = initialPosX + e.deltaX;
            posY = initialPosY + e.deltaY;
            updateZoom();
        });
    </script>
</body>

</html>

Try It Out!

To see your amazing Hammer.js Image Carousel in action:

  1. Save the entire code above as an index.html file on your computer.
  2. Open that index.html file in your web browser (like Chrome, Firefox, Safari, Edge).
  3. Click the “Choose Files” button to select some images from your computer.
  4. Click “Load Images.”
  5. Now, try:
    • Clicking the and buttons to navigate.
    • If you have a touchscreen, swipe left and right on the carousel to change images!
    • Click on an image in the carousel to open the zoom view.
    • In the zoom view, try scrolling your mouse wheel to zoom in and out.
    • Click and drag the zoomed image to pan around it.
    • If you have a touchscreen, try pinching to zoom and dragging with one finger to pan!

You’ve just built a fantastic and interactive Hammer.js Image Carousel!

Conclusion

You did it! You’ve successfully built a powerful and interactive Hammer.js Image Carousel with touch, zoom, and pan features right in your browser. This project teaches you not only about basic web development (HTML, CSS, JavaScript) but also how to use an external library like Hammer.js to add advanced touch gestures. This skill is super valuable for making modern, mobile-friendly websites.

Keep experimenting, try adding more features (like deleting images, or a “download” button!), and don’t be afraid to break things and fix them. That’s how we learn best!


Did you find this post helpful for building your Hammer.js Image Carousel? Please let us know if you liked it or disliked it! Share this amazing project with your friends or on your social media to inspire others to build cool things!

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply