最近项目须要在移动端作一个能够选座订票的功能。上网搜了一下没找到一个现成的库能够解决需求,因此就本身写了一个粗糙的版本。javascript
详细效果能够去demo那体验一下,建议用手机端体验。html
座位分布图的SVG是UI画好后导出来,而且经过后端接口返回整个SVG标签以及里面的内容。 前端只要发请求得到SVG而且插入就行了。插入后须要将已订购的座位置黑,没法选择。前端
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)
})
复制代码
箱子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...
}
复制代码
写在最后
不是十分完美,不过也知足如今的需求,但愿能给你们带来一点启发。不足的地方,也望各位大神指点迷津。