JSFiddle - React, Tailwind, and code Playground

by Jordan Sayner

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.js"></script>
<div class="imageContainer"></div>
<div id="hud"></div>

CSS

html, body {
  background: #1a1a1b;
  height: 100%;
  margin: 0;
  padding: 0;
}

.imageContainer {
  width: 96%;
  height: 96%;
  max-width: 800px;
  max-height: 600px;
  position: absolute;
  overflow: hidden;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  margin: auto;
  background: #2b2b2c;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}


.imageContainer > img {
  display: block;
  max-width:100%;
  max-height:100%;
  cursor: move;
  touch-action: none;
}

#hud {
  background: rgba(0,0,0,0.6);
  position: absolute;
  padding: 10px 20px;
  color: #FFF;
  font-family: sans-serif;
  font-size: 1.2em;
  pointer-events: none;
}

JavaScript

'use strict';

const imageUrl = 'https://source.unsplash.com/random';
const imageContainer = document.querySelector('.imageContainer');
const hud = document.querySelector('#hud');

let minScale = 1;
let maxScale = 4;
let imageWidth;
let imageHeight;
let containerWidth;
let containerHeight;
let displayImageX = 0;
let displayImageY = 0;
let displayImageScale = 1;

let displayDefaultWidth;
let displayDefaultHeight;

let rangeX = 0;
let rangeMaxX = 0;
let rangeMinX = 0;

let rangeY = 0;
let rangeMaxY = 0;
let rangeMinY = 0;

let displayImageRangeY = 0;

let displayImageCurrentX = 0;
let displayImageCurrentY = 0;
let displayImageCurrentScale = 1;


function resizeContainer() {
  containerWidth = imageContainer.offsetWidth;
  containerHeight = imageContainer.offsetHeight;
  if (displayDefaultWidth !== undefined && displayDefaultHeight !== undefined) {
    displayDefaultWidth = displayImage.offsetWidth;
    displayDefaultHeight = displayImage.offsetHeight;
    updateRange();
    displayImageCurrentX = clamp( displayImageX, rangeMinX, rangeMaxX );
    displayImageCurrentY = clamp( displayImageY, rangeMinY, rangeMaxY );
    updateDisplayImage(
      displayImageCurrentX,
      displayImageCurrentY,
      displayImageCurrentScale );
  }
}

resizeContainer();

function clamp(value, min, max) {
  return Math.min(Math.max(min, value), max);
}

function clampScale(newScale) {
  return clamp(newScale, minScale, maxScale);
}

window.addEventListener('resize', resizeContainer, true);

const displayImage = new Image();
displayImage.src = imageUrl;
displayImage.onload = function(){
  imageWidth = displayImage.width;
  imageHeight = displayImage.height;
  imageContainer.appendChild(displayImage);
  displayImage.addEventListener('mousedown', e => e.preventDefault(), false);
  displayDefaultWidth = displayImage.offsetWidth;
  displayDefaultHeight = displayImage.offsetHeight;
  rangeX = Math.max(0, displayDefaultWidth - containerWidth);
  rangeY = Math.max(0, displayDefaultHeight -...