zoom and pan any element

Zoom and pan any element, while zooming in or out on the mouse cursor.

by Laurens Maneschijn

HTML

<div class="container">
			<img class="js_zoom_and_pan" src="https://placekitten.com/600/300" />
		</div>

<!--

TODO: lots of code cleanup, closure, scoping, options, config, etc.
      But it is a working prototype so far.

Interface:
- DoubleClick to reset element to original zoom and position.
- Mousewheel to zoom.
- Click and drag to pan element.

Works: 
- zooming in/out by using mousewheel
- panning image so mouse cursor stays focused on the same spot while zooming,
  (using css transform: translate(x%, y%) )
- pan image by dragging, using same system as above so we can both zoom and pan without problems.

See js panel for more details.

TODO: check support for different browser clients.


-->

CSS

html, body{
	background: #000;
	color: #fff;
	margin: 0;
	padding: 0;
}
.container {
	position: absolute;
	top: 10px;
	left: 10px;
	overflow: hidden;
	width: 95%;
	height: 95%;
	background: rgba(255, 0, 255, 0.1);
	border: 1px solid rgba(255, 0, 255, 0.2);
}
.js_zoom_and_pan {
	background: rgba(255, 255, 0, 0.1);
	border: 1px solid rgba(255, 255, 0, 0.2);
}

JavaScript

// https://jsfiddle.net/ElMoonLite/fog2vach/

// TODO: lots of code cleanup, closure, scoping, etc.
//
// REQUIREMENTS:
// Can basicly zoom and pan any element, 
// as long as nothing else is adjusting it's css properties for zoom and/or transform.
// Works best if inside an overflow: hidden element. 
// Can get weird behaviour if inside a scrollable element (or even full document scroll).
//
// NOTES, references, etc.:
// https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent
// https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event
// deltaY > 0 = mousewheel down
// To make event.preventDefault() work on wheel event:
// https://stackoverflow.com/questions/44406297/mark-event-as-explicitly-not-passive


// https://stackoverflow.com/questions/799981/document-ready-equivalent-without-jquery
function ready(callback) {
	if (document.readyState!='loading') {
		// in case the document is already rendered
		callback();
	} else if (document.addEventListener) {
		// modern browsers
		document.addEventListener('DOMContentLoaded', callback);
	} else {
		// IE <= 8
		document.attachEvent('onreadystatechange', function(){
			if (document.readyState=='complete') {
				callback();
			}
		});
	}
}

ready(function() {
    init();
});

function init() {
	// wheel event for zooming:
	// NB: { passive: false } needed so we can do event.preventDefault() on mousewheel event (to prevent scrolling and zooming at the same time).
	document.body.addEventListener('wheel', function(event) {
		handleWheel(event);
	}, { passive: false } );

	// events below are for panning:
	document.body.addEventListener('dragstart', function(event){
		handleDragStart(event);
	});
	document.body.addEventListener('dragend', function(event){
		stopDrag();
	});
	document.body.addEventListener('drag', function(event){
		handleDrag(event);
	});
//	// This prevents dragging the entire element out of view if it is inside an overflow: hidden container, 
//	// (in which case you can't drag it back...