canvas/pages/index/gesture-canvas-page.vue

299 lines
7.4 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>
</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) => {
// 只处理最基本的行为
if (event.touches.length > 0) {
const firstTouch = event.touches[0];
const x = firstTouch.clientX - this.containerRect.left;
const y = firstTouch.clientY - this.containerRect.top;
// 仅更新调试信息显示
this.gestureStatus = "降级模式: 单指拖动";
this.touchPoints = event.touches.length;
// 简单变换
if (event.type === 'touchmove') {
this.transformMatrix.tx = x;
this.transformMatrix.ty = y;
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) {
if (!this.gestureHandler || !this.containerRect) {
console.warn('手势处理器未就绪,尝试重新初始化...');
await this.getContainerPosition();
this.initGestureHandler();
if (event.type === 'touchend' || event.type === 'touchcancel') {
this.lastPoint = null;
this.lastPoints = null;
this.gestureStatus = '结束';
// 更新一次画面
this.updateCanvas();
return;
}
// 重新处理当前事件
if (this.gestureHandler) {
return this.handleTouchEvent(event);
}
// 如果是简单处理器,使用下面优化的基础处理方法
if (!this.at) {
// 使用优化后的基础处理方法
if (correctedEvent.touches.length === 1) {
this.handlePan(correctedEvent.touches[0]);
} else if (correctedEvent.touches.length > 1) {
this.handlePinch(correctedEvent.touches);
}
}
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();
},
}
};
</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>