295 lines
8.3 KiB
Vue
295 lines
8.3 KiB
Vue
<template>
|
|
<view class="container" @click="handleCanvasClick">
|
|
<view class="gesture-container"
|
|
@touchstart="handleTouchEvent"
|
|
@touchmove="handleTouchEvent"
|
|
@touchend="handleTouchEvent"
|
|
@touchcancel="handleTouchEvent">
|
|
|
|
<!-- 显示调试信息 -->
|
|
<view class="debug-info">
|
|
<text>缩放: {{scaleValue.toFixed(2)}} | X: {{transformMatrix.tx.toFixed(1)}} | Y: {{transformMatrix.ty.toFixed(1)}}</text>
|
|
<text>手势: {{gestureStatus}}</text>
|
|
<text>触点: {{touchPoints}}</text>
|
|
</view>
|
|
|
|
<transform-canvas
|
|
ref="canvasRef"
|
|
:width="canvasWidth"
|
|
:height="canvasHeight"
|
|
:matrix="transformMatrix"
|
|
:imgUrl="'https://assets.tech.troyrc.com/sx25/images/events/XBDT.jpg'"
|
|
:areaData="seatAreas"
|
|
/>
|
|
</view>
|
|
|
|
<!-- <view class="controls">
|
|
<button @click="resetCanvas">重置</button>
|
|
<button @click="zoomIn">放大</button>
|
|
<button @click="zoomOut">缩小</button>
|
|
</view> -->
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import TransformMatrix from './transform-matrix.js'
|
|
import GestureHandler from './gesture-handler.js'
|
|
import TransformCanvas from './transform-canvas.vue'
|
|
|
|
export default {
|
|
components: {
|
|
TransformCanvas
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
canvasWidth: 800,
|
|
canvasHeight: 600,
|
|
transformMatrix: new TransformMatrix(),
|
|
gestureHandler: null,
|
|
containerRect: null,
|
|
gestureStatus: '等待手势...',
|
|
scaleValue: 1,
|
|
touchPoints: 0,
|
|
lastGestureTime: 0,
|
|
seatAreas: [] // 座位区域数据
|
|
};
|
|
},
|
|
|
|
async mounted() {
|
|
await this.getContainerPosition();
|
|
this.initGestureHandler();
|
|
|
|
// 加载座位区域数据
|
|
await this.loadSeatAreas();
|
|
|
|
// 初始绘制
|
|
this.$nextTick(() => {
|
|
this.updateCanvas();
|
|
});
|
|
},
|
|
|
|
methods: {
|
|
handleCanvasClick(e) {
|
|
if (!this.containerRect) return;
|
|
|
|
// 计算相对坐标
|
|
const x = e.detail.x - this.containerRect.left;
|
|
const y = e.detail.y - this.containerRect.top;
|
|
|
|
// 检测点击区域
|
|
const hitArea = this.$refs.canvasRef.checkHitArea(x, y);
|
|
console.log('hitArea',hitArea)
|
|
if (hitArea) {
|
|
console.log('点击区域:', hitArea);
|
|
|
|
}
|
|
},
|
|
|
|
async getContainerPosition() {
|
|
// 添加重试机制
|
|
let retryCount = 0;
|
|
const maxRetries = 3;
|
|
|
|
while (retryCount < maxRetries) {
|
|
try {
|
|
const rect = await new Promise(resolve => {
|
|
const query = uni.createSelectorQuery().in(this);
|
|
query.select('.gesture-container').boundingClientRect(res => {
|
|
resolve(res);
|
|
}).exec();
|
|
});
|
|
|
|
if (rect) {
|
|
this.containerRect = {
|
|
left: rect.left,
|
|
top: rect.top,
|
|
width: rect.width,
|
|
height: rect.height
|
|
};
|
|
return;
|
|
}
|
|
} catch (e) {
|
|
console.error('获取容器位置失败:', e);
|
|
}
|
|
|
|
// 等待后重试
|
|
await new Promise(r => setTimeout(r, 100));
|
|
retryCount++;
|
|
}
|
|
},
|
|
|
|
initGestureHandler() {
|
|
try {
|
|
this.gestureHandler = new GestureHandler(this, this.transformMatrix, {
|
|
container: this.containerRect
|
|
});
|
|
} catch (e) {
|
|
console.error('手势处理器初始化失败:', e);
|
|
// 简化降级方案
|
|
this.gestureHandler = {
|
|
catchEvent: (event) => {
|
|
if (event.touches.length > 0) {
|
|
this.gestureStatus = "降级模式";
|
|
this.touchPoints = event.touches.length;
|
|
|
|
if (event.type === 'touchmove') {
|
|
const firstTouch = event.touches[0];
|
|
const x = firstTouch.clientX - this.containerRect.left;
|
|
const y = firstTouch.clientY - this.containerRect.top;
|
|
|
|
this.transformMatrix.tx = x;
|
|
this.transformMatrix.ty = y;
|
|
this.updateCanvas();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
// 事件处理
|
|
async handleTouchEvent(event) {
|
|
if (!this.gestureHandler || !this.containerRect) {
|
|
await this.getContainerPosition();
|
|
this.initGestureHandler();
|
|
if (event.type === 'touchend' || event.type === 'touchcancel') {
|
|
this.gestureStatus = '结束';
|
|
this.updateCanvas();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 记录时间戳
|
|
const currentTime = Date.now();
|
|
|
|
// 更新触点数量
|
|
this.touchPoints = event.touches.length;
|
|
|
|
// 修正坐标
|
|
const correctedTouches = Array.from(event.touches).map(touch => {
|
|
return {
|
|
...touch,
|
|
x: touch.clientX - this.containerRect.left,
|
|
y: touch.clientY - this.containerRect.top
|
|
};
|
|
});
|
|
|
|
// 创建修正后的事件对象
|
|
const correctedEvent = {
|
|
...event,
|
|
touches: correctedTouches,
|
|
changedTouches: correctedTouches
|
|
};
|
|
|
|
// 传递事件给手势处理器
|
|
this.gestureHandler.catchEvent(correctedEvent);
|
|
|
|
// 更新手势状态
|
|
if (event.type === 'touchstart') {
|
|
this.gestureStatus = event.touches.length > 1 ? '双指开始' : '单指开始';
|
|
}
|
|
else if (event.type === 'touchmove') {
|
|
this.gestureStatus = event.touches.length > 1 ? '双指缩放/移动' : '单指移动';
|
|
}
|
|
else {
|
|
this.gestureStatus = '结束';
|
|
}
|
|
|
|
// 限制更新频率
|
|
if (currentTime - this.lastGestureTime > 50) {
|
|
this.lastGestureTime = currentTime;
|
|
this.updateCanvas();
|
|
}
|
|
},
|
|
|
|
// 更新Canvas
|
|
updateCanvas() {
|
|
this.$nextTick(() => {
|
|
this.scaleValue = Math.sqrt(
|
|
this.transformMatrix.a * this.transformMatrix.a +
|
|
this.transformMatrix.b * this.transformMatrix.b
|
|
);
|
|
});
|
|
},
|
|
|
|
// 重置画布
|
|
resetCanvas() {
|
|
this.transformMatrix.reset();
|
|
this.scaleValue = 1;
|
|
this.gestureStatus = "画布已重置";
|
|
this.updateCanvas();
|
|
},
|
|
|
|
// 放大
|
|
zoomIn() {
|
|
this.transformMatrix.scale(1.2, 1.2, this.canvasWidth/2, this.canvasHeight/2);
|
|
this.updateCanvas();
|
|
},
|
|
|
|
// 缩小
|
|
zoomOut() {
|
|
this.transformMatrix.scale(0.8, 0.8, this.canvasWidth/2, this.canvasHeight/2);
|
|
this.updateCanvas();
|
|
},
|
|
|
|
// 加载座位区域数据
|
|
async loadSeatAreas() {
|
|
try {
|
|
// 模拟API请求
|
|
const response = {
|
|
"code":200,
|
|
"message":"",
|
|
"data":[{"areacode":"03tkrrjukrgu","areaname":"主席台","description":"主席台","remain":100,"polygon":[262,154,262,165,262,177,262,188,314,188,365,188,417,188,417,177,417,165,417,154,365,154,314,154,262,154]},{"areacode":"ea0jg3jukrgw","areaname":"A区","description":"A区","remain":1000,"polygon":[105,94,105,125,105,158,105,189,183,189,251,189,250,147,336,147,337,125,337,94,259,94,183,94,105,94]},{"areacode":"832fe6ej0kqc","areaname":"C区","description":"C区","remain":1000,"polygon":[106,418,106,452,106,487,106,521,183,521,261,521,338,521,338,487,338,452,338,418,261,418,183,418,106,418]},{"areacode":"p5naxqej0kqd","areaname":"B区","description":"B区","remain":1000,"polygon":[345,93,345,125,344,147,425,148,425,188,499,190,576,190,576,158,576,125,576,93,499,93,422,93,345,93]},{"areacode":"uknpk3sa819j","areaname":"D区","description":"D区","remain":1000,"polygon":[347,419,347,453,347,487,347,521,423,521,499,521,575,521,575,487,575,453,575,419,499,419,423,419,347,419]}]
|
|
}
|
|
|
|
this.seatAreas = response.data;
|
|
} catch (e) {
|
|
console.error('加载区域数据失败:', e);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.container {
|
|
/* padding: 20px; */
|
|
}
|
|
|
|
.gesture-container {
|
|
position: relative;
|
|
width: 100%;
|
|
/* height: 70vh; */
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.debug-info {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
background: rgba(0,0,0,0.6);
|
|
color: white;
|
|
padding: 5px;
|
|
font-size: 12px;
|
|
z-index: 10;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.controls {
|
|
margin-top: 20px;
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
button {
|
|
padding: 8px 16px;
|
|
background: #4a8cff;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 4px;
|
|
}
|
|
</style> |