<div class="container">
  <div class="image">
    <img src="https://picsum.photos/160/90" />
  </div>
</div>
.container {
  width: 160px;
  height: 90px;
  overflow: hidden;
  outline: 1px solid gray;
}

.image {
  width: 100%;
  height: 100%;
  transition: transform .3s;
  transform-origin: 0 0;
}

img {
  width: auto;
  height: auto;
  max-width: 100%;
}
const container = document.querySelector('.container');
const image = document.querySelector('.image');
const speed = 0.25;
let size = { 
  w: image.offsetWidth, 
  h: image.offsetHeight 
};
let pos = { x: 0, y: 0 };
let target = { x: 0, y: 0 };
let pointer = { x: 0, y: 0 };
let scale = 1;

window.addEventListener('wheel', event => {
  event.preventDefault();
  
  pointer.x = event.pageX - container.offsetLeft;
  pointer.y = event.pageY - container.offsetTop;
  
  target.x = (pointer.x - pos.x) / scale;
  target.y = (pointer.y - pos.y) / scale;
  
    console.log('scale', scale)
 
  scale += -1 * Math.max(-1, Math.min(1, event.deltaY)) * speed * scale;
  const max_scale = 5;
  const min_scale = 1;
  scale = Math.max(min_scale, Math.min(max_scale, scale));

    console.log('scale', scale)
  
  pos.x = -target.x * scale + pointer.x;
  pos.y = -target.y * scale + pointer.y;

  
  if (pos.x > 0) pos.x = 0;
  if (pos.x + size.w * scale < size.w) pos.x = -size.w * (scale - 1);
  if (pos.y > 0) pos.y = 0;
  if (pos.y + size.h * scale < size.h) pos.y = -size.h * (scale - 1);

  // console.log('pointer', pointer)
  // console.log('target', target)

  // console.log('pos', pos)
  // console.log('______________________________________________________________')
  image.style.transform = `translate(${pos.x}px,${pos.y}px) scale(${scale},${scale})`;
}, { passive: false });

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.