1

I have implemented a zoom to cursor position for a child element of a container.

While the zoom to cursor functions as expected, once the child element is scaled higher than 1, I can no longer scroll up or to the left, despite the parent container having overflow: scroll and the child content is overflowing to the top and left.

Steps reproduce

  1. Render stack snippet below
  2. Hover on the child element, input ctrl + mousewheel to zoom child element
  3. Once zoomed, attempt to scroll up or to the left to view overflowing content.

Desired Behavior

  • While the child element is scaled up, all the overflowing content of the child is able to be scrolled into view.
  • Zoom to cursor functionality is not inhibited.
  • Vanilla javascript/css with no library
Answers to similar questions

Use transform-origin: 0 0;

Set container scroll position to 0, 0 on zoom

const zoomContainer = document.querySelector(".zoomable");
let zoom = 1;
let origin = {
  x: 0,
  y: 0
};

zoomContainer.addEventListener(
  "wheel",
  (e) => {
    if (e.ctrlKey) {
      e.preventDefault();
      const {
        width,
        height
      } = e.currentTarget.getBoundingClientRect();
      const {
        left,
        top
      } = e.currentTarget.parentElement.getBoundingClientRect();
      const {
        scrollLeft,
        scrollTop
      } = e.currentTarget.parentElement

      const mousePositionX = e.clientX - left + scrollLeft;
      const mousePositionY = e.clientY - top + scrollTop;

      const zoomX = (mousePositionX - origin.x) / zoom;
      const zoomY = (mousePositionY - origin.y) / zoom;

      zoom = zoom + -1 * Math.max(-1, Math.min(1, e.deltaY)) * 0.5 * zoom;
      zoom = Math.max(1, zoom);

      const zoomWidth = width * zoom;
      const zoomHeight = height * zoom;

      origin.x = -zoomX * zoom + mousePositionX;
      origin.y = -zoomY * zoom + mousePositionY;

      if (origin.x > 0) origin.x = 0;
      if (origin.x + zoomWidth < width) origin.x = -width * (zoom - 1);
      if (origin.y > 0) origin.y = 0;
      if (origin.y + zoomHeight < height) origin.y = -height * (zoom - 1);

      e.currentTarget.style.transform = `translate(${origin.x}px, ${origin.y}px) scale(${zoom})`;
    }
  }, {
    passive: false
  }
);
* {
  box-sizing: border-box;
}

body {
  background-color: lightgrey;
  min-height: 100vh;
  margin: 0;
  display: grid;
  place-items: center;
}

.scroll-container {
  height: 400px;
  width: 400px;
  background-color: #222;
  overflow: scroll;
}

.zoomable {
  height: 100%;
  color: lightgrey;
  padding: 0.5rem;
  transform-origin: 0 0;
  transition: transform 300ms ease-in-out;
}
<div class="scroll-container">
  <div class="zoomable">
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce tristique, ante ac cursus pulvinar, ligula nibh sodales purus, vitae tempus metus lacus et tortor. Integer ut hendrerit nisi. Curabitur metus lorem, egestas ut lacus semper, tristique
      condimentum nisi. In facilisis lorem sem, vel viverra quam convallis sed. Aenean porta sit amet ante ac condimentum. Vivamus molestie vehicula magna ut sagittis.</p>
    <p> Vestibulum ut felis convallis, fringilla neque eu, imperdiet ante. Proin venenatis orci arcu, eu rutrum quam consectetur sit amet. Aenean et dui vel eros sagittis facilisis. Curabitur aliquam felis in magna tempor tristique. Proin ac tellus gravida,
      viverra turpis id, posuere magna. Nunc mollis orci id erat fermentum, a consectetur massa sollicitudin. Maecenas fringilla nibh pulvinar tortor eleifend tincidunt in sed sapien. Suspendisse cursus gravida ante, finibus convallis nunc feugiat id.
    </p>
  </div>
</div>

I have been through many iterations of this zoom to cursor solution, and the problem has been present the whole time. I would doubt that any one removal/change of existing code in this example will solve the issue.

PsiKai
  • 1,803
  • 1
  • 5
  • 19

0 Answers0