图片或内容区域缩放及滚动

因为最近公司的项目上有用到图片缩放及滚动的效果,而swiper在该项目上有一些问题,所以就决定自己写一个图片的缩放和滚动效果,以及回弹效果, 用的是面向对象的方式封装的, 因为是移动端的,所以用的

touch方法,HTML结构样式先放出来

  <div id="box">
    <div id="mask"></div>
    <div id="content">
      <img src="./11.jpg" alt="">
      <div>
        <table>
          <tr>
            <th>姓名</th>
            <th>手机号</th>
            <th>年龄</th>
            <th>性别</th>
          </tr>
          <tr id="tr">
            <td>六六六</td>
            <td>18437928787</td>
            <td>18</td>
            <td></td>
          </tr>
        </table>
      </div>
    </div>
  </div>
    * {
      padding: 0;
      margin: 0;
    }
    body,html {
      width: 100%;
      height: 100%;
    }
    #box {
      width: 100%;
      height: 100%;
      overflow: hidden;
      position: relative;
    }
    #content {
      position: absolute;
      top: 0;
      left: 0;
    }
    #mask {
      position: absolute;
      top: 0;
      left: 0;
      z-index: 10;
      width: 100%;
      height: 100%;
    }
    img {
      width: 100%;
      display: block;
    }

主要的缩放和滑动区域是content盒子,所以content里面随便放什么都无所谓, 这里的mask是一个遮罩层,层级大于content, 所有事件都绑定在了mask上, 这样做是因为, 如果content里的元素又小又多, 那么当双指进行缩放时,两个手指出碰到的不是同一元素, 获取到的e.target的目标会不一样,这样就会出问题, 所以设置这样一个遮罩层

    function Scroll(ele) {
      this.box = document.querySelector(ele)
      this.content = document.querySelector('#content')
      this.mask = document.querySelector('#mask')
      this.data = {
        width: this.content.offsetWidth,  //获取内容区域的初始宽度
        height: this.content.offsetHeight,  //获取内容区域的初始高度
        currentWidth: this.content.offsetWidth,   //获取内容区域当前宽度  
        currentHeight: this.content.offsetHeight,   //获取内容区域当前高度
        startXA: 0,   //第一根手指的起始位置
        startYA: 0,   
        endXA: 0,   //第一根手指的结束位置
        endYA: 0,
        startXB: 0,   //第二根手指的起始位置
        startYB: 0,
        endXB: 0,   //第二根手指的结束位置
        endYB: 0,
        currentMultiple: 1,   //当前的缩放倍数
        multiple:1,   //缩放倍数
        positionY: 0,   //在Y轴的位置
        positionX: 0,   //在X轴的位置
        startTime: 0,   //手指触碰的起始时间
        endTime: 0,   //手指离开的结束时间
        centreX: 0,   //缩放位置的X轴坐标
        centreY: 0,   //缩放位置的Y轴坐标
        bool: false,    //用来判断是否是双指操作
        num: 0,   //用来判断屏幕上的手指数
        timer: null   //定时器
      }
      
      this.mask.ontouchstart = (e) => {
        this.touchBegin(e)
      }
      this.mask.ontouchmove = (e) => {
        this.touching(e)
      }
      this.mask.ontouchend = (e) => {
        this.touchFinish(e)
      }
    }

当触摸事件触发时,调用touchBegin事件

    Scroll.prototype.touchBegin = function(e) {
      this.data.num++  //当调用这个方法时,num自增
      e.preventDefault()  //阻止浏览器滑动
      switch (e.targetTouches.length) {
        case 1:
          this.data.startXA = e.targetTouches[0].pageX  
          this.data.startYA = e.targetTouches[0].pageY
          this.data.startTime = new Date().getTime()
          break
        case 2:
          this.data.bool = true   //如果是双指操作,将bool的值置为true
          this.data.startXA = e.targetTouches[0].pageX
          this.data.startYA = e.targetTouches[0].pageY
          this.data.startXB = e.targetTouches[1].pageX
          this.data.startYB = e.targetTouches[1].pageY
          this.data.centreY = Math.abs((-this.data.positionY + this.data.startYA) - (-this.data.positionY + this.data.startYB)) / 2 + (-this.data.positionY + this.data.startYA)    
          this.data.centreX = Math.abs((-this.data.positionX + this.data.startXA) - (-this.data.positionX + this.data.startXB)) / 2 + (-this.data.positionX + this.data.startXA)
      }
    }

当手指滑动时, 触发touching事件

    Scroll.prototype.touching = function(e) {
      switch (e.targetTouches.length) {
        case 1:
          if(!this.data.bool) {   //如果bool是false, 也就是说不是双手操作,或者刚刚结束双手操作,但只离开了一只手指
            this.data.endXA = e.targetTouches[0].pageX
            this.data.endYA = e.targetTouches[0].pageY
            let x= this.data.endXA - this.data.startXA+this.data.positionX    //content应在X轴上移动的距离
            let y= this.data.endYA - this.data.startYA+this.data.positionY    //content应在Y轴上移动的距离
            if(this.data.currentWidth === this.data.width) {    //判断当前的宽度与初始化时的宽度大小是否相等 , 如果相等,也就是说明没有进行缩放操作, 那么就只在Y轴进行移动
              this.content.style.transform = "translate(0,"+y+"px) scale("+this.data.multiple+")"
            } else {    //否则,就在X,Y轴上进行移动
              this.content.style.transform = "translate("+x+"px,"+y+"px) scale("+this.data.multiple+")"
            }
          }
          break
        case 2:
          this.data.endXA = e.targetTouches[0].pageX
          this.data.endYA = e.targetTouches[0].pageY
          this.data.endXB = e.targetTouches[1].pageX
          this.data.endYB = e.targetTouches[1].pageY
          this.content.style.transformOrigin = this.data.centreX+"px "+this.data.centreY+"px"   //设置缩放中心点坐标
          let multipleOne = Math.sqrt(Math.pow(Math.abs(this.data.startXA - this.data.startXB),2) + Math.pow(Math.abs(this.data.startYA - this.data.startYB),2))    //获取两只手指的起始直线距离
          let multipleTwo = Math.sqrt(Math.pow(Math.abs(this.data.endXA - this.data.endXB),2) + Math.pow(Math.abs(this.data.endYA - this.data.endYB),2))    //获取滑动时两只手指的直线距离
          let multiple = multipleTwo / multipleOne * this.data.currentMultiple    //获取放大倍数
          if(multiple < 1 ) {   //规定放大倍数,不能大于2且不能小于1
            multiple = 1
          }
          if(multiple > 2 ) {
            multiple = 2
          }
          this.data.multiple = multiple
          this.content.style.transform = "translate("+this.data.positionX+"px,"+this.data.positionY+"px) scale("+this.data.multiple+")"
          this.data.currentWidth = this.data.multiple * this.content.offsetWidth 
          this.data.currentHeight = this.data.multiple * this.content.offsetHeight
      }
    }

手指离开时,调用touchFinish事件

    Scroll.prototype.touchFinish = function(e) {
      let limitTop = this.data.centreY*this.data.multiple - this.data.centreY   //获取上下左右的边界值(当图片进行缩放时,边界值也会发生改变,就是缩放中心到边界的距离*缩放倍数 - 缩放中心到边界的距离)
      let limitDown = (this.content.offsetHeight - this.data.centreY)*this.data.multiple - (this.content.offsetHeight - this.data.centreY)
      let limitLeft = this.data.centreX*this.data.multiple - this.data.centreX
      let limitRight = (this.content.offsetWidth - this.data.centreX)*this.data.multiple - (this.content.offsetWidth - this.data.centreX)
      this.data.num--   //num自减
      this.data.currentMultiple = this.data.multiple    
      let down = this.box.offsetHeight - this.content.offsetHeight
      let right = this.box.offsetWidth - this.content.offsetWidth
      this.data.endTime = new Date().getTime()
      this.data.positionY = this.content.style.transform.slice(this.content.style.transform.indexOf(',')+1,this.content.style.transform.indexOf(')')-2)-0
      this.data.positionX = this.content.style.transform.slice(this.content.style.transform.indexOf('(')+1,this.content.style.transform.indexOf(',')-2)-0
      if(this.data.endTime - this.data.startTime <300 && Math.abs(this.data.startYA-e.changedTouches[0].pageY)>50) {    //如果手指滑动时间小于300毫秒,并且距离大于50,则触发滑动缓冲效果
        let p = (this.data.endYA-this.data.startYA) * (1/(this.data.endTime - this.data.startTime)) * 100 + this.data.positionY   //缓冲的距离
        this.Drop(this.content,p,this.data.positionX)   //调用移动动画
      }
      this.data.timer = setTimeout(()=>{    //设置定时器,当手指离开200毫秒后触发边界判断
        if(this.data.positionY >= limitTop ){   //如果大于上边界,调用回弹动画,再判断左右边界是否超出
          this.Drop(this.content, limitTop,this.data.positionX)
          setTimeout(()=> {
            if(this.data.positionX >= limitLeft ){
            this.Drop(this.content,limitTop,limitLeft)
            }
            if(this.data.positionX <= right-limitRight) {
              this.Drop(this.content,limitTop,right-limitRight)
            }
          },200)
        }

        if(this.data.positionY <= -limitDown+down) {
          this.Drop(this.content,-limitDown+down,this.data.positionX)
          setTimeout(()=>{
            if(this.data.positionX >= limitLeft ){
              this.Drop(this.content,-limitDown+down,limitLeft)
            }
            if(this.data.positionX <= right-limitRight) {
              this.Drop(this.content,-limitDown+down,right-limitRight)
            }
          },200)
        }

        if(this.data.positionX >= limitLeft ){
          this.Drop(this.content,this.data.positionY,limitLeft)
        }

        if(this.data.positionX <= -limitRight+right) {
          this.Drop(this.content,this.data.positionY,right-limitRight)
        }
      },200)
      if(!this.data.num) {    //当两只手指都离开后, 将bool设置为false
        this.data.bool = false
      }
    }

移动动画方法

  Scroll.prototype.Drop = function(ele, positionY,positionX) {    //移动动画方式, 传入参数是目标元素, Y轴移动距离, X轴移动距离
      clearInterval(ele.timer);
      ele.timer = setInterval(() => {
        this.data.positionY = this.content.style.transform.slice(this.content.style.transform.indexOf(',')+1,this.content.style.transform.indexOf(')')-2)-0        
        this.data.positionX = this.content.style.transform.slice(this.content.style.transform.indexOf('(')+1,this.content.style.transform.indexOf(',')-2)-0
        var stepY = (positionY - this.data.positionY)/10
        var stepX = (positionX - this.data.positionX)/10
        stepY = stepY > 0 ? Math.ceil(stepY) : Math.floor(stepY); 
        stepX = stepX > 0 ? Math.ceil(stepX) : Math.floor(stepX);
        let longY = this.data.positionY + stepY
        let longX = this.data.positionX + stepX
        ele.style.transform = "translate("+longX+"px,"+longY+"px) scale("+this.data.multiple+")";
        if (Math.abs(positionY -  this.data.positionY) <= Math.abs(stepY) && Math.abs(positionX -  this.data.positionX) <= Math.abs(stepX)) {
          ele.style.transform = "translate("+positionX+"px,"+positionY+"px) scale("+this.data.multiple+")";
          this.data.positionX = positionX
          this.data.positionY = positionY
          clearInterval(ele.timer);
        }
      }, 10)
   }

 

posted @ 2018-08-07 10:37  LuckQ  阅读(609)  评论(0编辑  收藏  举报