功能:图片预览组件,支持双手指放大/缩小,双击放大/缩小,单击消失隐藏。
注: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()
事件,设置图片初始显示的宽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
},
滑动开始,区分放缩事件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线上地址