286 lines
7.1 KiB
Vue
286 lines
7.1 KiB
Vue
<template>
|
||
<view class="container">
|
||
<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"
|
||
/>
|
||
</view>
|
||
|
||
<view class="controls">
|
||
<button @click="resetCanvas">重置</button>
|
||
<button @click="zoomIn">放大</button>
|
||
<button @click="zoomOut">缩小</button>
|
||
<button @click="logMatrix">调试</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: 300,
|
||
canvasHeight: 300,
|
||
transformMatrix: new TransformMatrix(),
|
||
gestureHandler: null,
|
||
containerRect: null,
|
||
gestureStatus: '等待手势...',
|
||
scaleValue: 1,
|
||
touchPoints: 0,
|
||
lastGestureTime: 0
|
||
};
|
||
},
|
||
|
||
async mounted() {
|
||
await this.getContainerPosition();
|
||
|
||
// 2. 初始化手势处理器(传入容器位置信息)
|
||
this.initGestureHandler();
|
||
|
||
// 3. 初始绘制
|
||
this.$nextTick(() => {
|
||
this.updateCanvas();
|
||
});
|
||
},
|
||
|
||
methods: {
|
||
// 独立的手势初始化方法
|
||
initGestureHandler() {
|
||
try {
|
||
this.gestureHandler = new GestureHandler(this, this.transformMatrix, {
|
||
container: this.containerRect
|
||
});
|
||
} catch (e) {
|
||
console.error('手势处理器初始化失败:', e);
|
||
// 创建降级方案
|
||
this.gestureHandler = {
|
||
catchEvent: (event) => {
|
||
// 基本手势处理
|
||
const touches = event.touches || [];
|
||
if (touches.length > 0) {
|
||
const point = touches[0];
|
||
const x = point.clientX - this.containerRect.left;
|
||
const y = point.clientY - this.containerRect.top;
|
||
|
||
// 简单移动逻辑
|
||
this.transformMatrix.tx = x - 50;
|
||
this.transformMatrix.ty = y - 50;
|
||
this.updateCanvas();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
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
|
||
};
|
||
console.log('成功获取容器位置:', this.containerRect);
|
||
return; // 成功获取则退出
|
||
}
|
||
} catch (e) {
|
||
console.error('获取容器位置失败:', e);
|
||
}
|
||
|
||
// 等待后重试
|
||
await new Promise(r => setTimeout(r, 100));
|
||
retryCount++;
|
||
}
|
||
|
||
console.error(`容器位置获取失败,已重试${maxRetries}次`);
|
||
},
|
||
// 事件处理(完整修复)
|
||
async handleTouchEvent(event) {
|
||
// 修复点1:确保处理器就绪的防护代码
|
||
if (!this.gestureHandler || !this.containerRect) {
|
||
console.warn('手势处理器未就绪,尝试重新初始化...');
|
||
await this.getContainerPosition();
|
||
this.initGestureHandler();
|
||
|
||
// 重新处理当前事件
|
||
if (this.gestureHandler) {
|
||
return this.handleTouchEvent(event);
|
||
}
|
||
return;
|
||
}
|
||
|
||
|
||
// 记录时间戳
|
||
const currentTime = Date.now();
|
||
|
||
// 更新触点数量
|
||
this.touchPoints = event.touches.length;
|
||
|
||
// 修正坐标(核心修复点)
|
||
const correctedTouches = Array.from(event.touches).map(touch => {
|
||
const x = touch.clientX - this.containerRect.left;
|
||
const y = touch.clientY - this.containerRect.top;
|
||
|
||
console.log(`原始坐标: (${touch.clientX}, ${touch.clientY}) 修正后: (${x}, ${y})`);
|
||
|
||
return {
|
||
...touch,
|
||
x: x,
|
||
y: y
|
||
};
|
||
});
|
||
|
||
// 创建修正后的事件对象
|
||
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 = '结束';
|
||
}
|
||
|
||
// 限制更新频率:至少每50ms更新一次
|
||
if (currentTime - this.lastGestureTime > 50) {
|
||
this.lastGestureTime = currentTime;
|
||
this.updateCanvas();
|
||
}
|
||
},
|
||
|
||
// 更新Canvas(性能优化版)
|
||
updateCanvas() {
|
||
this.$nextTick(() => {
|
||
const canvas = this.$refs.canvasRef;
|
||
if (canvas) {
|
||
canvas.draw();
|
||
|
||
// 计算当前缩放值
|
||
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();
|
||
},
|
||
|
||
// 调试输出
|
||
logMatrix() {
|
||
console.log('当前变换矩阵:', this.transformMatrix.toArray());
|
||
console.log('容器位置:', this.containerRect);
|
||
if (this.$refs.canvasRef) {
|
||
this.$refs.canvasRef.drawDebugGrid();
|
||
}
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.container {
|
||
padding: 20px;
|
||
}
|
||
|
||
.gesture-container {
|
||
position: relative;
|
||
width: 300px;
|
||
height: 300px;
|
||
border: 1px solid #ccc;
|
||
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> |