原生js实现图片滚轮/点击放缩及拖拽

205 阅读4分钟

前言

实现缩放和拖拽,一般我们使用transform 的scale进行放缩,使用translate进行平移,还可以使用定位进行拖拽,综合考虑,此处统一使用transform

获取元素transform属性值

window中的getComputedStyle()方法可以获取元素当前的样式

即可通过getComputedStyle(dom).transform获取transform属性值
  • dom没有该属性,返回:'none'

  • 有该属性,返回一个matrix矩阵

    'matrix(x向缩放倍数,旋转,拉伸,y向缩放倍数,x偏移量,y偏移量)',再通过split(',')将各个属性值转为数组

为什么不用DOM.style.transform来获取tranform,因为DOM.style.transform会返回css字符串代码,如:

如果只有偏移值那么会返回'translate(100px, 100px)', 如果有偏移值和缩放值时会返回'translate(100px, 100px) scale(2)' 因此这样获取具体的缩放比例、偏移值都不太方便

拖拽步骤

  1. 鼠标摁下onmousedown,记录鼠标和图片的位置,从而计算鼠标相对图片的坐标
鼠标相对图片的坐标:
鼠标相对浏览器(有效区域)左侧的位置-图片的translateX和translateY的值
  1. 监听鼠标的移动onmousemove,当鼠标移动时被拖拽的元素跟随鼠标移动,根据鼠标当前坐标和鼠标相对图片的距离计算出图片当前的坐标

图片当前的translateX和translateY的值:
        
鼠标当前相对浏览器(有效区域)位置-鼠标相对图片的距离 计算出图片移动的距离

代码实现

html部分

picView.html

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>图片查看器</title>
  <link rel="stylesheet" href="./picView.css">
</head>
<body>
  <div class="img">
    <div class="img_top">
        <div class="img_tool">
            <!-- <div class="zoomCount" onbind="toPercent(multiples)"></div> -->
            <div class="icon bigger_icon" onclick="biggerImg()"></div>
            <div class="icon smaller_icon" onclick="smallerImg()"></div>
            <div class="returnImg" onclick="returnImg()">还原</div>
        </div>
    </div>
    <div class="img_content" onmousedown="dragImg()" onmousewheel="scrollFunc()">
      <!-- 注意的是img标签的draggable属性,draggable属性在img、a标签中默认true,属于浏览器默认拖动,会跟自己写的事件冲突从而导致拖不动、拖动残影等等奇怪的问题
                    因此先置成false -->  
      <img id="bigImg" src="./airplane.png" alt="" draggable="false">
    </div>
    <!-- <div class="left" onclick="afterImg()"></div>
    <div class="right" onclick="beforeImg()"></div> -->
  </div>
  <script src="./picView.js"></script>
</body>

js部分

picView.js

  // 图片放大 缩小
  let multiples = 1
  // 图片拖拽后的偏移
  let newTransX = 0
  let newTransY = 0
  // 鼠标相对图片的位置
  let disX = 0
  let disY = 0
  /**
   * 鼠标摁下onmousedown,触发dragImg事件,记录鼠标和图片的位置,从而计算鼠标相对图片的坐标
   *     鼠标相对图片的坐标:鼠标相对浏览器(有效区域)左侧的位置-图片的translateX和translateY的值
   * 
   * 监听鼠标的移动onmousemove,当鼠标移动时被拖拽的元素跟随鼠标移动,根据鼠标当前坐标和鼠标相对图片的距离计算出图片当前的坐标
   *     图片当前的translateX和translateY的值:鼠标当前相对浏览器(有效区域)位置-鼠标相对图片的距离 计算出图片移动的距离
   * 
   * 是否存在边界限制 暂无
   */
  // 改变元素位置的方式有:定位、translate偏移

  function dragImg($event) {
    let e = $event || event
    let dragEl = document.querySelector('#bigImg')
    getTransInfo(e)
    document.onmousemove = (e) => {
      newTransX = e.clientX - disX
      newTransY = e.clientY - disY
      dragEl.style.transform = `matrix(${multiples}, 0, 0, ${multiples}, ${newTransX}, ${newTransY})`
    }
    document.onmouseup = () => {
      document.onmousemove = null
    }
  }

  function getTransInfo(e) {
    // 获取元素当前的宽高
    let dragEl = document.querySelector('#bigImg')
    // 获取元素当前的偏移量
    let translateStr = getComputedStyle(dragEl).transform.split(',')
    console.log(translateStr)
    // 获取translateX
    let transX= isNaN(+translateStr[translateStr.length - 2]) ? 0 : +translateStr[translateStr.length - 2] 
    // 获取translateY
    let transY= isNaN(+translateStr[translateStr.length - 1].split(')')[0]) ? 0 : +translateStr[translateStr.length - 1].split(')')[0] 
    // 记录鼠标的起始位置
    //鼠标指针相对于浏览器页面(或当前窗口)的水平坐标
    let startX = e.clientX 
    //鼠标指针相对于浏览器页面(或当前窗口)的y坐标
    let startY = e.clientY

    disX = startX - transX //鼠标相对于图片左侧的水平距离
    disY = startY - transY
  }

  // 鼠标滚轮缩放
  document.scrollFunc = function(e) {
    e = e || window.event
    // 火狐下没有wheelDelta,用detail代替,由于detail值的正负和wheelDelta相反,所以取反
    e.delta = e.wheelDelta || -e.detail
    e.preventDefault()
    if (e.delta > 0) {
      //当滑轮向上滚动时
      biggerImg()
    }
    if (e.delta < 0) {
      //当滑轮向下滚动时
      smallerImg()
    }
  }

  function smallerImg() {
    let img = document.getElementById('bigImg')
    multiples = Number(multiples-0.1)
      if(multiples > 0.4) {
        img.style.transform = `matrix(${multiples}, 0, 0, ${multiples}, ${newTransX}, ${newTransY})`
      } else {
        multiples = 0.5
      }
  }

  function biggerImg() {
    let img = document.getElementById('bigImg')
    // 数字相加一定要注意字符串,一定要防止数字变成字符串数字,请刻入DNA
    multiples = Number(multiples+0.1) 
    if(multiples < 2.1) {
      img.style.transform = `matrix(${multiples}, 0, 0, ${multiples}, ${newTransX}, ${newTransY})`
    } else {
      multiples = 2
    }
  }

css部分

picView.css

 *{
   box-sizing: border-box;
 }

 body {
  margin: 0;
  /* -webkit-user-select: none;
  margin: 0 auto; */
  /* background-color: #0d141d; */
  font-family: Microsoft YaHei;
  overflow: hidden;
  font-size: 14px;
}
 .img {
  position: relative;
  height: 100%;
  width: 100%;
}

 .img .img_top{
  position: fixed;
  z-index: 999;
  height: 35px;
  line-height: 35px;
  width: 100%;
  background-color: #0070B8;
  color: #fff;
}

 .img .img_top .img_tool{
  display: flex;
  float: right;
  margin-right: 200px;
  height: 25px;
  margin-top: 4px;
  line-height: 35px;
  color: #fff;
}

 .img .img_top .img_tool .icon{
  display: inline-block;
  width: 25px;
  height: 25px;
  cursor: pointer;
}

 .img .img_top .img_tool .smaller_icon{
  background: url(./small.png);
  background-size: 100% 100%;
  margin-right: 40px;

}

 .img .img_top .img_tool .bigger_icon{
  background: url(./big.png);
  background-size: 100% 100%;
  margin-right: 40px;
}

 .img .img_content {
  position: fixed;
    transform: translate(50%, 50%);
    margin-top: 35px;
}

 .img img {
  /* display: block;
  margin: 0 auto;
  height: 100%; */
}

 .img .img_top .title{
  display: inline-block;
  font-size: 16px;
  text-indent: 1em;
  margin-right: 100px;
}

 .img .left,
 .img .right {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 40px;
  height: 40px;
  background-color: rgba(33, 33, 33, 0.3);
  border-radius: 50%;
  background-size: 60% 60%;
  background-repeat: no-repeat;
  background-position: center center;
  background-image: url(./indexpre.png);
  cursor: pointer;
}

 .img .left {
  left: 10px;
}

 .img .right {
  right: 10px;
  background-image: url(./indexnext.png);
}

参考:juejin.cn/post/693843…