likes
comments
collection
share

当背景图片缩放遇上点位拖拽

作者站长头像
站长
· 阅读数 16

0、写在前面

在我之前实现点位拖拽这篇文章中提到过,本文将是那一篇文章的需求升级版。如果只是希望阅读普通点位拖拽的同学可以移步那篇文章。

回归正题,本文要实现的是精确标点:背景图片缩放并拖拽+点位拖拽。

文字描述可能不太清楚,大家可以结合第二部分的gif加以理解。

1、成品效果

当背景图片缩放遇上点位拖拽

如上gif图所示,在一个宽高确定的区域中,有一张图片,我们可以在图片上标注点位(即粉红色框)。考虑到标点的准确性,我们需要将图片放大,并调整图片位置,将点位拖拽至精确位置。将图片恢复至原大小后,点位相对于图片的位置不变。

借助背景图片的缩放,实现精确标点。

2、整体布局

<style>
    .content {
      width: 600px;
      height: 400px;
      margin: auto;
      border: 1px solid pink;
      overflow: hidden;
    }
    .pic {
      position: relative;
      width: 100%;
      height: 100%;
      background: url('https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg');
      background-size: 100% 100%;
      transform-origin: 0 0;
    }
    .point {
      position: absolute;
      width: 50px;
      height: 50px;
      background-color: pink;
    }
  </style>
  
  <div class="content">
    <div class="pic">
      <div class="point"></div>
    </div>
  </div>

如上html所示,content为宽高固定的区域,pic就是里面要展示的图片,然后需要拖拽的点位就是point。

考虑到图片放大之后会溢出content,所以content中需要overflow:hidden。

3、实现普通点位拖拽

关于point的拖拽,在之前的文章中已经描述过,这里就不再赘述了。

后面实现其他功能时,会对这部分代码稍作修改,将会在下文中展开。

4、实现背景图片缩放

4.1 缩放方案

实现图片的缩放,我们选用的方案是使用转换:transform: scale()。它在MDN上的描述为:

CSS 函数 scale() 用于修改元素的大小。可以通过向量形式定义的缩放值来放大或缩小元素。

按我自己的理解就是,用scale( )进行缩放的元素,他们的宽高在数值上是不变(即100px的宽高,放大后还是100px),只是它们长度的比例不一样了,经放大后的1px要长于普通的1px。这样来实现放大的效果。

scale()的默认值为1,即正常比例,设置成2,即2倍比例,放大成2倍,以此类推。

值得注意的是,转换的中心点默认是元素的中心,需要设置css样式transform-origin: 0 0;来使元素以左上角为源点进行缩放(本文直接写在了样式表中)。

4.2 缩放控制

缩放当然是使用鼠标滚轮来控制,对应的事件是wheel

其中事件对象中的deltaY属性就是判断滚轮滚动方向的依据:大于0是向下滚动,小于0是向上滚动

4.3 代码

// 缩放大小
var scale = 1
pic.addEventListener('wheel', function(e){
  const { deltaY } = e

  if(deltaY > 0){
    // 向下滚 缩小
    scale -= 0.2
    // 缩小最小值为1(可根据需求更改)
    if(scale < 1) scale = 1;
    pic.style.transform = `scale(${scale})`
  } else if(deltaY < 0){
    // 向上滚 放大
    scale += 0.2
    // 放大最大值为2(可根据需求更改)
    if(scale > 2) scale = 2;
    pic.style.transform = `scale(${scale})`
  }
  // 阻止默认行为,防止缩放图片时使页面滚动
  e.preventDefault()
})

5、图片放大后拖拽pic

我们再看一眼布局:

<div class="content"> 
    <div class="pic">
        <div class="point"></div>
    </div> 
</div>

默认情况下,pic的大小是与content相同的,但是当我们使用scale进行放大时,pic就会溢出content,导致content出现滚动条(即使pic的px值不变)。

我们希望实现对放大后的pic进行拖拽,其实可以操作content的滚动条,来实现拖拽了pic的假象。

这里我们操作滚动条需要用到的属性和方法有:

事件对象属性:e.clientXe.clientY:鼠标相对于浏览器可视区域的距离 元素对象属性:el.scrollLeftel.scrollTop:元素向左(向下)滚动的距离

这里控制content滚动的方法其实就是之前文章中的思路一

  1. 记录mousedown时,鼠标的原始位置
  2. 鼠标移动时,计算鼠标移动的距离,然后让content也滚动相同的距离。
// pic滚动的开关
var isPicDragging = false
var content = document.getElementsByClassName('content')[0]
// mousedown时鼠标点位
var cursorPosition = {x: 0, y: 0}
// mousedown时,x轴滚动条滚动的距离
var originScrollLeft= 0
var originScrollTop= 0

pic.addEventListener('mousedown', function(e){
  isPicDragging = true
  // 记录鼠标相对于浏览器可视区域的x,y距离
  cursorPosition.x = e.clientX
  cursorPosition.y = e.clientY
  // 记录mousedown时,content已经滚动的距离
  originScrollLeft = content.scrollLeft
  originScrollTop = content.scrollTop
})

// 为了防止鼠标移动时,移动到pic的范围之外,
// 需要将事件绑定在document上
document.addEventListener('mousemove', function(e){
    if(isPicDragging){
      // 计算鼠标相对于原始位置的偏移量
      var changeX = e.clientX - cursorPosition.x
      var changeY = e.clientY - cursorPosition.y
      
      // 让content也滚动相同的偏移量
      // 值得注意的是,鼠标移动的方向数值与scrollLeft移动方向的数值是相反的,
      // 所以不是加上changX,而是减去changeY。scrollTop同理。
      content.scrollLeft = originScrollLeft - changeX
      content.scrollTop = originScrollTop - changeY
    }
})

document.addEventListener('mouseup', function (e) {
    isPicDragging = false
})

6、图片放大时拖拽点位

点位拖拽在之前的文章中已经实现了,但是这次需要在pic放大的情况下进行拖拽,情况就变得不一样了。

回顾一下之前拖拽点位的实现:

cursorPosition是鼠标在mousedown时的原始位置,pointPosition是点位在mousedown时的style.left和style.top 计算鼠标移动的距离,然后使point的left和top也移动相同的距离。

document.addEventListener('mousemove', function (e) {
  if (isDragging) {
    var changeX = e.clientX - cursorPosition.x
    var changeY = e.clientY - cursorPosition.y
    point.style.left = pointPosition.x + changeX + 'px'
    point.style.top = pointPosition.y + changeY + 'px'
  }
})

但是当图片使用scale放大之后,就不能直接相加了。

上述代码中的changeX和changeY是鼠标在document上正常比例的移动距离。但是假设我们使用transform: scale(2)对pic进行了放大,那么point的位移应该移动的位移应该是changeX/2和changeY/2。

所以point进行拖拽时应该注意pic的缩放比例。

完善后的代码如下:

var scale = 1 //scale为pic的缩放比例
document.addEventListener('mousemove', function (e) {
  if (isDragging) {
    var changeX = e.clientX - cursorPosition.x
    var changeY = e.clientY - cursorPosition.y
    // 需要注意scale的缩放比例
    point.style.left = pointPosition.x + changeX/scale + 'px'
    point.style.top = pointPosition.y + changeY/scale + 'px'
  }
})

7、完整代码

完整功能的代码实现如下:

  <style>
    .content {
      width: 600px;
      height: 400px;
      margin: auto;
      border: 1px solid pink;
      overflow: hidden;
    }
    .pic {
      position: relative;
      width: 100%;
      height: 100%;
      background: url('https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg');
      background-size: 100% 100%;
      transform-origin: 0 0;
    }
    .point {
      position: absolute;
      width: 50px;
      height: 50px;
      background-color: pink;
    }
  </style>
  <div class="content">
    <div class="pic">
      <div class="point"></div>
    </div>
  </div>
  <script type="text/javascript">
    var point = document.getElementsByClassName('point')[0]
    var pic = document.getElementsByClassName('pic')[0]

    // point是否正在拖拽
    var isDragging = false
    // 鼠标点位
    var cursorPosition = { x: 0, y: 0 }
    // point的left和top
    var pointPosition = { x: 0, y: 0 }

    // 开始拖拽point
    point.addEventListener('mousedown', function (e) {
      // 阻止事件传播,否则pic绑定的事件处理函数也会执行
      e.stopPropagation()
      isDragging = true
      cursorPosition.x = e.clientX
      cursorPosition.y = e.clientY
      pointPosition.x = parseFloat(point.style.left || 0)
      pointPosition.y = parseFloat(point.style.top || 0)
    })

    // 图片是否正在拖拽
    var isPicDragging = false
    var content = document.getElementsByClassName('content')[0]
    // mousedown时,content的原始scrollLeft
    var originScrollLeft= 0
    var originScrollTop= 0

    // pic开始拖拽
    pic.addEventListener('mousedown', function(e){
      isPicDragging = true
      // 记录鼠标相对于浏览器可视区域的x,y距离
      cursorPosition.x = e.clientX
      cursorPosition.y = e.clientY
      // 记录mousedown时,content已经滚动的距离
      originScrollLeft = content.scrollLeft
      originScrollTop = content.scrollTop
    })

    // 鼠标移动,判断是否正在拖拽,是哪个元素正在拖拽
    // 为了防止鼠标移动时,移动到元素的范围之外,
    // 需要将事件绑定在document上
    document.addEventListener('mousemove', function (e) {
      // point正在拖拽
      if (isDragging) {
        // 计算鼠标相对于原始位置的偏移量
        var changeX = e.clientX - cursorPosition.x
        var changeY = e.clientY - cursorPosition.y

        // 这里注意scale缩放比例
        point.style.left = pointPosition.x + changeX / scale + 'px'
        point.style.top = pointPosition.y + changeY /scale + 'px'

        // 下面的操作是防止拖拽的div越过外层盒子
        if (point.offsetLeft < 0) point.style.left = 0;
        if (point.offsetTop < 0) point.style.top = 0;
        if (point.offsetLeft > (pic.offsetWidth - point.offsetWidth)) {
          point.style.left = pic.offsetWidth - point.offsetWidth + 'px';
        }
        if (point.offsetTop > (pic.offsetHeight - point.offsetHeight)) {
          point.style.top = pic.offsetHeight - point.offsetHeight + 'px';
        }
      }
      
      // pic正在拖拽
      if(isPicDragging){
        // 计算鼠标相对于原始位置的偏移量
        var changeX = e.clientX - cursorPosition.x
        var changeY = e.clientY - cursorPosition.y
        
        // 让content也滚动相同的偏移量
        // 值得注意的是,鼠标移动的方向数值与scrollLeft移动方向的数值是相反的,
        // 所以不是加上changX,而是减去changeY。scrollTop同理。
        content.scrollLeft = originScrollLeft - changeX
        content.scrollTop = originScrollTop - changeY
      }

    })

    // 鼠标抬起,停止拖拽
    document.addEventListener('mouseup', function (e) {
      isDragging = false
      isPicDragging = false
    })

    // 背景图片缩放比例
    var scale = 1
    pic.addEventListener('wheel', function(e){
      const { deltaY } = e

      if(deltaY > 0){
        // 向下滚 缩小
        scale -= 0.2
        // 缩小最小值为1(可根据需求更改)
        if(scale < 1) scale = 1;
        pic.style.transform = `scale(${scale})`

      } else if(deltaY < 0){
        // 向上滚 放大
        scale += 0.2
        // 放大最大值为2(可根据需求更改)
        if(scale > 2) scale = 2;
        pic.style.transform = `scale(${scale})`
      }
      // 阻止默认行为,防止缩放图片时使页面滚动
      e.preventDefault()
    })
  </script>
转载自:https://juejin.cn/post/7249758081293533239
评论
请登录