front-end

How to move (drag/pan) and zoom an element (image or div) using JavaScript

· John Doe

614 Views

Check this out!

 

<!DOCTYPE html>
<html>
    <head>
        <title>Move (drag/pan) and zoom object (image or div) in pure js</title>
        <style>
            html,
            body {
                margin: 0;
                padding: 0;
            }

            #zoomContainer {
                width: 100vw;
                height: 100vh;
                overflow: hidden;
                position: relative;
            }

            #zoomImage {
                position: absolute;
                cursor: move;
                transition: transform 0.3s;
                max-width: 100%;
                max-height: 100%;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
            }

            .zoomButton {
                position: fixed;
                top: 20px;
                padding: 10px;
                font-size: 16px;
                background-color: #ffffff;
                border: none;
                cursor: pointer;
            }

            #zoomInButton {
                left: 20px;
            }

            #zoomOutButton {
                left: 60px;
            }
        </style>
    </head>
    <body>
        <div id="zoomContainer">
        	<img id="zoomImage" src="bg.jpg" alt="zoom image" />
        </div>
        <button id="zoomInButton" class="zoomButton">zoom in</button>
        <button id="zoomOutButton" class="zoomButton">zoom out</button>
        
        <script>
		document.addEventListener('DOMContentLoaded', function() {
		    var zoomContainer = document.getElementById('zoomContainer');
		    var zoomImage = document.getElementById('zoomImage');
		
		    var zoomLevel = 1;
		    var minZoomLevel = 1;
		    var maxZoomLevel = 2;
		    var zoomIncrement = 0.2;
		    var isDragging = false;
		    let newPosX = 0,
		        newPosY = 0,
		        startPosX = 0,
		        startPosY = 0;
		
		    function updateZoomedImage() {
		        var imageWidth = zoomImage.offsetWidth;
		        var imageHeight = zoomImage.offsetHeight;
		
		        var newImageWidth = imageWidth * zoomLevel;
		        var newImageHeight = imageHeight * zoomLevel;
		
		        var left = zoomImage.offsetLeft;
		        var top = zoomImage.offsetTop;
		
		        zoomImage.style.transform = 'scale(' + zoomLevel + ')';
		        zoomImage.style.width = newImageWidth + 'px';
		        zoomImage.style.height = newImageHeight + 'px';
		
		        // Restrict images from leaving containers
		        if (Math.abs(zoomImage.offsetLeft - newPosX) >= Math.abs((parseInt(zoomImage.style.width, 10) - window.innerWidth) / 2)) {
		            left = 0;
		        }
		        if (Math.abs(zoomImage.offsetTop - newPosY) >= Math.abs((parseInt(zoomImage.style.height, 10) - window.innerHeight) / 2)) {
		            top = 0;
		        }
		
		        zoomImage.style.left = left + 'px';
		        zoomImage.style.top = top + 'px';
		
		        // when the user clicks down on the element
		        zoomImage.addEventListener('mousedown', function(e) {
		            e.preventDefault();
		
		            // get the starting position of the cursor
		            startPosX = e.clientX;
		            startPosY = e.clientY;
		
		            document.addEventListener('mousemove', mouseMove);
		
		            document.addEventListener('mouseup', function() {
		                document.removeEventListener('mousemove', mouseMove);
		            });
		
		        });
		    }
		
		    function zoomIn() {
		        if (zoomLevel < maxZoomLevel) {
		            zoomLevel += zoomIncrement;
		            zoomLevel = Math.min(zoomLevel, maxZoomLevel);
		            updateZoomedImage();
		        }
		        if (zoomLevel === maxZoomLevel) {
		            document.getElementById('zoomInButton').disabled = true;
		        }
		        document.getElementById('zoomOutButton').disabled = false;
		    }
		
		    function zoomOut() {
		        if (zoomLevel > minZoomLevel) {
		            zoomLevel -= zoomIncrement;
		            zoomLevel = Math.max(zoomLevel, minZoomLevel);
		            updateZoomedImage();
		        }
		        if (zoomLevel === minZoomLevel) {
		            document.getElementById('zoomOutButton').disabled = true;
		        }
		        document.getElementById('zoomInButton').disabled = false;
		        if (zoomLevel === 1) {
		            zoomImage.style.cursor = 'move';
		        }
		    }
		
		    function mouseMove(e) {
		        // calculate the new position
		        newPosX = startPosX - e.clientX;
		        newPosY = startPosY - e.clientY;
		
		        // with each move we also want to update the start X and Y
		        startPosX = e.clientX;
		        startPosY = e.clientY;
		
		        // Restrict images from leaving containers
		        if (Math.abs(zoomImage.offsetLeft - newPosX) >= Math.abs((parseInt(zoomImage.style.width, 10) - window.innerWidth) / 2) ||
		            Math.abs(zoomImage.offsetTop - newPosY) >= Math.abs((parseInt(zoomImage.style.height, 10) - window.innerHeight) / 2)
		        ) {
		            return;
		        }
		
		        // set the element's new position:
		        zoomImage.style.left = (zoomImage.offsetLeft - newPosX) + "px";
		        zoomImage.style.top = (zoomImage.offsetTop - newPosY) + "px";
		    }
		
		    function initialize() {
		        var windowWidth = window.innerWidth;
		        var windowHeight = window.innerHeight;
		
		        zoomContainer.style.width = windowWidth + 'px';
		        zoomContainer.style.height = windowHeight + 'px';
		        updateZoomedImage();
		
		        document.getElementById('zoomInButton').addEventListener('click', zoomIn);
		        document.getElementById('zoomOutButton').addEventListener('click', zoomOut);
		    }
		
		    initialize();
		
		    window.addEventListener('resize', function() {
		        initialize();
		    });
		});
		</script>
        
    </body>
</html>

 

Result:

Close Start with a boilerplate: jQuery Vue React React + JSX Preact TypeScript CoffeeScript SCSS CSS Grid Bootstrap PostCSS Show boilerplate bar less often? Links: 👍🏻 Roadmap (vote for features) 🐞 Bug tracker 📙 Docs 🎛 Service status Support JSFiddle and g

 

Ref.

While building out a new image generator, I needed a way to drag and move HTML elements inside of another element. Take a look at what I was building below: This is actually the new Post Image Genera...