週末嘗試用vue寫了一個android版QQ客戶端的Web App demo。暫時只寫了首頁消息這個界面,幾個小圖標是用sketch畫的svg。側滑欄的背景圖終於祭出了個人ps。底部導航圖標由於實在不太會用sketch,先放棄了。javascript
請使用最新版chrome + mobile emulator 訪問vue
想來說下手勢識別的實現的思路,代碼在這裏(目前還不完善)android
listen (type, $el, func) {
let rect
if (!$el) {
rect = null
} else {
let tmp = $el.getBoundingClientRect()
rect = {
x: tmp.left,
y: tmp.top,
width: tmp.width,
height: tmp.height
}
}
let token = Math.random() * 9999 + type
this.queue[type].push({
$el: $el,
token: token,
func: func,
rect: rect
})
}複製代碼
首先個人思路是監聽窗口的touch事件,以組件自身的像素區域來派發事件,調用listen的時候傳入想要監聽事件的類型和指望觸發該事件的Element, 還有就是加上觸發的函數回調。若是不傳入Element的話,就會接收到所有的事件觸發。git
這裏首先是註冊訂閱該組件固然的像素區域,那麼若是滑動頁面以後改怎麼辦?github
update () {
let keys = Object.keys(this.queue)
let tmp
for (let key of keys) {
this.queue[key].forEach(item => {
if (item.$el) {
tmp = item.$el.getBoundingClientRect()
item.rect = {
x: tmp.left,
y: tmp.top,
width: tmp.width,
height: tmp.height
}
}
})
}
}複製代碼
這裏的update就須要在body 或者其上級節點 scroll的時候調用來更新組件的註冊區域。chrome
init () {
if (this._hasTouch) {
window.document.body.addEventListener('touchstart', this.touchStart.bind(this), false)
window.document.body.addEventListener('touchmove', this.touchMove.bind(this), false)
window.document.body.addEventListener('touchend', this.touchEnd.bind(this), false)
window.document.body.addEventListener('touchcancel', this.touchCancel.bind(this), false)
}
}複製代碼
這裏是註冊全局的touch監聽dom
touchStart (e) {
this.state.swiping = true
this.touch.start = this.getPosition(e)[0]
}
touchMove (e) {
this.touch.end = this.getPosition(e)[0]
this.notice('swiping', this.getRect(this.touch.end, this.touch.start))
}複製代碼
這裏是對swiping 在touchmove時不斷派發事件,可能須要優化一下加上節流。若是在加上滑動路徑和趨勢分析就更好了。svg
touchEnd (e) {
this.notice('swiped', this.getRect(this.touch.end, this.touch.start))
this.state.swiping = true
let angle = this.getAngle(this.touch.end, this.touch.start)
if (angle > -45 && angle < 45) {
this.notice('swipe-left', this.getRect(this.touch.end, this.touch.start))
}
if (angle < -135 || angle > 135) {
this.notice('swipe-right', this.getRect(this.touch.end, this.touch.start))
}
if (angle > 45 && angle < 135) {
this.notice('swipe-up', this.getRect(this.touch.end, this.touch.start))
}
if (angle < -45 && angle > -135) {
this.notice('swipe-down', this.getRect(this.touch.end, this.touch.start))
}
}
touchCancel (e) {
console.log(e)
}複製代碼
這裏就是對一次touch過程的方向判斷,而後派發事件函數
notice (type, rect) {
let rectTmp = {}
let once = true
let tmpFunc = null
for (let i = 0, len = this.queue[type].length; i < len; i++) {
rectTmp = this.queue[type][i].rect
if (!rectTmp) {
tmpFunc = this.queue[type][i].func
} else {
if (this.rectIn(rect, rectTmp) && !this.state.global) {
this.queue[type][i].func(this.touch.start, this.touch.end)
once = false
}
}
}
if (once && tmpFunc) {
tmpFunc(this.touch.start, this.touch.end)
}
}複製代碼
這裏是派發的函數實現,爲了此次demo的實現,我把全局手勢優先級設置爲低於指定區域的優先級。
export default {
props: {
message: Object
},
data () {
return {
closeToken: '',
swiping: 0,
menu: false
}
},
ready () {
this.add()
},
methods: {
add () {
this.$swipe.listen('swipe-left', this.$el, () => {
this.swiping = -160
this.menu = true
this.closeToken = this.$swipe.listen('swipe-right', this.$el, () => {
this.swiping = 0
this.menu = false
this.$swipe.leave('swipe-right', this.closeToken)
})
})
this.$swipe.listen('swiping', this.$el, (start, end) => {
if (start.x > end.x && this.swiping > -160) {
this.swiping = -(start.x - end.x)
}
})
this.$swipe.listen('swiped', this.$el, (start, end) => {
if (!this.menu) {
this.swiping = 0
}
})
}
}
}複製代碼
這是在消息組件所有代碼。 目前還沒(bu)計(xiang)劃(xie)整合爲vue指令。