How to Drag / Pan an Image in a Container div using jQuery

javascript
Published on November 8, 2016

There are situations sometimes when it is required that an image should be draggable within the container div. Something like the demo below is required :

Demo

Try dragging the image

How It Works

  • Dragging can be achieved by combining mousedown, mousemove and mouseup events.

  • While dragging we are changing the top and left properties of the image. For this we need the image to be relative/absolute positioned.

  • When the top and left properties of the image are changed, the part of the image seen in the container will change as well. For example below, the first image has top and left property set to 0 and 0. The second image has top and left set to -50px and -50px. The third one has top and left as -100px and -100px.

    When the image is dragged we can change the top and left properties depending on the amount of the drag. For example if we drag the image 50px upwards and 50px to the left, we should reduce the top value by 50px and left value by 50px to see the new part of the image.

Implementing the Drag Effect

Dragging is a combination of 3 mouse events. When the user starts dragging he presses the mouse down (mousedown event). Then he moves the mouse (mousemove events). To stop dragging he releases the mouse (mouseup event).

We can implement the dragging by :

  • Firstly check whether the image has been loaded or not. Otherwise on the first page load, jQuery will return the height and width of the image as 0.

    There can also can be a chance that the image is already loaded and available in the browser cache.

    // Width of image
    var _IMAGE_WIDTH;
    
    // Height of image
    var _IMAGE_HEIGHT;
    
    // Image loaded or not
    var _IMAGE_LOADED = 0;
    
    if($('#image-element').get(0).complete) {
    	ImageLoaded();
    }
    else {
    	$('#image-element').on('load', function() {
    		ImageLoaded();
    	});
    }
    
    // Image is loaded
    function ImageLoaded() {
    	_IMAGE_WIDTH = $("#image-element").width();
    	_IMAGE_HEIGHT = $("#image-element").height();
    	_IMAGE_LOADED = 1;	
    }
    
  • When the user performs a mousedown event we initialize a global variable and set it to 1. For example we set _DRAGGGING_STARTED = 1

    This indicates start of dragging.

    var _DRAGGGING_STARTED = 0;
    
    $('#image-container').on('mousedown', function(event) {
    	// Image should be loaded before it can be dragged
    	if(_IMAGE_LOADED == 1) { 
    		_DRAGGGING_STARTED = 1;
    	}
    });
    
  • When the mouseup event occurs we set _DRAGGGING_STARTED = 0

    This indicates end of dragging.

    $('#image-container').on('mouseup', function() {
    	_DRAGGGING_STARTED = 0;
    });
    
  • Upon a mousemove event we check whether _DRAGGGING_STARTED == 1. This is important because the user may move the mouse without keeping the mouse pressed. Dragging occurs only when the mouse is moved keeping the mouse pressed.

    This event indicates that the image is being dragged.

    $('#image-container').on('mousemove', function(event) {
    	if(_DRAGGGING_STARTED == 1) {
    		// Code to drag the image
    	}
    });
    

    On each occurring mousemove event we find the position of the mouse in the document (through the pageX and pageY properties of the event object). We then find the position of the mouse within the container (mouse position in the document - position of the container in the document).

    $('#image-container').on('mousemove', function(event) {
    	// rest of the codes
    	
    	// Position of mouse in the document
    	var mouse_pos_in_document = { x: event.pageX, y: event.pageY };
    
    	// Position of parent container in the document
    	var parent_pos_in_document =  $('#image-container').offset();
    
    	// Position of mouse within the container
    	var mouse_pos_in_container = { 
    								x: mouse_pos_in_document.x - parent_pos_in_document.left, 
    								y: mouse_pos_in_document.y - parent_pos_in_document.top 
    								};
    	// rest of the codes
    });
    

    This mouse position is compared with the previous mouse position. This comparison will give the amount by which the mouse has been moved. For example the previous mouse position was (100, 100) and current mouse position is (50, 50) — this means that the mouse has been moved 50px upwards and 50px to the left.

    We then subtract the current top and left properties of the image by the amount of mouse drag. For example if top and left are 0 & 0, and mouse movement is 50px up and 50px left, the new top and left values will be -50px and -50px. Similarly if top and left are -25px & 30px, the new top and left values will be -75px and -20px.

Complete Codes

<div id="image-container">
	<img id="drag-image" src="img.jpg" />
</div>
#image-container {
	display: block;
	height: 300px;
	width: 300px;
	margin: 40px auto;
	overflow: hidden;
	border: 1px solid #cccccc;
	box-sizing: border-box;
	position: relative;
	cursor: move;
}

#drag-image {
	left: 0;
	pointer-events: none;
	position: relative;
	top: 0;
	-moz-user-select: none;
}
var _DRAGGGING_STARTED = 0;
var _LAST_MOUSEMOVE_POSITION = { x: null, y: null };
var _DIV_OFFSET = $('#image-container').offset();
var _CONTAINER_WIDTH = $("#image-container").outerWidth();
var _CONTAINER_HEIGHT = $("#image-container").outerHeight();
var _IMAGE_WIDTH;
var _IMAGE_HEIGHT;
var _IMAGE_LOADED = 0;

// Check whether image is cached or wait for the image to load 
// This is necessary before calculating width and height of the image
if($('#drag-image').get(0).complete) {
	ImageLoaded();
}
else {
	$('#drag-image').on('load', function() {
		ImageLoaded();
	});
}

// Image is loaded
function ImageLoaded() {
	_IMAGE_WIDTH = $("#drag-image").width();
	_IMAGE_HEIGHT = $("#drag-image").height();
	_IMAGE_LOADED = 1;	
}

$('#image-container').on('mousedown', function(event) {
	/* Image should be loaded before it can be dragged */
	if(_IMAGE_LOADED == 1) { 
		_DRAGGGING_STARTED = 1;

		/* Save mouse position */
		_LAST_MOUSE_POSITION = { x: event.pageX - _DIV_OFFSET.left, y: event.pageY - _DIV_OFFSET.top };
	}
});

$('#image-container').on('mouseup', function() {
	_DRAGGGING_STARTED = 0;
});

$('#image-container').on('mousemove', function(event) {
	if(_DRAGGGING_STARTED == 1) {
		var current_mouse_position = { x: event.pageX - _DIV_OFFSET.left, y: event.pageY - _DIV_OFFSET.top };
		var change_x = current_mouse_position.x - _LAST_MOUSE_POSITION.x;
		var change_y = current_mouse_position.y - _LAST_MOUSE_POSITION.y;

		/* Save mouse position */
		_LAST_MOUSE_POSITION = current_mouse_position;

		var img_top = parseInt($("#drag-image").css('top'), 10);
		var img_left = parseInt($("#drag-image").css('left'), 10);

		var img_top_new = img_top + change_y;
		var img_left_new = img_left + change_x;

		/* Validate top and left do not fall outside the image, otherwise white space will be seen */
		if(img_top_new > 0)
			img_top_new = 0;
		if(img_top_new < (_CONTAINER_HEIGHT - _IMAGE_HEIGHT))
			img_top_new = _CONTAINER_HEIGHT - _IMAGE_HEIGHT;

		if(img_left_new > 0)
			img_left_new = 0;
		if(img_left_new < (_CONTAINER_WIDTH - _IMAGE_WIDTH))
			img_left_new = _CONTAINER_WIDTH - _IMAGE_WIDTH;

		$("#drag-image").css({ top: img_top_new + 'px', left: img_left_new + 'px' });
	}
});

Alternative Method

We can use the transform CSS property of the image to achieve the same thing. This will be better performance-wise and also the dragging effect can be animated.

Implementing in Mobiles

The same thing can be easily achieved in mobiles by using pointer events, insted of using mouse events. Codes will remain ~ same. Consult Advantages of Using Pointer Events Over Mouse & Touch Events to know more.

In this Tutorial