JSFiddle - React, Tailwind, and code Playground

HTML

<div class="container">
  <div class="wrapper">
    <div class="image-wrapper">
      <img class="viewer-img" src="https://cdn.pixabay.com/photo/2020/06/08/17/54/rain-5275506_960_720.jpg" draggable="false" />
    </div>
  </div>
</div>

CSS

* {
  box-sizing: border-box;
}

html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
}

.container {
  width: 100%;
  height: 100%;
  position: relative;
}

.wrapper {
  width: 100%;
  height: 100%;
  overflow: hidden;
  transition: all 0.1s;
  position: relative;
}

.image-wrapper {
  position: absolute;
}

.wrapper img {
  position: absolute;
  transform-origin: top left;
}

.point {
  width: 4px;
  height: 4px;
  background: #f00;
  position: absolute;
}

JavaScript

let wrapper = document.querySelector(".wrapper");
let image = wrapper.querySelector(".image-wrapper");
let img = document.querySelector('img')
let scale = 1;
let accx = 0, accy = 0
image.querySelector("img").addEventListener("wheel", (e) => {
  if (e.deltaY < 0) {
    scale = Number((scale * 1.2));
    accx += Number(e.offsetX * scale/1.2 - e.offsetX * scale)
    accy += Number(e.offsetY * scale/1.2 - e.offsetY * scale)

  } else {
    scale = Number((scale / 1.2));
    accx += Number(e.offsetX * scale * 1.2 - e.offsetX * scale)
    accy += Number(e.offsetY * scale * 1.2 - e.offsetY * scale)
  }
  e.target.style.transform = `scale3D(${scale}, ${scale}, ${scale})`
  image.style.transform = `translate(${accx}px, ${accy}px)`;
});