功能:圖片預覽組件,支持雙手指放大/縮小,雙擊放大/縮小,單擊消失隱藏。
注: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()
事件,設置圖片初始顯示的寬imgWidth
高imgHeight
以及放縮比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 },
$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線上地址動畫