移動端Vue.js的圖片預覽組件,支持放縮、滑動功能!

功能:圖片預覽組件,支持雙手指放大/縮小,雙擊放大/縮小,單擊消失隱藏。
注:touch事件請手機預覽git

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

源碼分享

組件參數

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)
    }
  }
})

結束...segmentfault

圖片描述

效果圖
圖片描述緩存

掃碼預覽
圖片描述dom

歡迎bugingoop

同時推推本身開源的ml-ui移動端組件庫
https://segmentfault.com/a/11...
git地址
ml-ui線上地址動畫

相關文章
相關標籤/搜索