This commit is contained in:
sunmeng 2025-08-13 18:14:56 +08:00
parent a0129fa7ce
commit ed07181a5e
10 changed files with 446 additions and 266 deletions

View File

@ -21,6 +21,7 @@
:imgUrl="'https://assets.tech.troyrc.com/sx25/images/events/XBDT.jpg'" :imgUrl="'https://assets.tech.troyrc.com/sx25/images/events/XBDT.jpg'"
:areaData="currentView === 'area' ? seatAreas : []" :areaData="currentView === 'area' ? seatAreas : []"
:seatData="currentView === 'seat' ? seatPositions : []" :seatData="currentView === 'seat' ? seatPositions : []"
:selectedCodes="selectedCodes"
/> />
</view> </view>
<!-- isShowSeat --> <!-- isShowSeat -->
@ -57,7 +58,8 @@ export default {
seatAreas: [], // seatAreas: [], //
currentView:'area', currentView:'area',
seatPositions: [], // seatPositions: [], //
selectedArea: null // selectedArea: null, //
selectedCodes: new Set(), // code
}; };
}, },
@ -84,9 +86,10 @@ export default {
// dpr // dpr
const x = (e.detail.x - this.containerRect.left) * dpr; const x = (e.detail.x - this.containerRect.left) * dpr;
const y = (e.detail.y - this.containerRect.top) * dpr; const y = (e.detail.y - this.containerRect.top) * dpr;
console.log('handleCanvasClick',x,y)
if (this.currentView === 'area') { if (this.currentView === 'area') {
const hitArea = this.$refs.canvasRef.checkHitArea(x, y); const hitArea = this.$refs.canvasRef.checkHitArea(x, y);
console.log('handleCanvasClick',hitArea)
if (hitArea) { if (hitArea) {
uni.showModal({ uni.showModal({
title: '请确认', title: '请确认',
@ -99,7 +102,6 @@ export default {
this.currentView = 'seat'; this.currentView = 'seat';
this.loadSeatData(hitArea.areacode) this.loadSeatData(hitArea.areacode)
} }
console.log(res,'showModa-- success')
}, },
}) })
} }
@ -107,12 +109,32 @@ export default {
const hitSeat = this.$refs.canvasRef.checkSeatHit(x, y); const hitSeat = this.$refs.canvasRef.checkSeatHit(x, y);
if (hitSeat) { if (hitSeat) {
console.log('选中座位:', hitSeat); console.log('选中座位:', hitSeat);
this.toggleSeatSelection(hitSeat);
// //
} }
} }
}, },
toggleSeatSelection(seat) {
if (seat.status !== 1) return; //
if (this.selectedCodes.has(seat.code)) {
this.selectedCodes.delete(seat.code);
} else {
// 4
if (this.selectedCodes.size < 4) {
this.selectedCodes.add(seat.code);
} else {
uni.showToast({
title: '最多只能选择4个座位',
icon: 'none'
});
}
}
this.$refs.canvasRef.redraw(); //
},
async getContainerPosition() { async getContainerPosition() {
// //
let retryCount = 0; let retryCount = 0;
@ -136,9 +158,9 @@ export default {
}; };
return; return;
} }
} catch (e) { } catch (e) {
console.error('获取容器位置失败:', e); console.error('获取容器位置失败:', e);
} }
// //
await new Promise(r => setTimeout(r, 100)); await new Promise(r => setTimeout(r, 100));
@ -147,89 +169,111 @@ export default {
}, },
initGestureHandler() { initGestureHandler() {
try { try {
this.gestureHandler = new GestureHandler(this, this.transformMatrix, { this.gestureHandler = new GestureHandler(this, this.transformMatrix, {
container: this.containerRect container: this.containerRect
}); });
} catch (e) { console.log( 'initGestureHandler', this.gestureHandler )
console.error('手势处理器初始化失败:', e); } catch (e) {
// console.error('手势处理器初始化失败:', e);
this.gestureHandler = { //
catchEvent: (event) => { this.gestureHandler = {
if (event.touches.length > 0) { catchEvent: this.createGestureFallback(),
this.gestureStatus = "降级模式"; setScale: (scale) => {
this.touchPoints = event.touches.length; this.transformMatrix.scale(scale, scale, this.canvasWidth/2, this.canvasHeight/2);
this.updateCanvas();
if (event.type === 'touchmove') { },
const firstTouch = event.touches[0]; reset: () => {
const x = firstTouch.clientX - this.containerRect.left; this.transformMatrix.reset();
const y = firstTouch.clientY - this.containerRect.top; this.updateCanvas();
}
this.transformMatrix.tx = x; }
this.transformMatrix.ty = y; }
this.updateCanvas(); },
}
} createGestureFallback() {
} let isClick = true;
} let startPoint = null;
} let startTime = null;
}, let lastPoint = null;
// return (event) => {
async handleTouchEvent(event) { const touches = event.touches || [];
if (!this.gestureHandler || !this.containerRect) {
await this.getContainerPosition(); if (touches.length === 1) {
this.initGestureHandler(); const getPoint = (t) => ({
if (event.type === 'touchend' || event.type === 'touchcancel') { x: t.clientX - this.containerRect.left,
this.gestureStatus = '结束'; y: t.clientY - this.containerRect.top
this.updateCanvas(); });
return;
} const currentPoint = getPoint(touches[0]);
}
if (!startPoint) {
// startPoint = currentPoint;
const currentTime = Date.now(); startTime = Date.now();
}
//
this.touchPoints = event.touches.length; const dx = currentPoint.x - startPoint.x;
const dy = currentPoint.y - startPoint.y;
// const distance = Math.sqrt(dx * dx + dy * dy);
const correctedTouches = Array.from(event.touches).map(touch => {
return { //
...touch, if (isClick && distance < 5 && Date.now() - startTime < 200) {
x: touch.clientX - this.containerRect.left, return; //
y: touch.clientY - this.containerRect.top }
};
}); //
isClick = false;
//
const correctedEvent = { //
...event, if (lastPoint) {
touches: correctedTouches, const moveDx = currentPoint.x - lastPoint.x;
changedTouches: correctedTouches const moveDy = currentPoint.y - lastPoint.y;
};
this.transformMatrix.tx += moveDx;
// this.transformMatrix.ty += moveDy;
this.gestureHandler.catchEvent(correctedEvent); }
// lastPoint = currentPoint;
if (event.type === 'touchstart') { }
this.gestureStatus = event.touches.length > 1 ? '双指开始' : '单指开始';
} //
else if (event.type === 'touchmove') { if (event.type === 'touchend' || event.type === 'touchcancel') {
this.gestureStatus = event.touches.length > 1 ? '双指缩放/移动' : '单指移动'; isClick = true;
} startPoint = null;
else { startTime = null;
this.gestureStatus = '结束'; lastPoint = null;
} }
};
// },
if (currentTime - this.lastGestureTime > 50) {
this.lastGestureTime = currentTime; async handleTouchEvent(event) {
this.updateCanvas(); console.log(event,'handleTouchEvent')
} //
}, this.touchPoints = event.touches.length;
//
if (event.type === 'touchstart') {
this.gestureStatus = event.touches.length > 1 ? '双指开始' : '单指开始';
}
//
this.gestureHandler?.catchEvent(event);
//
if (event.type === 'touchend' || event.type === 'touchcancel') {
this.gestureStatus = '结束';
this.updateCanvas();
}
//
const currentTime = Date.now();
if (currentTime - this.lastGestureTime > 50) {
this.lastGestureTime = currentTime;
this.updateCanvas();
}
},
// Canvas // Canvas
updateCanvas() { updateCanvas() {
this.$nextTick(() => { this.$nextTick(() => {
@ -595,29 +639,37 @@ export default {
this.transformMatrix = new TransformMatrix(); this.transformMatrix = new TransformMatrix();
// //
this.$nextTick(() => { //
if (this.seatPositions.length > 0) { this.$nextTick(() => {
const minX = Math.min(...this.seatPositions.map(s => s.x)); if (!this.gestureHandler) {
const maxX = Math.max(...this.seatPositions.map(s => s.x)); this.initGestureHandler();
const minY = Math.min(...this.seatPositions.map(s => s.y)); }
const maxY = Math.max(...this.seatPositions.map(s => s.y)); if (this.seatPositions.length > 0) {
//
const centerX = (minX + maxX) / 2; const xs = this.seatPositions.map(s => s.x);
const centerY = (minY + maxY) / 2; const ys = this.seatPositions.map(s => s.y);
const minX = Math.min(...xs);
// const maxX = Math.max(...xs);
const widthRatio = this.canvasWidth / (maxX - minX + 100); const minY = Math.min(...ys);
const heightRatio = this.canvasHeight / (maxY - minY + 100); const maxY = Math.max(...ys);
const scale = Math.min(widthRatio, heightRatio, 1);
//
// const centerX = (minX + maxX) / 2;
// this.transformMatrix.scale(scale, scale); const centerY = (minY + maxY) / 2;
// this.transformMatrix.translate(
// this.canvasWidth/2 - centerX * scale, //
// this.canvasHeight/2 - centerY * scale const widthRatio = this.canvasWidth / (maxX - minX + 100);
// ); const heightRatio = this.canvasHeight / (maxY - minY + 100);
} const scale = Math.min(widthRatio, heightRatio, 1);
});
//
// this.transformMatrix.scale(scale, scale);
// this.transformMatrix.translate(
// this.canvasWidth/2 - centerX * scale,
// this.canvasHeight/2 - centerY * scale
// );
}
});
} catch (e) { } catch (e) {

View File

@ -4,7 +4,8 @@ class GestureHandler {
this.containerRect = container; this.containerRect = container;
this.panThreshold = 5; this.panThreshold = 5;
this.panStarted = false; this.panStarted = false;
// 保存父组件上下文
this.context = context;
// 判断是否是小程序环境 // 判断是否是小程序环境
this.isMiniProgram = typeof wx !== 'undefined' || typeof uni !== 'undefined'; this.isMiniProgram = typeof wx !== 'undefined' || typeof uni !== 'undefined';
@ -52,66 +53,63 @@ class GestureHandler {
} }
} }
// 创建小程序专用的简化手势处理器 createSimpleGestureHandler() {
createSimpleGestureHandler() { let isClick = true;
let isClick = true; let startPoint = null;
let startPoint = null; let startTime = null;
let startTime = null; let lastPoint = null;
let lastPoint = null;
return (event) => {
return (event) => { // 移除了视图判断
const touches = event.touches || []; const touches = event.touches || [];
if (touches.length === 1) { if (touches.length === 1) {
const getPoint = (t) => ({ const getPoint = (t) => ({
x: t.x || (t.clientX - this.containerRect.left), x: t.x || (t.clientX - this.containerRect.left),
y: t.y || (t.clientY - this.containerRect.top) y: t.y || (t.clientY - this.containerRect.top)
}); });
const currentPoint = getPoint(touches[0]); const currentPoint = getPoint(touches[0]);
// 第一次触摸 if (!startPoint) {
if (!startPoint) { startPoint = currentPoint;
startPoint = currentPoint; startTime = Date.now();
startTime = Date.now(); }
}
const dx = currentPoint.x - startPoint.x;
// 计算移动距离 const dy = currentPoint.y - startPoint.y;
const dx = currentPoint.x - startPoint.x; const distance = Math.sqrt(dx * dx + dy * dy);
const dy = currentPoint.y - startPoint.y;
const distance = Math.sqrt(dx * dx + dy * dy); // 区分点击和拖拽
if (isClick && distance < 5 && Date.now() - startTime < 200) {
// 如果是点击(移动距离小于阈值且时间短) return;
if (isClick && distance < 5 && Date.now() - startTime < 200) { }
return; // 不执行移动操作
} isClick = false;
// 标记为非点击操作 if (lastPoint) {
isClick = false; const moveDx = currentPoint.x - lastPoint.x;
const moveDy = currentPoint.y - lastPoint.y;
// 执行移动操作
if (lastPoint) { this.transformMatrix.tx += moveDx;
const moveDx = currentPoint.x - lastPoint.x; this.transformMatrix.ty += moveDy;
const moveDy = currentPoint.y - lastPoint.y; }
this.transformMatrix.tx += moveDx; lastPoint = currentPoint;
this.transformMatrix.ty += moveDy; } else if (touches.length > 1) {
} this._handlePinch(touches);
}
lastPoint = currentPoint;
} else if (touches.length > 1) { // 重置状态
this._handlePinch(touches); if (event.type === 'touchend' || event.type === 'touchcancel') {
} isClick = true;
startPoint = null;
// 在触摸结束时重置状态 startTime = null;
if (event.type === 'touchend' || event.type === 'touchcancel') { lastPoint = null;
isClick = true; }
startPoint = null; };
startTime = null; }
lastPoint = null;
}
};
}
setupGestures() { setupGestures() {
// 平移手势 // 平移手势

View File

@ -27,7 +27,8 @@ export default {
}) })
}, },
areaData:{}, areaData:{},
seatData:{} seatData:{},
selectedCodes:{}
}, },
data() { data() {
return { return {
@ -37,10 +38,17 @@ export default {
canvasDisplayWidth: 800, // canvasDisplayWidth: 800, //
canvasDisplayHeight: 600, canvasDisplayHeight: 600,
canvasActualWidth: 800, // canvasActualWidth: 800, //
canvasActualHeight: 600 canvasActualHeight: 600,
nowSelectedCodes:this.selectedCodes
}; };
}, },
watch: { watch: {
selectedCodes:{
handler(newVal) {
this.nowSelectedCodes = newVal
},
immediate: true
},
imgUrl: { imgUrl: {
handler(newUrl) { handler(newUrl) {
if (newUrl) this.loadImage(newUrl); if (newUrl) this.loadImage(newUrl);
@ -83,6 +91,13 @@ export default {
this.initCanvas(); this.initCanvas();
}, },
methods: { methods: {
invertPoint(x, y) {
// 使dpr
const inverted = this.matrix.invertPoint(x, y);
return inverted;
},
// //
checkHitArea(x, y) { checkHitArea(x, y) {
if (!this.areaData) return null; if (!this.areaData) return null;
@ -302,26 +317,62 @@ export default {
}); });
}, },
drawSeats() { drawSeats() {
// this.ctx.save();
const scale = Math.sqrt(this.matrix.a * this.matrix.a + this.matrix.b * this.matrix.b);
//
this.seatData.forEach(seat => { const { a, b, c, d, tx, ty } = this.matrix;
// 使 this.ctx.setTransform(a, b, c, d, tx, ty);
const x = seat.x;
const y = seat.y; //
const scale = Math.sqrt(a * a + b * b);
this.ctx.beginPath();
// this.seatData.forEach(seat => {
const radius = 8 / scale; const x = seat.x;
this.ctx.arc(x, y, radius, 0, Math.PI * 2); const y = seat.y;
this.ctx.fillStyle = seat.status === 1 ? '#4cd964' : '#dd524d'; //
this.ctx.fill(); const radius = 8 / scale;
// console.log(this.nowSelectedCodes,'nowSelectedCodesnowSelectedCodesnowSelectedCodes')
// 1
}); if (this.nowSelectedCodes.has(seat.code) && seat.status === 1) {
// 1.
this.ctx.beginPath();
this.ctx.arc(x, y, radius + 1, 0, Math.PI * 2);
this.ctx.strokeStyle = '#FFD700'; //
this.ctx.lineWidth = 2 / scale;
this.ctx.stroke();
// 2.
this.ctx.beginPath();
this.ctx.arc(x, y, radius, 0, Math.PI * 2);
this.ctx.fillStyle = 'rgba(255, 215, 0, 0.3)';
this.ctx.fill();
// 3.
this.ctx.strokeStyle = '#fff';
this.ctx.lineWidth = 1 / scale;
this.ctx.lineCap = 'round';
this.ctx.beginPath();
this.ctx.moveTo(x - 2.5, y);
this.ctx.lineTo(x - 0.5, y + 1.5);
this.ctx.lineTo(x + 2.5, y - 2);
this.ctx.stroke();
} else {
//
this.ctx.beginPath();
this.ctx.arc(x, y, radius, 0, Math.PI * 2);
this.ctx.fillStyle = seat.status === 1 ? '#4cd964' : '#dd524d';
this.ctx.fill();
}
//
});
this.ctx.restore();
} }
} }
} }
</script> </script>

View File

@ -1 +1 @@
{"version":3,"file":"gesture-canvas-page.js","sources":["pages/index/gesture-canvas-page.vue?type=page"],"sourcesContent":["import MiniProgramPage from '/Users/sunmeng/Desktop/wx/canvas/pages/index/gesture-canvas-page.vue'\nwx.createPage(MiniProgramPage)"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,GAAG,WAAW,eAAe;"} {"version":3,"file":"gesture-canvas-page.js","sources":["pages/index/gesture-canvas-page.vue?type=page"],"sourcesContent":["import MiniProgramPage from '/Users/sunmeng/Desktop/wx/canvas/pages/index/gesture-canvas-page.vue'\nwx.createPage(MiniProgramPage)"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,GAAG,WAAW,eAAe;"}

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"version":3,"file":"transform-canvas.js","sources":["/Users/sunmeng/Desktop/wx/canvas/pages/index/transform-canvas.vue?type=component"],"sourcesContent":["import Component from '/Users/sunmeng/Desktop/wx/canvas/pages/index/transform-canvas.vue'\nwx.createComponent(Component)"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,GAAG,gBAAgB,SAAS;"} {"version":3,"file":"transform-canvas.js","sources":["/Users/sunmeng/Desktop/wx/canvas/pages/index/transform-canvas.vue?type=component"],"sourcesContent":["import Component from '/Users/sunmeng/Desktop/wx/canvas/pages/index/transform-canvas.vue'\nwx.createComponent(Component)"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,GAAG,gBAAgB,SAAS;"}

View File

@ -6882,7 +6882,7 @@ function initOnError() {
function initRuntimeSocketService() { function initRuntimeSocketService() {
const hosts = "127.0.0.1,172.10.0.127"; const hosts = "127.0.0.1,172.10.0.127";
const port = "8090"; const port = "8090";
const id = "mp-weixin_8j0jyd"; const id = "mp-weixin_3NWJxu";
const lazy = typeof swan !== "undefined"; const lazy = typeof swan !== "undefined";
let restoreError = lazy ? () => { let restoreError = lazy ? () => {
} : initOnError(); } : initOnError();

View File

@ -23,8 +23,10 @@ const _sfc_main = {
currentView: "area", currentView: "area",
seatPositions: [], seatPositions: [],
// 座位数据 // 座位数据
selectedArea: null selectedArea: null,
// 当前选中分区 // 当前选中分区
selectedCodes: /* @__PURE__ */ new Set()
// 存储选中的座位code
}; };
}, },
async mounted() { async mounted() {
@ -42,8 +44,10 @@ const _sfc_main = {
const dpr = this.$refs.canvasRef.dpr || 1; const dpr = this.$refs.canvasRef.dpr || 1;
const x = (e.detail.x - this.containerRect.left) * dpr; const x = (e.detail.x - this.containerRect.left) * dpr;
const y = (e.detail.y - this.containerRect.top) * dpr; const y = (e.detail.y - this.containerRect.top) * dpr;
common_vendor.index.__f__("log", "at pages/index/gesture-canvas-page.vue:89", "handleCanvasClick", x, y);
if (this.currentView === "area") { if (this.currentView === "area") {
const hitArea = this.$refs.canvasRef.checkHitArea(x, y); const hitArea = this.$refs.canvasRef.checkHitArea(x, y);
common_vendor.index.__f__("log", "at pages/index/gesture-canvas-page.vue:92", "handleCanvasClick", hitArea);
if (hitArea) { if (hitArea) {
common_vendor.index.showModal({ common_vendor.index.showModal({
title: "请确认", title: "请确认",
@ -55,17 +59,34 @@ const _sfc_main = {
this.currentView = "seat"; this.currentView = "seat";
this.loadSeatData(hitArea.areacode); this.loadSeatData(hitArea.areacode);
} }
common_vendor.index.__f__("log", "at pages/index/gesture-canvas-page.vue:102", res, "showModa-- success");
} }
}); });
} }
} else if (this.currentView === "seat") { } else if (this.currentView === "seat") {
const hitSeat = this.$refs.canvasRef.checkSeatHit(x, y); const hitSeat = this.$refs.canvasRef.checkSeatHit(x, y);
if (hitSeat) { if (hitSeat) {
common_vendor.index.__f__("log", "at pages/index/gesture-canvas-page.vue:109", "选中座位:", hitSeat); common_vendor.index.__f__("log", "at pages/index/gesture-canvas-page.vue:111", "选中座位:", hitSeat);
this.toggleSeatSelection(hitSeat);
} }
} }
}, },
toggleSeatSelection(seat) {
if (seat.status !== 1)
return;
if (this.selectedCodes.has(seat.code)) {
this.selectedCodes.delete(seat.code);
} else {
if (this.selectedCodes.size < 4) {
this.selectedCodes.add(seat.code);
} else {
common_vendor.index.showToast({
title: "最多只能选择4个座位",
icon: "none"
});
}
}
this.$refs.canvasRef.redraw();
},
async getContainerPosition() { async getContainerPosition() {
let retryCount = 0; let retryCount = 0;
const maxRetries = 3; const maxRetries = 3;
@ -87,7 +108,7 @@ const _sfc_main = {
return; return;
} }
} catch (e) { } catch (e) {
common_vendor.index.__f__("error", "at pages/index/gesture-canvas-page.vue:140", "获取容器位置失败:", e); common_vendor.index.__f__("error", "at pages/index/gesture-canvas-page.vue:162", "获取容器位置失败:", e);
} }
await new Promise((r) => setTimeout(r, 100)); await new Promise((r) => setTimeout(r, 100));
retryCount++; retryCount++;
@ -98,59 +119,75 @@ const _sfc_main = {
this.gestureHandler = new pages_index_gestureHandler.GestureHandler(this, this.transformMatrix, { this.gestureHandler = new pages_index_gestureHandler.GestureHandler(this, this.transformMatrix, {
container: this.containerRect container: this.containerRect
}); });
common_vendor.index.__f__("log", "at pages/index/gesture-canvas-page.vue:176", "initGestureHandler", this.gestureHandler);
} catch (e) { } catch (e) {
common_vendor.index.__f__("error", "at pages/index/gesture-canvas-page.vue:155", "手势处理器初始化失败:", e); common_vendor.index.__f__("error", "at pages/index/gesture-canvas-page.vue:178", "手势处理器初始化失败:", e);
this.gestureHandler = { this.gestureHandler = {
catchEvent: (event) => { catchEvent: this.createGestureFallback(),
if (event.touches.length > 0) { setScale: (scale) => {
this.gestureStatus = "降级模式"; this.transformMatrix.scale(scale, scale, this.canvasWidth / 2, this.canvasHeight / 2);
this.touchPoints = event.touches.length; this.updateCanvas();
if (event.type === "touchmove") { },
const firstTouch = event.touches[0]; reset: () => {
const x = firstTouch.clientX - this.containerRect.left; this.transformMatrix.reset();
const y = firstTouch.clientY - this.containerRect.top; this.updateCanvas();
this.transformMatrix.tx = x;
this.transformMatrix.ty = y;
this.updateCanvas();
}
}
} }
}; };
} }
}, },
// 事件处理 createGestureFallback() {
async handleTouchEvent(event) { let isClick = true;
if (!this.gestureHandler || !this.containerRect) { let startPoint = null;
await this.getContainerPosition(); let startTime = null;
this.initGestureHandler(); let lastPoint = null;
if (event.type === "touchend" || event.type === "touchcancel") { return (event) => {
this.gestureStatus = "结束"; const touches = event.touches || [];
this.updateCanvas(); if (touches.length === 1) {
return; const getPoint = (t) => ({
x: t.clientX - this.containerRect.left,
y: t.clientY - this.containerRect.top
});
const currentPoint = getPoint(touches[0]);
if (!startPoint) {
startPoint = currentPoint;
startTime = Date.now();
}
const dx = currentPoint.x - startPoint.x;
const dy = currentPoint.y - startPoint.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (isClick && distance < 5 && Date.now() - startTime < 200) {
return;
}
isClick = false;
if (lastPoint) {
const moveDx = currentPoint.x - lastPoint.x;
const moveDy = currentPoint.y - lastPoint.y;
this.transformMatrix.tx += moveDx;
this.transformMatrix.ty += moveDy;
}
lastPoint = currentPoint;
}
if (event.type === "touchend" || event.type === "touchcancel") {
isClick = true;
startPoint = null;
startTime = null;
lastPoint = null;
} }
}
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); },
async handleTouchEvent(event) {
var _a;
common_vendor.index.__f__("log", "at pages/index/gesture-canvas-page.vue:251", event, "handleTouchEvent");
this.touchPoints = event.touches.length;
if (event.type === "touchstart") { if (event.type === "touchstart") {
this.gestureStatus = event.touches.length > 1 ? "双指开始" : "单指开始"; this.gestureStatus = event.touches.length > 1 ? "双指开始" : "单指开始";
} else if (event.type === "touchmove") {
this.gestureStatus = event.touches.length > 1 ? "双指缩放/移动" : "单指移动";
} else {
this.gestureStatus = "结束";
} }
(_a = this.gestureHandler) == null ? void 0 : _a.catchEvent(event);
if (event.type === "touchend" || event.type === "touchcancel") {
this.gestureStatus = "结束";
this.updateCanvas();
}
const currentTime = Date.now();
if (currentTime - this.lastGestureTime > 50) { if (currentTime - this.lastGestureTime > 50) {
this.lastGestureTime = currentTime; this.lastGestureTime = currentTime;
this.updateCanvas(); this.updateCanvas();
@ -491,7 +528,7 @@ const _sfc_main = {
}; };
this.seatAreas = response.data; this.seatAreas = response.data;
} catch (e) { } catch (e) {
common_vendor.index.__f__("error", "at pages/index/gesture-canvas-page.vue:574", "加载区域数据失败:", e); common_vendor.index.__f__("error", "at pages/index/gesture-canvas-page.vue:618", "加载区域数据失败:", e);
} }
}, },
async loadSeatData(areaCode) { async loadSeatData(areaCode) {
@ -512,11 +549,16 @@ const _sfc_main = {
}); });
this.transformMatrix = new pages_index_transformMatrix.TransformMatrix(); this.transformMatrix = new pages_index_transformMatrix.TransformMatrix();
this.$nextTick(() => { this.$nextTick(() => {
if (!this.gestureHandler) {
this.initGestureHandler();
}
if (this.seatPositions.length > 0) { if (this.seatPositions.length > 0) {
const minX = Math.min(...this.seatPositions.map((s) => s.x)); const xs = this.seatPositions.map((s) => s.x);
const maxX = Math.max(...this.seatPositions.map((s) => s.x)); const ys = this.seatPositions.map((s) => s.y);
const minY = Math.min(...this.seatPositions.map((s) => s.y)); const minX = Math.min(...xs);
const maxY = Math.max(...this.seatPositions.map((s) => s.y)); const maxX = Math.max(...xs);
const minY = Math.min(...ys);
const maxY = Math.max(...ys);
const centerX = (minX + maxX) / 2; const centerX = (minX + maxX) / 2;
const centerY = (minY + maxY) / 2; const centerY = (minY + maxY) / 2;
const widthRatio = this.canvasWidth / (maxX - minX + 100); const widthRatio = this.canvasWidth / (maxX - minX + 100);
@ -525,7 +567,7 @@ const _sfc_main = {
} }
}); });
} catch (e) { } catch (e) {
common_vendor.index.__f__("error", "at pages/index/gesture-canvas-page.vue:624", "加载座位数据失败:", e); common_vendor.index.__f__("error", "at pages/index/gesture-canvas-page.vue:676", "加载座位数据失败:", e);
common_vendor.index.showToast({ title: "加载座位失败", icon: "none" }); common_vendor.index.showToast({ title: "加载座位失败", icon: "none" });
} }
} }
@ -549,7 +591,8 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
matrix: $data.transformMatrix, matrix: $data.transformMatrix,
imgUrl: "https://assets.tech.troyrc.com/sx25/images/events/XBDT.jpg", imgUrl: "https://assets.tech.troyrc.com/sx25/images/events/XBDT.jpg",
areaData: $data.currentView === "area" ? $data.seatAreas : [], areaData: $data.currentView === "area" ? $data.seatAreas : [],
seatData: $data.currentView === "seat" ? $data.seatPositions : [] seatData: $data.currentView === "seat" ? $data.seatPositions : [],
selectedCodes: $data.selectedCodes
}), }),
h: common_vendor.o((...args) => $options.handleTouchEvent && $options.handleTouchEvent(...args)), h: common_vendor.o((...args) => $options.handleTouchEvent && $options.handleTouchEvent(...args)),
i: common_vendor.o((...args) => $options.handleTouchEvent && $options.handleTouchEvent(...args)), i: common_vendor.o((...args) => $options.handleTouchEvent && $options.handleTouchEvent(...args)),

View File

@ -6,9 +6,10 @@ class GestureHandler {
this.containerRect = container; this.containerRect = container;
this.panThreshold = 5; this.panThreshold = 5;
this.panStarted = false; this.panStarted = false;
this.context = context;
this.isMiniProgram = typeof common_vendor.wx$1 !== "undefined" || typeof common_vendor.index !== "undefined"; this.isMiniProgram = typeof common_vendor.wx$1 !== "undefined" || typeof common_vendor.index !== "undefined";
if (this.isMiniProgram) { if (this.isMiniProgram) {
common_vendor.index.__f__("log", "at pages/index/gesture-handler.js:13", "小程序环境,使用降级手势处理器"); common_vendor.index.__f__("log", "at pages/index/gesture-handler.js:14", "小程序环境,使用降级手势处理器");
this.catchEvent = this.createSimpleGestureHandler(); this.catchEvent = this.createSimpleGestureHandler();
return; return;
} }
@ -35,17 +36,16 @@ class GestureHandler {
"../../common/vendor.js".then((n) => n.index_es).then((AnyTouch) => { "../../common/vendor.js".then((n) => n.index_es).then((AnyTouch) => {
this.at = new AnyTouch.default(atOptions); this.at = new AnyTouch.default(atOptions);
this.setupGestures(); this.setupGestures();
common_vendor.index.__f__("log", "at pages/index/gesture-handler.js:44", "AnyTouch手势处理器已初始化"); common_vendor.index.__f__("log", "at pages/index/gesture-handler.js:45", "AnyTouch手势处理器已初始化");
}).catch((e) => { }).catch((e) => {
common_vendor.index.__f__("error", "at pages/index/gesture-handler.js:46", "AnyTouch加载失败:", e); common_vendor.index.__f__("error", "at pages/index/gesture-handler.js:47", "AnyTouch加载失败:", e);
this.catchEvent = this.createSimpleGestureHandler(); this.catchEvent = this.createSimpleGestureHandler();
}); });
} catch (e) { } catch (e) {
common_vendor.index.__f__("error", "at pages/index/gesture-handler.js:50", "AnyTouch初始化失败:", e); common_vendor.index.__f__("error", "at pages/index/gesture-handler.js:51", "AnyTouch初始化失败:", e);
this.catchEvent = this.createSimpleGestureHandler(); this.catchEvent = this.createSimpleGestureHandler();
} }
} }
// 创建小程序专用的简化手势处理器
createSimpleGestureHandler() { createSimpleGestureHandler() {
let isClick = true; let isClick = true;
let startPoint = null; let startPoint = null;
@ -135,7 +135,7 @@ class GestureHandler {
this.catchEvent(event); this.catchEvent(event);
} }
} catch (e) { } catch (e) {
common_vendor.index.__f__("error", "at pages/index/gesture-handler.js:178", "手势处理错误:", e); common_vendor.index.__f__("error", "at pages/index/gesture-handler.js:176", "手势处理错误:", e);
} }
} }
// 基础平移手势处理 // 基础平移手势处理

View File

@ -21,7 +21,8 @@ const _sfc_main = {
}) })
}, },
areaData: {}, areaData: {},
seatData: {} seatData: {},
selectedCodes: {}
}, },
data() { data() {
return { return {
@ -33,10 +34,17 @@ const _sfc_main = {
canvasDisplayHeight: 600, canvasDisplayHeight: 600,
canvasActualWidth: 800, canvasActualWidth: 800,
// 实际像素尺寸 // 实际像素尺寸
canvasActualHeight: 600 canvasActualHeight: 600,
nowSelectedCodes: this.selectedCodes
}; };
}, },
watch: { watch: {
selectedCodes: {
handler(newVal) {
this.nowSelectedCodes = newVal;
},
immediate: true
},
imgUrl: { imgUrl: {
handler(newUrl) { handler(newUrl) {
if (newUrl) if (newUrl)
@ -81,12 +89,16 @@ const _sfc_main = {
this.initCanvas(); this.initCanvas();
}, },
methods: { methods: {
invertPoint(x, y) {
const inverted = this.matrix.invertPoint(x, y);
return inverted;
},
// 添加点击检测方法 // 添加点击检测方法
checkHitArea(x, y) { checkHitArea(x, y) {
if (!this.areaData) if (!this.areaData)
return null; return null;
const inverted = this.matrix.invertPoint(x, y, this.dpr); const inverted = this.matrix.invertPoint(x, y, this.dpr);
common_vendor.index.__f__("log", "at pages/index/transform-canvas.vue:92", "checkHitArea", inverted); common_vendor.index.__f__("log", "at pages/index/transform-canvas.vue:107", "checkHitArea", inverted);
for (const area of this.areaData) { for (const area of this.areaData) {
if (this.pointInPolygon(inverted.x, inverted.y, area.polygon)) { if (this.pointInPolygon(inverted.x, inverted.y, area.polygon)) {
return area; return area;
@ -173,7 +185,7 @@ const _sfc_main = {
}); });
this.redraw(); this.redraw();
} catch (e) { } catch (e) {
common_vendor.index.__f__("error", "at pages/index/transform-canvas.vue:223", "图片加载失败:", e); common_vendor.index.__f__("error", "at pages/index/transform-canvas.vue:238", "图片加载失败:", e);
this.image = null; this.image = null;
} }
}, },
@ -242,16 +254,40 @@ const _sfc_main = {
}); });
}, },
drawSeats() { drawSeats() {
const scale = Math.sqrt(this.matrix.a * this.matrix.a + this.matrix.b * this.matrix.b); this.ctx.save();
const { a, b, c, d, tx, ty } = this.matrix;
this.ctx.setTransform(a, b, c, d, tx, ty);
const scale = Math.sqrt(a * a + b * b);
this.seatData.forEach((seat) => { this.seatData.forEach((seat) => {
const x = seat.x; const x = seat.x;
const y = seat.y; const y = seat.y;
this.ctx.beginPath();
const radius = 8 / scale; const radius = 8 / scale;
this.ctx.arc(x, y, radius, 0, Math.PI * 2); if (this.nowSelectedCodes.has(seat.code) && seat.status === 1) {
this.ctx.fillStyle = seat.status === 1 ? "#4cd964" : "#dd524d"; this.ctx.beginPath();
this.ctx.fill(); this.ctx.arc(x, y, radius + 1, 0, Math.PI * 2);
this.ctx.strokeStyle = "#FFD700";
this.ctx.lineWidth = 2 / scale;
this.ctx.stroke();
this.ctx.beginPath();
this.ctx.arc(x, y, radius, 0, Math.PI * 2);
this.ctx.fillStyle = "rgba(255, 215, 0, 0.3)";
this.ctx.fill();
this.ctx.strokeStyle = "#fff";
this.ctx.lineWidth = 1 / scale;
this.ctx.lineCap = "round";
this.ctx.beginPath();
this.ctx.moveTo(x - 2.5, y);
this.ctx.lineTo(x - 0.5, y + 1.5);
this.ctx.lineTo(x + 2.5, y - 2);
this.ctx.stroke();
} else {
this.ctx.beginPath();
this.ctx.arc(x, y, radius, 0, Math.PI * 2);
this.ctx.fillStyle = seat.status === 1 ? "#4cd964" : "#dd524d";
this.ctx.fill();
}
}); });
this.ctx.restore();
} }
} }
}; };