admin管理员组

文章数量:1278985

So what I have is a bigger image wrapped inside a smaller div. So i have scrollbar to see the hidden image parts. But what I want is the image to pan through mouse drag rather than the scroll bars, basically I want to implement Panning on my image.

I have used Panzoom JS but its other functionalities are ruining my code. So is there any javascript hack that I can use to Pan image

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

.mainContent {
  width: 400px;
  height: 400px;
  overflow: scroll;
  display: flex;
  justify-content: center;
  align-items: center;
}
<div class="mainContent">
  <img src=".jpg">
</div>

So what I have is a bigger image wrapped inside a smaller div. So i have scrollbar to see the hidden image parts. But what I want is the image to pan through mouse drag rather than the scroll bars, basically I want to implement Panning on my image.

I have used Panzoom JS but its other functionalities are ruining my code. So is there any javascript hack that I can use to Pan image

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

.mainContent {
  width: 400px;
  height: 400px;
  overflow: scroll;
  display: flex;
  justify-content: center;
  align-items: center;
}
<div class="mainContent">
  <img src="https://cdn.pixabay./photo/2015/04/23/22/00/tree-736885_960_720.jpg">
</div>

Share Improve this question edited Jan 29, 2024 at 12:29 Roko C. Buljan 207k41 gold badges327 silver badges337 bronze badges asked Jul 7, 2021 at 4:48 ChempooroChempooro 4351 gold badge8 silver badges29 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 14

Two examples,
one using a CSS flexbox centered canvas in viewport that uses CSS transform: translate() (No scrollbars out of the box), and the other using scrollTo() and scrollLeft/Top (Can have browser scrollbars).

Centered canvas inside viewport area. CSS translate

The formula is quite simple, the transform is relative to the canvas center, therefore you simply need to translate it using:

offsetX = currentPointerX - oldPointerX - oldOffsetX

Here's a basic example without the constraints calculation:

const Pannable = (elViewport) => {

  const elCanvas = elViewport.firstElementChild;
  const start = {x: 0, y: 0};
  const offset = {x: 0, y: 0}; // The transform offset (from center)
  let isPan = false;

  const panStart = (ev) => {
    ev.preventDefault();
    isPan = true;
    start.x = ev.clientX - offset.x;
    start.y = ev.clientY - offset.y;
  };

  const panMove = (ev) => {
    if (!isPan) return; // Do nothing
    offset.x = ev.clientX - start.x;
    offset.y = ev.clientY - start.y;
    elCanvas.style.translate = `${offset.x}px ${offset.y}px`;
  };

  const panEnd = () => {
    isPan = false;
  };

  elViewport.addEventListener("pointerdown", panStart);
  addEventListener("pointermove", panMove);
  addEventListener("pointerup", panEnd);
};

document.querySelectorAll(".viewport").forEach(Pannable);
.viewport {
  margin: 50px auto;
  width: 300px;
  height: 150px;
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
  outline: 1px solid #000;
  touch-action: none; /* Prevent pointermove default panning */
}

.viewport>* {
  max-width: 500px;
}
<div class="viewport">
  <img src="https://cdn.pixabay./photo/2015/04/23/22/00/tree-736885_960_720.jpg">
</div>

Or even simpler, by using Event.movementX,Y - because we don't have to remember and store the initial coordinates and manually calculate the difference:

const Pannable = (elViewport) => {

  const elCanvas = elViewport.firstElementChild;
  const offset = {x: 0, y: 0};
  let isPan = false;

  const panStart = (ev) => {
    ev.preventDefault();
    isPan = true;
  };

  const panMove = (ev) => {
    if (!isPan) return;
    offset.x += ev.movementX;
    offset.y += ev.movementY;
    elCanvas.style.translate = `${offset.x}px ${offset.y}px`;
  };

  const panEnd = () => {
    isPan = false;
  };

  elViewport.addEventListener("pointerdown", panStart);
  addEventListener("pointermove", panMove);
  addEventListener("pointerup", panEnd);
};

document.querySelectorAll(".viewport").forEach(Pannable);
.viewport {
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 30px auto;
  width: 300px;
  height: 150px;
  overflow: hidden;
  outline: 1px solid #000;
  touch-action: none; /* Prevent pointermove default panning */
}

.viewport>* {
  transform-origin: 0 0;
  max-width: 500px;
}
<div class="viewport">
  <img src="https://cdn.pixabay./photo/2015/04/23/22/00/tree-736885_960_720.jpg">
</div>

Using scrollTo() and scrollLeft/Top

This one is not that good since the inner canvas transforms are as browser default: top left corner. (It might be necessary to trigger an automatic scroll in order to center the canvas inside the viewport on init)

The formula:

scrollLeft = oldScrollLeft + oldPointerX - currentpointerX
  • On pan start ("mousedown") - store the start X,Y positions (account also for the current scrollLeft,scrollTop values)
  • On pan ("mousemove") - use Element.scrollTo(x, y) where x and y are the initial position minus the current position

const Pannable = (elViewport) => {

  const start = {x: 0, y: 0};
  let isPan = false;

  const panStart = (ev) => {
    ev.preventDefault();
    isPan = true;
    start.x = elViewport.scrollLeft + ev.clientX;
    start.y = elViewport.scrollTop + ev.clientY;
  };

  const panMove = (ev) => {
    if (!isPan) return;
    elViewport.scrollTo(
      start.x - ev.clientX,
      start.y - ev.clientY
    );
  };

  const panEnd = () => {
    isPan = false;
  };

  elViewport.addEventListener("pointerdown", panStart);
  addEventListener("pointermove", panMove);
  addEventListener("pointerup", panEnd);
};


document.querySelectorAll(".viewport").forEach(Pannable);
.viewport {
  margin: 50px auto;
  width: 300px;
  height: 150px;
  overflow: auto; /* Set to hidden to remove scrollbars */
  touch-action: none; /* Prevent pointermove default panning */
}

.viewport > *{
  display: block;
  max-width: 500px;
}
<div class="viewport">
  <img src="https://cdn.pixabay./photo/2015/04/23/22/00/tree-736885_960_720.jpg">
</div>

or also, by using Event.movementX,Y

const Pannable = (elViewport) => {

  let isPan = false;

  const panStart = (ev) => {
    ev.preventDefault();
    isPan = true;
  };

  const panMove = (ev) => {
    if (!isPan) return;
    elViewport.scrollTo(
      elViewport.scrollLeft - ev.movementX,
      elViewport.scrollTop - ev.movementY
    );
  };

  const panEnd = () => {
    isPan = false;
  };

  elViewport.addEventListener("pointerdown", panStart);
  addEventListener("pointermove", panMove);
  addEventListener("pointerup", panEnd);
};


document.querySelectorAll(".viewport").forEach(Pannable);
.viewport {
  margin: 50px auto;
  width: 300px;
  height: 150px;
  overflow: auto; /* Set to hidden to remove scrollbars */
  touch-action: none; /* Prevent pointermove default panning */
}

.viewport > *{
  display: block;
  max-width: 500px;
}
<div class="viewport">
  <img src="https://cdn.pixabay./photo/2015/04/23/22/00/tree-736885_960_720.jpg">
</div>

To center-scroll the scrollable element see: Auto center horizontal scrollbar

本文标签: javascriptPanning Image When Overflow scrollStack Overflow