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

最近項目須要在移動端作一個能夠選座訂票的功能。上網搜了一下沒找到一個現成的庫能夠解決需求,因此就本身寫了一個粗糙的版本。javascript

需求

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

技術棧

效果圖

捏放

拖動

詳細效果能夠去demo那體驗一下,建議用手機端體驗。html

svg

座位分佈圖的SVG是UI畫好後導出來,而且經過後端接口返回整個SVG標籤以及裏面的內容。 前端只要發請求得到SVG而且插入就行了。插入後須要將已訂購的座位置黑,沒法選擇。前端

hammerjs

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

用法

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

html 結構

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

svg用以偏移github

<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的縮放。bash

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

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調整分數。ui

手勢配置

// 初始化 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...
}
複製代碼

完整代碼


寫在最後

不是十分完美,不過也知足如今的需求,但願能給你們帶來一點啓發。不足的地方,也望各位大神指點迷津。

相關文章
相關標籤/搜索