项目:taro3+vue3
描述:一张停车场地图,图上标记空车位,点击空车位能够导航到此位置,可以移动,缩放
拆解:
A、移动
B、缩放 -> 手势缩放和按钮缩放
C、标记空车位
D、点击,计算是否在空车位圆圈内
<base-move-layout> -移动组件
<reverse-canvas></reverse-canvas> -画布组件
</base-move-layout>
<reverse-car-scale></reverse-car-scale> - 缩放组件
结合movable-area/movable-view
手势缩放:movable-view的scale事件
/**
* 绘制空车位
* @param ctx
* @param list 空车位列表
*/
export function useDrawEmptyBerth(ctx, list) {
if (!list.length) {
return
}
for (let i = 0; i < list.length; i++) {
const item = list[i]
const { x, y } = item
ctx.beginPath()
ctx.strokeStyle = '#FF028C'
ctx.lineWidth = 3
ctx.arc(x, y, EMPTY_CIRCLE_R, 0, 2 * Math.PI)
ctx.stroke()
}
}
点击的点到圆心的距离是否大于半径,是:圈外,否:圈上
const dis = Math.sqrt((x - a) * (x - a) + (y - b) * (y - b))
if (dis <= circleR) {
// 在圈上
}
解决:
移动组件:
设置2个开关isDragging, isScaling
缩放组件中,点击按钮的时候也设置一个开关,移动组件根据这个开关去scale
注意:
① 缩放组件和移动组件没有层级关系,所以用trigger
② 缩放后,点击复原按钮,需要重置 movable-view 的x, y, 重置时 直接赋值正确的值,并不能复原,需要 x = 值 - 1,再x = 值
移动组件
<movable-area
id="movableArea"
class="movable-area"
scale-area>
<movable-view
:style="`width: ${width}px; height: ${height}px;`"
class="movable-view"
:x="movableViewX"
:y="movableViewY"
:is-dragging="isDragging"
:damping="50"
:out-of-bounds="true"
direction="all"
:scale="true"
:scale-value="scaleValue"
:scale-min="SCALE_MIN"
:scale-max="SCALE_MAX"
@change="onChangeMovable"
@scale="onScaleMovable">
<!--用于观察当前实际位置-->
<view
class="reverse-car-now"
:style="nowStyle"></view>
<slot :info="{isDragging, isScaling, tempScale}"></slot>
</movable-view>
</movable-area>
events.on(REVERSE_PAGE_EVENTS.CLICK_SCALE_BTN, async type => {
this.isClickScaleBtn = true
if (type === SCALE_TYPE.DEFAULT) {
const x = this.movableViewX.value
const y = this.movableViewY.value
this.movableViewX.value = x - 1
this.movableViewY.value = y - 1
await sleep(100)
this.movableViewX.value = x
this.movableViewY.value = y
}
setTimeout(() => {
this.isClickScaleBtn = false
}, 1000)
})
/**
* 拖动
*/
onChangeMovable() {
console.log('拖动')
this.isDragging.value = true
setTimeout(() => {
this.isDragging.value = false
}, 1000)
}
/**
* 手势放大缩小
* @param e
*/
onScaleMovable(e) {
if (this.isClickScaleBtn) {
return
}
const { scale } = e.detail
this.isScaling = true
this.tempScale.value = scale
setTimeout(() => {
this.isScaling = false
}, 50)
}
canvas组件
<canvas
type="2d"
id="myCanvas"
:style="`width: ${width}px; height: ${height}px`"
canvas-id="myCanvas"
@touchstart="onTouchStart"></canvas>
/**
* 点击空车位
* @param event
*/
onTouchStart(event) {
const scale = this.scaleValue
const { x, y } = event.touches[0]
const circleR = EMPTY_CIRCLE_R // 半径
setTimeout(() => {
if (this.isDrag || this.isScale) {
return
}
....
const { x: a, y: b } = it // 标记点
const dis = Math.sqrt((x / scale - a) * (x / scale - a) + (y / scale - b) * (y / scale - b))
if (dis <= circleR) {
this.context.emit('getEmpty', it)
break
}
}, 500)
}
<base-move-layout
:width="canvasW"
:height="canvasH"
:scale="scale"
:nowStyle="nowStyle">
<template v-slot="slotProps">
<base-canvas
v-bind="$attrs"
:is-dragging="slotProps.info.isDragging"
:is-scaling="slotProps.info.isScaling"
:temp-scale="slotProps.info.tempScale"
:is-end="isEnd"
:img="mapImageInfo.mapUrl"
:width="canvasW"
:height="canvasH"
:path-list="pathList"
:empty-list="emptyBerthList"
@getEmpty="catchGetEmpty"></base-canvas>
</template>
</base-move-layout>
缩放组件
<view class="reverse-car-scale f-l-c">
<viewclass="scale-btn">
<view class="scale-item scale-item-s" @tap="onScale(SCALE_TYPE.SMALL)">—</view>
<view class="scale-item scale-item-b" @tap="onScale(SCALE_TYPE.BIG)">+</view>
</view>
<view class="center-btn" @tap="onScale(SCALE_TYPE.DEFAULT)">
<image class="img" :src="IconMapLocation"></image>
</view>
</view>
const onScale = async(type) => {
const val = props.modelValue
if (type === SCALE_TYPE.SMALL && val > 1) {
scaleValue.value = val - 1
} else if (type === SCALE_TYPE.BIG && val < 3) {
scaleValue.value = val + 1
} else {
scaleValue.value = 1
}
context.emit('update:modelValue', scaleValue.value)
events.trigger(REVERSE_PAGE_EVENTS.CLICK_SCALE_BTN, type)
}