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
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, Javascript may return invalid height and width of the image.
There can also can be a chance that the image is already loaded and available in the browser cache.
// image element const imageElement = document.querySelector(".drag-image") // image width let imageWidth // image height let imageHeight // boolean whether image loaded or not let imageLoaded = false // check whether image is cached or wait for the image to load // this is necessary before calculating width and height of the image if(imageElement.complete) { imageFullyLoaded() } else { imageElement.addEventListener('load', function() { imageFullyLoaded() }) } // image is loaded function imageFullyLoaded() { imageWidth = imageElement.naturalWidth imageHeight = imageElement.naturalHeight imageLoaded = true }
-
When the user performs a mousedown event we set a global variable — for example we set draggingInProgress = true
This indicates start of dragging.
let draggingInProgress document.querySelector(".image-container").addEventListener('mousedown', (event) => { if(imageLoaded) { draggingInProgress = true } })
When the mouseup event occurs we set draggingInProgress = false
This indicates end of dragging.
document.querySelector(".image-container").addEventListener('mouseup', (event) => { draggingInProgress = false })
Upon a mousemove event we check whether draggingInProgress === true. 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.
document.querySelector(".image-container").addEventListener('mousemove', (event) => { if(draggingInProgress) { // code to drag the image } })
On each occurring mousemove event we find the position of the mouse within the image container (through the offsetX and offsetY properties of the event object).
document.querySelector(".image-container").addEventListener('mousemove', (event) => { // rest of the codes const currentMousePosition = { x: event.offsetX, y: event.offsetY } // 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 class="image-container">
<img class="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;
}
const containerWidth = document.querySelector('.image-container').offsetWidth
const containerHeight = document.querySelector('.image-container').offsetHeight
const imageElement = document.querySelector(".drag-image")
let draggingInProgress
let lastMousemovePosition = { x: null, y: null }
let imageWidth
let imageHeight
let imageLoaded = false
// check whether image is cached or wait for the image to load
// this is necessary before calculating width and height of the image
if(imageElement.complete) {
imageFullyLoaded()
} else {
imageElement.addEventListener('load', function() {
imageFullyLoaded()
})
}
// image is loaded
function imageFullyLoaded() {
imageWidth = imageElement.naturalWidth
imageHeight = imageElement.naturalHeight
imageLoaded = true
}
document.querySelector(".image-container").addEventListener('mousedown', (event) => {
if(imageLoaded) {
draggingInProgress = true
lastMousemovePosition = { x: event.offsetX, y: event.offsetY }
}
})
document.querySelector(".image-container").addEventListener('mouseup', (event) => {
draggingInProgress = false
})
document.querySelector(".image-container").addEventListener('mousemove', (event) => {
if(draggingInProgress) {
const currentMousePosition = { x: event.offsetX, y: event.offsetY }
const changeX = currentMousePosition.x - lastMousemovePosition.x
const changeY = currentMousePosition.y - lastMousemovePosition.y
// save mouse position
lastMousemovePosition = currentMousePosition
const imgTop = parseInt(window.getComputedStyle(imageElement).getPropertyValue('top'), 10)
const imgLeft = parseInt(window.getComputedStyle(imageElement).getPropertyValue('left'), 10)
const imgTopNew = imgTop + changeY
const imgLeftNew = imgLeft + changeX
// validate top and left do not fall outside the image, otherwise white space will be seen
if(imgTopNew > 0)
imgTopNew = 0
if(imgTopNew < (containerHeight - imageHeight))
imgTopNew = containerHeight - imageHeight
if(imgLeftNew > 0)
imgLeftNew = 0
if(imgLeftNew < (containerWidth - imageWidth))
imgLeftNew = containerWidth - imageWidth
imageElement.style.top = imgTopNew + 'px'
imageElement.style.left = imgLeftNew + '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.