8

功能:图片预览组件,支持双手指放大/缩小,双击放大/缩小,单击消失隐藏。
注:touch事件请手机预览

文档地址http://www.ml-ui.com/#/docs/i...

源码分享

组件参数

    data() {
      return {
        loading: 2, // 1成功 2正在加载 3error失败
        dragObject: {},
        starLine: 0, // 初始俩个点第1次距离
        zoom: 1, // 放缩比
        compress: null, // 最大压缩比
        elWidth: null, // 外层宽度
        elHeight: null, // 外层高度
        imgWidth: null, // 当前图片宽度
        imgHeight: null, // 当前图片高度
        mTop: 0,  // margin-top 值
        mLeft: 0, // margin-left 值
        isTouch: false, // 是否touch
        scrolling: false, // 是否放缩
        animating: false, // 是否动画中
        isTwoClick: false,  // 是否双击
        startTime: null, // 第一次时间
        timeFunc: null, // 定时器
      }
    },

组件初始化init(),dom绑定事件,区分滑动事件、单击、双击事件

 /**
  * 初始化事件
  * @param {Object} $el 当前DOM
  */
  init($el) {
    this.elWidth = document.documentElement.clientWidth
    this.elHeight = document.documentElement.clientHeight
    $el.addEventListener('touchstart', (e) => {
      if (this.animating) return
      // 记录点击时间和第二次点击的时差
      if (this.startTime) this.dragObject.duration = new Date() - this.startTime
      if (!this.startTime) this.startTime = new Date()
      if (this.dragObject.duration) this.startTime = null
      this.dragObject.startTime = new Date()
      this.touchStart(e)
      if (!this.isTouch) this.isTouch = e.touches.length > 1
    })
    $el.addEventListener('touchmove', (e) => {
     ....
    })
    $el.addEventListener('touchend', (e) => {
      ....
    }
 })

图片加载完后执行imgLoad()事件,设置图片初始显示的宽imgWidthimgHeight以及放缩比compress

 /**
  * 图片加载成功事件
  */
  imgLoad(e) {
    const compress = e.target.width / this.elWidth
    const scale = this.scale > 3 ? this.scale : 3
    this.imgWidth = compress > 1 ? this.elWidth : e.target.width
    this.imgHeight = compress > 1 ? e.target.height / compress : e.target.height
    this.compress = compress > scale ? compress : scale
    this.loading = 1
  },

滑动开始,区分放缩事件or滑动事件

 $el.addEventListener('touchmove', (e) => {
   e.preventDefault()
   if (this.animating) return
   this.isTouch = true
   if (e.touches.length === 2) this.touchTwoMove(e)
   if (e.touches.length === 1) this.touchMove(e)
 })

touchStart()滑动开始事件

/**
* 触发开发
*/
touchStart(e) {
  const touch = e.touches[0]
  if (e.touches.length > 1) {
    // 放缩初始俩点的距离
    const touch2 = e.touches[1]
    const diffX = touch.pageX - touch2.pageX
    const diffY = touch.pageY - touch2.pageY
    this.starLine = Math.pow((diffX * diffX + diffY * diffY), 0.5)
  }
  // 缓存初始的margin-left和margin-top的比值
  this.dragObject.topThan = this.mTop !== 0 ? this.mTop / this.reckonHeight(this.zoom) : 0
  this.dragObject.leftThan = this.mLeft !== 0 ? this.mLeft / this.reckonWidth(this.zoom) : 0
  this.dragObject.startLeft = touch.pageX
  this.dragObject.startTop = touch.pageY
  this.dragObject.zoom = this.zoom
} 

touchMove()触发滑动事件

/**
 * 触发移动
 */
touchMove(e) {
  if (this.scrolling) return
  const dragObject = this.dragObject
  const touch = e.touches[0]
  let xx = touch.pageX - (dragObject.oldLeft || dragObject.startLeft)
  let yy = touch.pageY - (dragObject.oldTop || dragObject.startTop)
  dragObject.oldLeft = touch.pageX
  dragObject.oldTop = touch.pageY
  if (this.imgWidth * dragObject.zoom > this.elWidth) {
    if (Math.abs(this.mLeft) > this.reckonWidth(dragObject.zoom)) xx *= 0.3
    this.mLeft += xx
  }
  if (this.imgHeight * dragObject.zoom > this.elHeight) {
    if (Math.abs(this.mTop) > this.reckonHeight(dragObject.zoom)) yy *= 0.3
    this.mTop += yy
  }
},

touchEnd()触发结束事件,计算滑动后的位置,并执行动画事件continueTranslate()

/**
 * 解发结束
 * @param {Number} dragDuration 间隔
 */
touchEnd(dragDuration) {
  ....
  this.continueTranslate(top, left, this.mLeft, this.mTop)
},

continueTranslate()动画事件,借助requestAnimationFrame()方法

/**
 * 继续执行一段距离滑行
 * @param {Number} top 将要到达的top值
 * @param {Number} left 将要到达的left值
 * @param {Number} oldX  动画执行前left值
 * @param {Number} oldY 动画执行前top值
 */
continueTranslate(top, left, oldX, oldY) {
  this.animating = true
  const xx = left - oldX
  const yy = top - oldY
  let diffX = 0
  let diffY = 0
  let ALPHA = 0.88
  const animationLoop = () => {
    ALPHA *= 0.95
    const resultX = Math.abs(diffX - xx) < 1
    const resultY = Math.abs(diffY - yy) < 1
    if (resultX && resultY) {
      this.animating = false
      this.mLeft = left
      this.mTop = top
    } else {
      diffX = diffX * ALPHA + (1 - ALPHA) * xx
      diffY = diffY * ALPHA + (1 - ALPHA) * yy
      if (!resultX) this.mLeft = oldX + diffX
      if (!resultY) this.mTop = oldY + diffY
      animationFrame(animationLoop)
    }
  }
  animationLoop()
},

touchTwoMove()放缩滑动事件

/**
 * 放缩移动
 */
touchTwoMove(e) {
  this.scrolling = true
  const dragObject = this.dragObject
  const touch = e.touches[0]
  const touch2 = e.touches[1]
  const diffX = touch.pageX - touch2.pageX
  const diffY = touch.pageY - touch2.pageY
  const line = Math.pow((diffX * diffX + diffY * diffY), 0.5) - this.starLine
  let zoom = Number(dragObject.zoom + (line / 2 / 75))
  if (zoom < 1) zoom = 1 - (1 - zoom) * 0.15
  if (zoom > this.compress) zoom = this.compress + (zoom - 3) * 0.2
  this.zoom = zoom
  this.mLeft = dragObject.leftThan * this.reckonWidth(zoom)
  this.mTop = dragObject.topThan * this.reckonHeight(zoom)
},

touchEnd()事件,当touches.length===0才执行事件,区分滑动事件、单击、双击事件。

$el.addEventListener('touchend', (e) => {
  if (this.animating || e.touches.length > 0) return
  const dragObject = this.dragObject
  // 单次间隔时长
  const duration = new Date() - this.dragObject.startTime
  let zoom = this.zoom
  if (this.isTouch) {
    // 滑动事件
    clearTimeout(this.timeFunc)
    this.timeFunc = null
    // 滑动执行事件
    if (!this.scrolling) this.touchEnd(duration)
    // 放缩执行事件
    if (this.scrolling) {
      if (zoom > this.compress) zoom = this.compress
      if (zoom < 1) zoom = 1
      if (dragObject.leftThan) this.mLeft = dragObject.leftThan * this.reckonWidth(zoom)
      if (dragObject.topThan) this.mTop = dragObject.topThan * this.reckonHeight(zoom)
    }
    this.isTouch = false
    this.zoom = zoom
    this.scrolling = false
    this.starLine = 0
    this.dragObject = {}
  } else {
    // 俩次点击时长<250双击
    if (dragObject.duration && dragObject.duration < 250) {
      // 双击事件
      clearTimeout(this.timeFunc)
      this.timeFunc = null
      this.zoom = zoom > 1 ? 1 : 2
      this.mLeft = this.mTop = 0
      this.dragObject = {}
    } else {
      // 单击事件
      if (this.timeFunc) return
      this.timeFunc = setTimeout(() => {
        this.timeFunc = null
        this.dragObject = {}
        this.startTime = null
        this.zoom = 1
        this.mLeft = this.mTop = 0
        this.$emit('input', false)
      }, 250)
    }
  }
})

结束...

图片描述

效果图
图片描述

扫码预览
图片描述

欢迎buging

同时推推自己开源的ml-ui移动端组件库
https://segmentfault.com/a/11...
git地址
ml-ui线上地址


zdliuccit
168 声望4 粉丝

清月映山,好风如水,枯崖老松成古趣,尘外云通天地衢,一盏茶、一浮世,对饮人间一场局。