JavaScript缩放到鼠标x/y坐标

3

我设法使鼠标拖动滚动div,但使用鼠标进行缩放不完整。

它能够工作,但是我希望鼠标指针在保持图像位置的同时缩放图像,就像这样:

Screenshot

我需要使用scrollBy()将滚动返回到缩放之前的点。有人知道怎么做吗?

这是有人制作的一个示例 https://jsfiddle.net/xta2ccdt/13/ ,这正是我所需要的,但该代码使用了translate()和其他一些不适用于这里的东西,因为我也有滚动/拖动。

这是我的jsfiddle代码:https://jsfiddle.net/catalinu/1f6e0jna/

以下是stackoverflow中的代码:

请帮忙。我已经努力了好几天。

for (const divMain of document.getElementsByClassName('main')) {
  // drag the section
  for (const divSection of divMain.getElementsByClassName('section')) {
   // when mouse is pressed store the current mouse x,y
    let previousX, previousY
    divSection.addEventListener('mousedown', (event) => {
      previousX = event.pageX
      previousY = event.pageY
    })
    
    // when mouse is moved, scrollBy() the mouse movement x,y
    divSection.addEventListener('mousemove', (event) => {
     // only do this when the primary mouse button is pressed (event.buttons = 1)
      if (event.buttons) {
        let dragX = 0
        let dragY = 0
        // skip the drag when the x position was not changed
        if (event.pageX - previousX !== 0) {
          dragX = previousX - event.pageX
          previousX = event.pageX
        }
        // skip the drag when the y position was not changed
        if (event.pageY - previousY !== 0) {
          dragY = previousY - event.pageY
          previousY = event.pageY
        }
        // scrollBy x and y
        if (dragX !== 0 || dragY !== 0) {
          divMain.scrollBy(dragX, dragY)
        }
      }
    })
  }

  // zoom in/out on the section
  let scale = 1
  const scaleFactor = 0.05
  divMain.addEventListener('wheel', (event) => {
   // preventDefault to stop the onselectionstart event logic
    event.preventDefault()
    for (const divSection of divMain.getElementsByClassName('section')) {
     // set the scale change value
      const scaleChange = (event.deltaY < 0) ? scaleFactor : -scaleFactor
      // don't allow the scale to go outside of [0,5 - 2]
      if (scale + scaleChange < 0.5 || scale + scaleChange > 2) {
        return
      }
      // round the value when using high dpi monitors
      scale = Math.round((scale + scaleChange) * 100) / 100

   // apply the css scale
      divSection.style.transform = `scale(${scale}, ${scale})`

      // re-adjust the scrollbars        
      const x = Math.round(divMain.scrollLeft * scaleChange)
      const y = Math.round(divMain.scrollTop * scaleChange)
      divMain.scrollBy(x, y)
    }
  })
}
body {
  margin: 0;
}

.main {
 width: 100%; /* percentage fixes the X axis white space when zoom out */
 height: 100vh; /* this is still an issue where you see white space when zoom out in the Y axis */
 overflow: scroll; /* needed for safari to show the x axis scrollbar */
}

.main .section {
 width: 200%;
 height: 200vh;
 background-image: url('https://iso.500px.com/wp-content/uploads/2014/07/big-one.jpg');
 transform-origin: 0 0;
}
<main class="main">
  <section class="section"></section>
</main>

1个回答

7
你的问题主要集中在以下几行代码。
const x = Math.round(divMain.scrollLeft * scaleChange)
const y = Math.round(divMain.scrollTop * scaleChange)

滚动缩放的方式如下:

  • 计算缩放发生的未经缩放的x,y坐标。
  • 通过将其乘以新的比例计算出新的缩放后的x,y坐标。
  • 现在,您希望这个新坐标保持与旧坐标相同的位置。所以基本上,如果从新的scaled x,y中减去offset x,y,您就会得到左侧和顶部的滚动。

更新的代码如下:

for (const divMain of document.getElementsByClassName('main')) {
  // drag the section
  for (const divSection of divMain.getElementsByClassName('section')) {
    // when mouse is pressed store the current mouse x,y
    let previousX, previousY
    divSection.addEventListener('mousedown', (event) => {
      previousX = event.pageX
      previousY = event.pageY
    })

    // when mouse is moved, scrollBy() the mouse movement x,y
    divSection.addEventListener('mousemove', (event) => {
        // only do this when the primary mouse button is pressed (event.buttons = 1)
      if (event.buttons) {
        let dragX = 0
        let dragY = 0
        // skip the drag when the x position was not changed
        if (event.pageX - previousX !== 0) {
          dragX = previousX - event.pageX
          previousX = event.pageX
        }
        // skip the drag when the y position was not changed
        if (event.pageY - previousY !== 0) {
          dragY = previousY - event.pageY
          previousY = event.pageY
        }
        // scrollBy x and y
        if (dragX !== 0 || dragY !== 0) {
          divMain.scrollBy(dragX, dragY)
        }
      }
    })
  }

  // zoom in/out on the section
  let scale = 1
  const factor = 0.05
  const max_scale =4

  divMain.addEventListener('wheel', (e) => {
    // preventDefault to stop the onselectionstart event logic
    for (const divSection of divMain.getElementsByClassName('section')) {
        e.preventDefault();
        var delta = e.delta || e.wheelDelta;
        if (delta === undefined) {
          //we are on firefox
          delta = e.originalEvent.detail;
        }
        delta = Math.max(-1,Math.min(1,delta)) // cap the delta to [-1,1] for cross browser consistency
            offset = {x: divMain.scrollLeft, y: divMain.scrollTop};
     image_loc = {
        x: e.pageX + offset.x,
        y: e.pageY + offset.y
     }

     zoom_point = {x:image_loc.x/scale, y: image_loc.y/scale}

        // apply zoom
        scale += delta*factor * scale
        scale = Math.max(1,Math.min(max_scale,scale))

     zoom_point_new = {x:zoom_point.x * scale, y: zoom_point.y * scale}

     newScroll = {
        x: zoom_point_new.x - e.pageX,
        y: zoom_point_new.y - e.pageY
     }


      divSection.style.transform = `scale(${scale}, ${scale})`
      divMain.scrollTop = newScroll.y
      divMain.scrollLeft = newScroll.x
    }


  })
}

更新的范例链接为:

https://jsfiddle.net/uy390v8t/1/

结果


1
谢谢。这确实修复了滚动问题。我已经将您的更改应用到我的代码中,地址是 https://jsfiddle.net/catalinu/zof8q63u/15/ ,我还在最大缩放时修复了底部的空白。需要等待18个小时才能给您打赏。 - user670839
使用您的fiddle https://jsfiddle.net/catalinu/jLfrbzwp/ 并将最小比例更改为0.5,您知道为什么当您在0.5缩小时,为什么垂直滚动条会那么大,并向下滚动会显示很多空白空间吗?有没有办法修复它? - user670839
这是因为您已经给定了一个固定的“200vh”高度,高度也需要通过JavaScript设置,以确保它是您想要的。 - Tarun Lalwani
我已经给section div添加了一个固定的宽度和高度,大小为5000px,就像这样https://jsfiddle.net/catalinu/jLfrbzwp/2/。由于它始终保持不变,只有缩放比例会改变,那么我需要用JavaScript设置什么宽度/高度呢?难道浏览器不能自动计算吗?水平方向可以工作,因为主容器是100%,但垂直方向会因为我们使用100vh而导致白色空间破裂,因为100%的高度没有任何作用。有什么解决办法吗? - user670839
我建议您发布一个新问题。另外,看看这个链接是否有帮助 https://stackoverflow.com/questions/42616579/css-transform-scale-to-background-image?同时,在此线程中引用您的新问题链接。 - Tarun Lalwani
谢谢您的建议。我已经在这里创建了一个新问题:https://stackoverflow.com/questions/57373062/cross-browser-height-100-on-transform-scale-1 - user670839

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接