溢出时平移图像:使用 Javascript 滚动

所以我拥有的是一个更大的图像,包裹在一个较小的 div 中。所以我有滚动条来查看隐藏的图像部分。但我想要的是通过鼠标拖动而不是滚动条来平移图像,基本上我希望在我的图像上实现平移。

我使用过 Panzoom JS,但它的其他功能正在破坏我的代码。那么有没有我可以用来平移图像的 javascript hack

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.com/photo/2015/04/23/22/00/tree-736885_960_720.jpg">
    </div>
stack overflow Panning Image When Overflow: scroll
原文答案
author avatar

接受的答案

两个例子,
一个在使用 CSS transform: translate() 的视口中使用 CSS flexbox centered canvas(没有开箱即用的滚动条),另一个使用 scrollTo() 和 scrollLeft/Top(可以有浏览器滚动条)。

视口区域内的居中画布。 CSS翻译
================================================ =

公式非常简单,转换是相对于画布中心,因此您只需使用以下方式转换它:

offsetX = currentPointerX - oldPointerX - oldOffsetX

这是一个没有约束计算的基本示例:

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 = (ev) => {
    isPan = false;
  };

  elViewport.addEventListener("mousedown", panStart);
  addEventListener("mousemove", panMove);
  addEventListener("mouseup", 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;
}

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

或者甚至更简单,通过使用 Event.movementX,Y - 因为我们不必记住和存储初始坐标并手动计算差异:

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 = (ev) => {
    isPan = false;
  };

  elViewport.addEventListener("mousedown", panStart);
  addEventListener("mousemove", panMove);
  addEventListener("mouseup", 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;
}

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

使用 scrollTo() 和 scrollLeft/Top

这个不是很好,因为内部 canvas 转换是浏览器默认的:左上角。 (可能需要触发自动滚动,以便在初始化时将画布置于视口内的中心)

公式:

scrollLeft = oldScrollLeft + oldPointerX - currentpointerX
  • 在平移开始时(“mousedown”)——存储开始的 X、Y 位置(也考虑当前的 scrollLeft、scrollTop 值)
  • 平移 ("mousemove") - 使用 Element.scrollTo(x, y) 其中 x 和 y 是初始位置减去当前位置

    
    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 = (ev) => {
    isPan = false;
    };
    
    elViewport.addEventListener("mousedown", panStart);
    addEventListener("mousemove", panMove);
    addEventListener("mouseup", panEnd);
    };

document.querySelectorAll(".viewport").forEach(Pannable);

.viewport {
margin: 50px auto;
width: 300px;
height: 150px;
overflow: auto; / Set to hidden to remove scrollbars /
}

.viewport > *{
display: block;
max-width: 500px;
}


或者,通过使用 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 = (ev) => {
isPan = false;
};

elViewport.addEventListener("mousedown", panStart);
addEventListener("mousemove", panMove);
addEventListener("mouseup", panEnd);
};

document.querySelectorAll(".viewport").forEach(Pannable);

.viewport {
margin: 50px auto;
width: 300px;
height: 150px;
overflow: auto; / Set to hidden to remove scrollbars /
}

.viewport > *{
display: block;
max-width: 500px;
}



要居中滚动可滚动元素,请参阅: [Auto center horizontal scrollbar](https://stackoverflow.com/a/74080838/383904) 

答案: