類似淘票票 選座功能(svg)

HeCoffee發表於2018-11-15

最近專案需要在移動端做一個可以選座訂票的功能。上網搜了一下沒找到一個現成的庫可以解決需求,所以就自己寫了一個粗糙的版本。

需求

  • 前端展示座位分佈
  • 可拖動 放大縮小 點選選座

技術棧

效果圖

捏放
拖動

詳細效果可以去demo那體驗一下,建議用手機端體驗。

svg

座位分佈圖的SVG是UI畫好後匯出來,並且通過後端介面返回整個SVG標籤以及裡面的內容。前端只要發請求獲得SVG並且插入就好了。插入後需要將已訂購的座位置黑,無法選擇。

hammerjs

hammerjs是一個手勢庫,提供了tap, doubletap, press, pan, swipe, pinch以及 rotate等多種手勢事件,同時也提供豐富的自定義配置可以讓你完成產品各(nao)種(dong)各(da)樣(kai)的需求。

用法

let hammertime = new Hammer(myElement, myOptions);
hammertime.on('pan', function(ev) {
console.log(ev)
})複製程式碼

html 結構

箱子svg-box用以捏放(縮放)

svg用以偏移

<
div class="ticket-map">
<
div class=svg-box>
<
svg>
.....<
/svg>
<
/div>
<
/div>
複製程式碼

初始化

設定一個變數用以記錄手勢操作後的屬性變化

// 記錄位移變數let transform = { 
svgScale: 0.5, // svg 預設縮放 scale: 1, // svg-box 縮放 maxScale: 7, // svg-box 最大縮放 minScale: 1, // svg-box 最小縮放 translateX: 0, // svg X軸偏移 translateY: 0, // svg Y軸偏移 minX: 0, // svg 最小X軸偏移 maxX: 0, // svg 最大X軸偏移 minY: 0, // svg 最小Y軸偏移 maxY: 0 // svg 最大Y軸偏移
}複製程式碼

因為UI提供的SVG是1000*715 略大,為了適應螢幕 作了svgScale: 0.5的縮放。

獲取到SVG後需要進行居中 並且 計算出拖動邊界(minX/Y maxX/Y)

    let svgTarget = document.querySelector('svg')    let svgBox = document.querySelector('.svg-box')    transform.translateX = Math.round((svgBox.clientWidth - svgTarget.clientWidth) / 2) // 垂直居中時的X偏移    transform.translateY = Math.round((svgBox.clientHeight - svgTarget.clientHeight) / 2) // 垂直居中時的Y偏移    transform.minX = transform.translateX - svgBox.clientWidth / 4    transform.maxX = transform.translateX + svgBox.clientWidth / 4    transform.minY = transform.translateY - svgBox.clientHeight / 2.5    transform.maxY = transform.translateY + svgBox.clientHeight / 2.5    svgTarget.style.transform = `translate(${transform.translateX
}
px, ${transform.translateY
}
px) scale(${transform.svgScale
}
)` 複製程式碼

以svg-box的width的4分之一,以及height的2.5分之一作為svg的X Y軸偏移量的極限,以免svg被拖動出螢幕之外。可以根據實際SVG調整分數。

手勢配置

  //  初始化 hammer物件  var svgHam = new Hammer(svgBox)  svgHam.get('pinch').set({ 
enable: true
}) // 返回pinch識別器 設定 可捏放 (放大縮小手勢) 預設不監聽 svgHam.get('pan').set({
direction: Hammer.DIRECTION_ALL
}) // 返回pan識別器 設定拖動方向為 所有方向複製程式碼

監聽svg-box,以免svg移動後手指點選不到觸發不了事件

捏放事件

svgHam.on('pinchstart pinchmove', (e) =>
{
let {
scale, maxScale, minScale
} = transform scale *= e.scale scale = scale >
= maxScale ? maxScale : scale scale = scale <
= minScale ? minScale : scale transform.scale = scale svgBox.style.transform = `scale(${scale
}
)`

})複製程式碼

設定maxScale,minScale以免無限大或者無限小

· 注意這裡縮放的是box而不是svg本身,否則拖動後再縮放就會發現整個SVG都跑偏了

拖動事件

function checkXY(x, y) { 
let {
minX, minY, maxX, maxY
} = transform x = x >
maxX ? maxX : x x = x <
minX ? minX : x y = y >
maxY ? maxY : y y = y <
minY ? minY : y return {
x, y
}
}svgHam.on('panstart panmove', (e) =>
{
let {
scale, translateX, translateY, svgScale
} = transform let y = translateY + e.deltaY / scale let x = translateX + e.deltaX / scale let validXY = checkXY(x, y) svgTarget.style.transform = `translate(${validXY.x
}
px, ${validXY.y
}
px) scale(${svgScale
}
)`
})svgHam.on('panend', (e) =>
{
let {
scale, translateX, translateY
} = transform let y = translateY + e.deltaY / scale let x = translateX + e.deltaX / scale let validXY = checkXY(x, y) transform.translateY = validXY.y transform.translateX = validXY.x
})複製程式碼

偏移結束後更新偏移值,因為hammer提供的event事件的偏差值deltaY/deltaX是你手指點選初始位置以及移動後的差值。

偏移量 / scale 可以有效的控制放大後的拖動速度,否則放大後一拖動,整個SVG就跑走了。

選座

做好了上述效果,選座就簡單了 直接一個點選事件就好了

document.querySelector('.svg-box').addEventListener('click', selectSeat)// svg 點選事件 選座function selectSeat (e) { 
if (e.target.tagName !== 'circle') return false e.target.style.fill = e.target.style.fill === 'red' ? '#ccc' : 'red' // 選中的座位變成紅色 // do something...
}複製程式碼

完整程式碼


寫在最後

不是十分完美,不過也滿足現在的需求,希望能給大家帶來一點啟發。不足的地方,也望各位大神指點迷津。

來源:https://juejin.im/post/5bed39096fb9a049b3478caf

相關文章