add 增加手势js 和 变换矩阵js 融合 canvas测试处理
This commit is contained in:
parent
0283e1b651
commit
0c82be5f58
@ -1,7 +1,7 @@
|
||||
{
|
||||
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
|
||||
{
|
||||
"path" : "pages/index/hammerjsTest/hammerjsTest",
|
||||
"path" : "pages/index/gesture-canvas-page",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : ""
|
||||
|
286
pages/index/gesture-canvas-page.vue
Normal file
286
pages/index/gesture-canvas-page.vue
Normal file
@ -0,0 +1,286 @@
|
||||
<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>
|
191
pages/index/gesture-handler.js
Normal file
191
pages/index/gesture-handler.js
Normal file
@ -0,0 +1,191 @@
|
||||
import AnyTouch from 'any-touch'
|
||||
|
||||
class GestureHandler {
|
||||
constructor(context, transformMatrix, { container }) {
|
||||
this.transformMatrix = transformMatrix;
|
||||
this.containerRect = container;
|
||||
|
||||
// 小程序环境兼容处理
|
||||
const atOptions = {
|
||||
getPoint: touch => ({
|
||||
x: touch.clientX - this.containerRect.left,
|
||||
y: touch.clientY - this.containerRect.top
|
||||
}),
|
||||
preventDefault: false
|
||||
};
|
||||
|
||||
// 小程序特化处理 - 跳过DOM相关操作
|
||||
if (typeof window === 'undefined' || typeof HTMLElement === 'undefined') {
|
||||
// 小程序环境下不需要设置DOM属性
|
||||
atOptions._element = {
|
||||
style: {},
|
||||
addEventListener: () => {},
|
||||
removeEventListener: () => {}
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
this.at = new AnyTouch(atOptions);
|
||||
} catch (e) {
|
||||
console.error('AnyTouch初始化失败:', e);
|
||||
// 降级方案:在小程序环境实现基本手势
|
||||
this.handleGesture = this.createSimpleGestureHandler();
|
||||
}
|
||||
|
||||
if (this.at) {
|
||||
this.setupGestures();
|
||||
console.log('AnyTouch手势处理器已初始化');
|
||||
} else {
|
||||
console.warn('使用简化手势处理器');
|
||||
}
|
||||
}
|
||||
|
||||
// 创建小程序专用的简化手势处理器
|
||||
createSimpleGestureHandler() {
|
||||
return {
|
||||
run: (event) => {
|
||||
// 小程序环境下的基本手势实现
|
||||
const touches = event.touches || [];
|
||||
|
||||
if (touches.length === 1) {
|
||||
// 单指移动
|
||||
this.handlePan(touches[0]);
|
||||
} else if (touches.length > 1) {
|
||||
// 双指缩放
|
||||
this.handlePinch(touches);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
setupGestures() {
|
||||
// 平移手势 - 修复版本
|
||||
this.at.on('pan', (event) => {
|
||||
if (event.type === 'panstart') {
|
||||
console.log('panstart', event);
|
||||
}
|
||||
else if (event.type === 'panmove') {
|
||||
// 关键修复:基于当前缩放比例修正移动量
|
||||
const currentScale = Math.sqrt(
|
||||
this.transformMatrix.a * this.transformMatrix.a +
|
||||
this.transformMatrix.b * this.transformMatrix.b
|
||||
);
|
||||
|
||||
// 逆缩放移动量
|
||||
const dx = event.deltaX / currentScale;
|
||||
const dy = event.deltaY / currentScale;
|
||||
|
||||
console.log(`平移: deltaX=${event.deltaX}, deltaY=${event.deltaY} | 修正后: dx=${dx}, dy=${dy}`);
|
||||
|
||||
this.transformMatrix.translate(dx, dy);
|
||||
}
|
||||
});
|
||||
|
||||
// 缩放手势 - 修复版本
|
||||
this.at.on('pinch', (event) => {
|
||||
if (event.type === 'pinchstart') {
|
||||
this.lastScale = 1;
|
||||
}
|
||||
else if (event.type === 'pinchmove') {
|
||||
// 计算缩放变化量
|
||||
const scaleChange = event.scale / this.lastScale;
|
||||
this.lastScale = event.scale;
|
||||
|
||||
// 使用两指中心点
|
||||
const centerX = event.center.x;
|
||||
const centerY = event.center.y;
|
||||
|
||||
console.log(`缩放: scale=${event.scale} | 变化=${scaleChange} | 中心点: (${centerX}, ${centerY})`);
|
||||
|
||||
// 应用缩放
|
||||
this.transformMatrix.scale(scaleChange, scaleChange, centerX, centerY);
|
||||
}
|
||||
});
|
||||
|
||||
// 点击手势
|
||||
this.at.on('tap', (event) => {
|
||||
console.log('点击事件', event);
|
||||
});
|
||||
|
||||
// 长按手势
|
||||
this.at.on('press', (event) => {
|
||||
console.log('长按事件', event);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
catchEvent(event) {
|
||||
try {
|
||||
if (this.at) {
|
||||
this.at.run(event);
|
||||
} else if (this.handleGesture) {
|
||||
this.handleGesture.run(event);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('手势处理错误:', e);
|
||||
// 降级处理
|
||||
const touches = event.touches || [];
|
||||
if (touches.length > 0) {
|
||||
this.handlePan(touches[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 基础平移手势处理
|
||||
handlePan(touch) {
|
||||
if (this.lastPoint) {
|
||||
const dx = touch.clientX - this.lastPoint.x;
|
||||
const dy = touch.clientY - this.lastPoint.y;
|
||||
|
||||
// 根据当前缩放比例修正移动量
|
||||
const currentScale = Math.sqrt(
|
||||
this.transformMatrix.a * this.transformMatrix.a +
|
||||
this.transformMatrix.b * this.transformMatrix.b
|
||||
);
|
||||
|
||||
const correctedDx = dx / currentScale;
|
||||
const correctedDy = dy / currentScale;
|
||||
|
||||
this.transformMatrix.translate(correctedDx, correctedDy);
|
||||
}
|
||||
this.lastPoint = {x: touch.clientX, y: touch.clientY};
|
||||
}
|
||||
|
||||
// 基础缩放手势处理
|
||||
handlePinch(touches) {
|
||||
const point1 = touches[0];
|
||||
const point2 = touches[1];
|
||||
|
||||
if (this.lastPoints) {
|
||||
// 计算当前距离
|
||||
const currentDistance = Math.sqrt(
|
||||
Math.pow(point2.clientX - point1.clientX, 2) +
|
||||
Math.pow(point2.clientY - point1.clientY, 2)
|
||||
);
|
||||
|
||||
// 上次距离
|
||||
const prevDistance = Math.sqrt(
|
||||
Math.pow(this.lastPoints[1].x - this.lastPoints[0].x, 2) +
|
||||
Math.pow(this.lastPoints[1].y - this.lastPoints[0].y, 2)
|
||||
);
|
||||
|
||||
// 缩放比例
|
||||
if (prevDistance > 0) {
|
||||
const scaleChange = currentDistance / prevDistance;
|
||||
|
||||
// 计算中心点
|
||||
const centerX = (point1.clientX + point2.clientX) / 2 - this.containerRect.left;
|
||||
const centerY = (point1.clientY + point2.clientY) / 2 - this.containerRect.top;
|
||||
|
||||
this.transformMatrix.scale(scaleChange, scaleChange, centerX, centerY);
|
||||
}
|
||||
}
|
||||
|
||||
this.lastPoints = [
|
||||
{x: point1.clientX, y: point1.clientY},
|
||||
{x: point2.clientX, y: point2.clientY}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export default GestureHandler;
|
@ -1,69 +0,0 @@
|
||||
<template>
|
||||
<any-touch
|
||||
:pan-options="{ threshold: 10 }"
|
||||
:pinch-options="{ threshold: 0.1 }"
|
||||
@tap="handleTap"
|
||||
@pan="handlePan"
|
||||
@swipe="handleSwipe"
|
||||
@pinch="handlePinch"
|
||||
@press="handlePress"
|
||||
@rotate="handleRotate"
|
||||
>
|
||||
<view class="gesture-box">手势识别区域</view>
|
||||
</any-touch>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
// 基本点击
|
||||
handleTap(e) {
|
||||
console.log('Tap:', e)
|
||||
},
|
||||
|
||||
// 平移拖拽
|
||||
handlePan(e) {
|
||||
const { displacement, deltaX, deltaY } = e
|
||||
console.log(`Pan: X=${deltaX}, Y=${deltaY}, 位移=${displacement}px`)
|
||||
},
|
||||
|
||||
// 快速滑动
|
||||
handleSwipe(e) {
|
||||
const { direction } = e
|
||||
const dirMap = {
|
||||
left: '向左',
|
||||
right: '向右',
|
||||
up: '向上',
|
||||
down: '向下'
|
||||
}
|
||||
console.log(`Swipe: ${dirMap[direction]}`)
|
||||
},
|
||||
|
||||
// 缩放
|
||||
handlePinch(e) {
|
||||
console.log(`缩放比例: ${e.scale.toFixed(2)}倍`)
|
||||
},
|
||||
|
||||
// 长按
|
||||
handlePress(e) {
|
||||
console.log(`长按 ${e.duration}ms`)
|
||||
},
|
||||
|
||||
// 旋转
|
||||
handleRotate(e) {
|
||||
console.log(`旋转角度: ${e.angle.toFixed(1)}°`)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.gesture-box {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
background-color: #f0f9ff;
|
||||
border: 1px solid #409eff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 20px auto;
|
||||
}
|
||||
</style>
|
154
pages/index/transform-canvas.vue
Normal file
154
pages/index/transform-canvas.vue
Normal file
@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<canvas
|
||||
canvas-id="gestureCanvas"
|
||||
:style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
width: Number,
|
||||
height: Number,
|
||||
matrix: Object
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
ctx: null,
|
||||
canvasWidth: this.width,
|
||||
canvasHeight: this.height
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.initCanvas();
|
||||
},
|
||||
|
||||
methods: {
|
||||
initCanvas() {
|
||||
this.ctx = uni.createCanvasContext('gestureCanvas', this);
|
||||
const pixelRatio = uni.getSystemInfoSync().pixelRatio;
|
||||
this.ctx.scale(pixelRatio, pixelRatio);
|
||||
},
|
||||
|
||||
applyTransform(matrix) {
|
||||
if (!matrix) return;
|
||||
|
||||
// 核心修复:考虑像素比的转换
|
||||
const pixelRatio = uni.getSystemInfoSync().pixelRatio;
|
||||
const tx = matrix.tx * pixelRatio;
|
||||
const ty = matrix.ty * pixelRatio;
|
||||
|
||||
// 直接设置完整的变换矩阵
|
||||
this.ctx.setTransform(
|
||||
matrix.a,
|
||||
matrix.b,
|
||||
matrix.c,
|
||||
matrix.d,
|
||||
tx,
|
||||
ty
|
||||
);
|
||||
},
|
||||
|
||||
// 调试网格
|
||||
drawDebugGrid() {
|
||||
if (!this.ctx) return;
|
||||
|
||||
const pixelRatio = uni.getSystemInfoSync().pixelRatio;
|
||||
const width = this.canvasWidth * pixelRatio;
|
||||
const height = this.canvasHeight * pixelRatio;
|
||||
|
||||
// 保存当前状态
|
||||
this.ctx.save();
|
||||
|
||||
// 重置变换
|
||||
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
|
||||
// 绘制网格
|
||||
this.ctx.strokeStyle = 'rgba(200, 200, 200, 0.3)';
|
||||
this.ctx.lineWidth = 1;
|
||||
|
||||
// 横向线
|
||||
for (let y = 0; y <= height; y += 50) {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(0, y);
|
||||
this.ctx.lineTo(width, y);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
// 纵向线
|
||||
for (let x = 0; x <= width; x += 50) {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(x, 0);
|
||||
this.ctx.lineTo(x, height);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
// 绘制坐标轴
|
||||
this.ctx.strokeStyle = '#ff0000';
|
||||
this.ctx.lineWidth = 2;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(0, 0);
|
||||
this.ctx.lineTo(width, 0);
|
||||
this.ctx.stroke();
|
||||
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(0, 0);
|
||||
this.ctx.lineTo(0, height);
|
||||
this.ctx.stroke();
|
||||
|
||||
// 恢复之前的状态
|
||||
this.ctx.restore();
|
||||
|
||||
this.ctx.draw();
|
||||
},
|
||||
|
||||
// 绘制主内容
|
||||
draw() {
|
||||
if (!this.ctx) {
|
||||
console.warn('Canvas上下文未初始化');
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 清除Canvas
|
||||
this.clearCanvas();
|
||||
|
||||
// 2. 应用当前变换
|
||||
this.applyTransform(this.matrix);
|
||||
|
||||
// 3. 绘制内容
|
||||
this.drawContent();
|
||||
},
|
||||
|
||||
clearCanvas() {
|
||||
const pixelRatio = uni.getSystemInfoSync().pixelRatio;
|
||||
const width = this.canvasWidth * pixelRatio;
|
||||
const height = this.canvasHeight * pixelRatio;
|
||||
|
||||
// 重置变换
|
||||
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
this.ctx.clearRect(0, 0, width, height);
|
||||
},
|
||||
|
||||
drawContent() {
|
||||
// 绘制一个矩形和圆形
|
||||
this.ctx.fillStyle = '#3498db';
|
||||
this.ctx.fillRect(50, 50, 100, 100);
|
||||
|
||||
this.ctx.fillStyle = '#e74c3c';
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(150, 150, 50, 0, 2 * Math.PI);
|
||||
this.ctx.fill();
|
||||
|
||||
// 绘制一个文字
|
||||
this.ctx.fillStyle = '#2c3e50';
|
||||
this.ctx.font = '20px sans-serif';
|
||||
this.ctx.fillText('手势Canvas', 50, 250);
|
||||
|
||||
// 执行绘制
|
||||
this.ctx.draw();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
57
pages/index/transform-matrix.js
Normal file
57
pages/index/transform-matrix.js
Normal file
@ -0,0 +1,57 @@
|
||||
class TransformMatrix {
|
||||
constructor() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.a = 1; // 水平缩放
|
||||
this.b = 0; // 垂直倾斜
|
||||
this.c = 0; // 水平倾斜
|
||||
this.d = 1; // 垂直缩放
|
||||
this.tx = 0; // 水平移动
|
||||
this.ty = 0; // 垂直移动
|
||||
this.stack = [];
|
||||
}
|
||||
|
||||
translate(dx, dy) {
|
||||
this.tx += this.a * dx + this.c * dy;
|
||||
this.ty += this.b * dx + this.d * dy;
|
||||
}
|
||||
|
||||
scale(sx, sy, cx = 0, cy = 0) {
|
||||
// 移动到中心点
|
||||
this.translate(cx, cy);
|
||||
|
||||
// 应用缩放
|
||||
this.a *= sx;
|
||||
this.b *= sx;
|
||||
this.c *= sy;
|
||||
this.d *= sy;
|
||||
|
||||
// 移回原位置
|
||||
this.translate(-cx, -cy);
|
||||
}
|
||||
|
||||
toArray() {
|
||||
return [this.a, this.b, this.c, this.d, this.tx, this.ty];
|
||||
}
|
||||
|
||||
// 用于调试的字符串表示
|
||||
toString() {
|
||||
return `[${this.a.toFixed(2)}, ${this.b.toFixed(2)}, ${this.c.toFixed(2)}, ${this.d.toFixed(2)}, ${this.tx.toFixed(1)}, ${this.ty.toFixed(1)}]`;
|
||||
}
|
||||
|
||||
// 克隆方法
|
||||
clone() {
|
||||
const clone = new TransformMatrix();
|
||||
clone.a = this.a;
|
||||
clone.b = this.b;
|
||||
clone.c = this.c;
|
||||
clone.d = this.d;
|
||||
clone.tx = this.tx;
|
||||
clone.ty = this.ty;
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
||||
export default TransformMatrix;
|
11
unpackage/dist/dev/.nvue/app.css.js
vendored
Normal file
11
unpackage/dist/dev/.nvue/app.css.js
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __commonJS = (cb, mod) => function __require() {
|
||||
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
||||
};
|
||||
var require_app_css = __commonJS({
|
||||
"app.css.js"(exports) {
|
||||
const _style_0 = {};
|
||||
exports.styles = [_style_0];
|
||||
}
|
||||
});
|
||||
export default require_app_css();
|
2
unpackage/dist/dev/.nvue/app.js
vendored
Normal file
2
unpackage/dist/dev/.nvue/app.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
Promise.resolve("./app.css.js").then(() => {
|
||||
});
|
File diff suppressed because one or more lines are too long
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/CanvasMatrix.js.map
vendored
Normal file
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/CanvasMatrix.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/GestureRecognizer.js.map
vendored
Normal file
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/GestureRecognizer.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"GestureRecognizer.js","sources":["pages/index/GestureRecognizer.js"],"sourcesContent":["import MiniProgramAdapter from './MiniProgramAdapter';\n// 独立的手势库组件\nexport default class GestureRecognizer {\n constructor() {\n this.adapter = new MiniProgramAdapter();\n this.touches = [];\n this.gestures = {};\n this.activeGestures = {};\n this.containerRect = null;\n this.callbacks = {};\n }\n\n initContainer(rect) {\n this.containerRect = rect;\n }\n\n // 注册手势回调\n on(event, callback) {\n\t\tif (!this.callbacks[event]) {\n\t\t\tthis.callbacks[event] = [];\n\t\t}\n\t\tthis.callbacks[event].push(callback);\n\t}\n\t\n\t trigger(event, data) {\n\t const handlers = this.callbacks[event] || [];\n\t handlers.forEach(handler => {\n\t handler({\n\t type: event,\n\t originEvent: this.currentEvent,\n\t containerRect: this.containerRect,\n\t ...data\n\t });\n\t });\n\t }\n\t\t\n // 处理触摸事件\n handleTouchEvent(type, event) {\n\t\t// 更新触点信息\n\t\tthis.updateTouches(event);\n\t\t\n\t\t// 根据类型处理\n\t\tswitch(type) {\n\t\t\tcase 'touchstart':\n\t\t\t\treturn this.handleTouchStart(event);\n\t\t\tcase 'touchmove':\n\t\t\t\treturn this.handleTouchMove(event);\n\t\t\tdefault:\n\t\t\t\treturn this.handleTouchEnd(event);\n\t\t}\n\t}\n\n\t// 处理缩放手势\n\t detectPinchMove() {\n\t if (!this.activeGestures.pinch) return;\n\t \n\t const currentDistance = this.calculateFingerDistance();\n\t const deltaDistance = currentDistance - this.activeGestures.pinch.lastDistance;\n\t \n\t // 计算缩放比例\n\t const scaleFactor = currentDistance / this.activeGestures.pinch.startDistance;\n\t \n\t // 触发事件\n\t this.triggerGesture('pinch', {\n\t scale: scaleFactor,\n\t center: this.calculateCenter()\n\t });\n\t\t\t\n\t \n\t // 保存最后距离\n\t this.activeGestures.pinch.lastDistance = currentDistance;\n\t }\n\n // 触发手势事件\n triggerGesture(name, data) {\n const handlers = this.callbacks[name] || [];\n handlers.forEach(handler => {\n handler({\n type: name,\n originEvent: this.currentEvent,\n containerRect: this.containerRect,\n ...data\n });\n });\n }\n }\t\n"],"names":["MiniProgramAdapter"],"mappings":";;AAEe,MAAM,kBAAkB;AAAA,EACrC,cAAc;AACZ,SAAK,UAAU,IAAIA,+BAAAA;AACnB,SAAK,UAAU;AACf,SAAK,WAAW;AAChB,SAAK,iBAAiB;AACtB,SAAK,gBAAgB;AACrB,SAAK,YAAY;EAClB;AAAA,EAED,cAAc,MAAM;AAClB,SAAK,gBAAgB;AAAA,EACtB;AAAA;AAAA,EAGD,GAAG,OAAO,UAAU;AACpB,QAAI,CAAC,KAAK,UAAU,KAAK,GAAG;AAC3B,WAAK,UAAU,KAAK,IAAI;IACxB;AACD,SAAK,UAAU,KAAK,EAAE,KAAK,QAAQ;AAAA,EACnC;AAAA,EAEA,QAAQ,OAAO,MAAM;AAClB,UAAM,WAAW,KAAK,UAAU,KAAK,KAAK,CAAA;AAC1C,aAAS,QAAQ,aAAW;AAC1B,cAAQ;AAAA,QACN,MAAM;AAAA,QACN,aAAa,KAAK;AAAA,QAClB,eAAe,KAAK;AAAA,QACpB,GAAG;AAAA,MACZ,CAAQ;AAAA,IACR,CAAM;AAAA,EACF;AAAA;AAAA,EAGF,iBAAiB,MAAM,OAAO;AAE9B,SAAK,cAAc,KAAK;AAGxB,YAAO,MAAI;AAAA,MACV,KAAK;AACJ,eAAO,KAAK,iBAAiB,KAAK;AAAA,MACnC,KAAK;AACJ,eAAO,KAAK,gBAAgB,KAAK;AAAA,MAClC;AACC,eAAO,KAAK,eAAe,KAAK;AAAA,IACjC;AAAA,EACD;AAAA;AAAA,EAGC,kBAAkB;AAChB,QAAI,CAAC,KAAK,eAAe;AAAO;AAEhC,UAAM,kBAAkB,KAAK;AACP,sBAAkB,KAAK,eAAe,MAAM;AAGlE,UAAM,cAAc,kBAAkB,KAAK,eAAe,MAAM;AAGhE,SAAK,eAAe,SAAS;AAAA,MAC3B,OAAO;AAAA,MACP,QAAQ,KAAK,gBAAiB;AAAA,IACrC,CAAM;AAID,SAAK,eAAe,MAAM,eAAe;AAAA,EAC1C;AAAA;AAAA,EAGF,eAAe,MAAM,MAAM;AACzB,UAAM,WAAW,KAAK,UAAU,IAAI,KAAK,CAAA;AACzC,aAAS,QAAQ,aAAW;AAC1B,cAAQ;AAAA,QACN,MAAM;AAAA,QACN,aAAa,KAAK;AAAA,QAClB,eAAe,KAAK;AAAA,QACpB,GAAG;AAAA,MACX,CAAO;AAAA,IACP,CAAK;AAAA,EACF;AACH;;"}
|
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/MiniGesture.js.map
vendored
Normal file
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/MiniGesture.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/MiniProgramAdapter.js.map
vendored
Normal file
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/MiniProgramAdapter.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/canvasRenderer.js.map
vendored
Normal file
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/canvasRenderer.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"canvasRenderer.js","sources":["pages/index/canvasRenderer.vue?type=page"],"sourcesContent":["import MiniProgramPage from '/Users/sunmeng/Desktop/wx/canvas/pages/index/canvasRenderer.vue'\nwx.createPage(MiniProgramPage)"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,GAAG,WAAW,eAAe;"}
|
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/gesture-canvas-page.js.map
vendored
Normal file
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/gesture-canvas-page.js.map
vendored
Normal file
@ -0,0 +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;"}
|
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/gesture-handler.js.map
vendored
Normal file
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/gesture-handler.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
{"version":3,"file":"index.js","sources":["pages/index/index.vue?type=page"],"sourcesContent":["import MiniProgramPage from '/Users/sunmeng/Desktop/wx/canvas/pages/index/index.vue'\nwx.createPage(MiniProgramPage)"],"names":["MiniProgramPage"],"mappings":";;AACA,GAAG,WAAWA,MAAe,eAAA;"}
|
||||
{"version":3,"file":"index.js","sources":["pages/index/index.vue?type=page"],"sourcesContent":["import MiniProgramPage from '/Users/sunmeng/Desktop/wx/canvas/pages/index/index.vue'\nwx.createPage(MiniProgramPage)"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,GAAG,WAAW,eAAe;"}
|
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/transform-canvas.js.map
vendored
Normal file
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/transform-canvas.js.map
vendored
Normal file
@ -0,0 +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;"}
|
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/transform-matrix.js.map
vendored
Normal file
1
unpackage/dist/dev/.sourcemap/mp-weixin/pages/index/transform-matrix.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"transform-matrix.js","sources":["pages/index/transform-matrix.js"],"sourcesContent":["class TransformMatrix {\n constructor() {\n this.reset();\n }\n \n reset() {\n this.a = 1; // 水平缩放\n this.b = 0; // 垂直倾斜\n this.c = 0; // 水平倾斜\n this.d = 1; // 垂直缩放\n this.tx = 0; // 水平移动\n this.ty = 0; // 垂直移动\n this.stack = [];\n }\n \n translate(dx, dy) {\n this.tx += this.a * dx + this.c * dy;\n this.ty += this.b * dx + this.d * dy;\n }\n \n scale(sx, sy, cx = 0, cy = 0) {\n // 移动到中心点\n this.translate(cx, cy);\n \n // 应用缩放\n this.a *= sx;\n this.b *= sx;\n this.c *= sy;\n this.d *= sy;\n \n // 移回原位置\n this.translate(-cx, -cy);\n }\n \n toArray() {\n return [this.a, this.b, this.c, this.d, this.tx, this.ty];\n }\n \n // 用于调试的字符串表示\n toString() {\n return `[${this.a.toFixed(2)}, ${this.b.toFixed(2)}, ${this.c.toFixed(2)}, ${this.d.toFixed(2)}, ${this.tx.toFixed(1)}, ${this.ty.toFixed(1)}]`;\n }\n \n // 克隆方法\n clone() {\n const clone = new TransformMatrix();\n clone.a = this.a;\n clone.b = this.b;\n clone.c = this.c;\n clone.d = this.d;\n clone.tx = this.tx;\n clone.ty = this.ty;\n return clone;\n }\n}\n\nexport default TransformMatrix;"],"names":[],"mappings":";AAAA,MAAM,gBAAgB;AAAA,EACpB,cAAc;AACZ,SAAK,MAAK;AAAA,EACX;AAAA,EAED,QAAQ;AACN,SAAK,IAAI;AACT,SAAK,IAAI;AACT,SAAK,IAAI;AACT,SAAK,IAAI;AACT,SAAK,KAAK;AACV,SAAK,KAAK;AACV,SAAK,QAAQ;EACd;AAAA,EAED,UAAU,IAAI,IAAI;AAChB,SAAK,MAAM,KAAK,IAAI,KAAK,KAAK,IAAI;AAClC,SAAK,MAAM,KAAK,IAAI,KAAK,KAAK,IAAI;AAAA,EACnC;AAAA,EAED,MAAM,IAAI,IAAI,KAAK,GAAG,KAAK,GAAG;AAE5B,SAAK,UAAU,IAAI,EAAE;AAGrB,SAAK,KAAK;AACV,SAAK,KAAK;AACV,SAAK,KAAK;AACV,SAAK,KAAK;AAGV,SAAK,UAAU,CAAC,IAAI,CAAC,EAAE;AAAA,EACxB;AAAA,EAED,UAAU;AACR,WAAO,CAAC,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,IAAI,KAAK,EAAE;AAAA,EACzD;AAAA;AAAA,EAGD,WAAW;AACT,WAAO,IAAI,KAAK,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,GAAG,QAAQ,CAAC,CAAC,KAAK,KAAK,GAAG,QAAQ,CAAC,CAAC;AAAA,EAC7I;AAAA;AAAA,EAGD,QAAQ;AACN,UAAM,QAAQ,IAAI;AAClB,UAAM,IAAI,KAAK;AACf,UAAM,IAAI,KAAK;AACf,UAAM,IAAI,KAAK;AACf,UAAM,IAAI,KAAK;AACf,UAAM,KAAK,KAAK;AAChB,UAAM,KAAK,KAAK;AAChB,WAAO;AAAA,EACR;AACH;;"}
|
16
unpackage/dist/dev/app-plus/__uniappautomator.js
vendored
Normal file
16
unpackage/dist/dev/app-plus/__uniappautomator.js
vendored
Normal file
File diff suppressed because one or more lines are too long
32
unpackage/dist/dev/app-plus/__uniappchooselocation.js
vendored
Normal file
32
unpackage/dist/dev/app-plus/__uniappchooselocation.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
unpackage/dist/dev/app-plus/__uniapperror.png
vendored
Normal file
BIN
unpackage/dist/dev/app-plus/__uniapperror.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
32
unpackage/dist/dev/app-plus/__uniappopenlocation.js
vendored
Normal file
32
unpackage/dist/dev/app-plus/__uniappopenlocation.js
vendored
Normal file
File diff suppressed because one or more lines are too long
33
unpackage/dist/dev/app-plus/__uniapppicker.js
vendored
Normal file
33
unpackage/dist/dev/app-plus/__uniapppicker.js
vendored
Normal file
File diff suppressed because one or more lines are too long
8
unpackage/dist/dev/app-plus/__uniappquill.js
vendored
Normal file
8
unpackage/dist/dev/app-plus/__uniappquill.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
unpackage/dist/dev/app-plus/__uniappquillimageresize.js
vendored
Normal file
1
unpackage/dist/dev/app-plus/__uniappquillimageresize.js
vendored
Normal file
File diff suppressed because one or more lines are too long
32
unpackage/dist/dev/app-plus/__uniappscan.js
vendored
Normal file
32
unpackage/dist/dev/app-plus/__uniappscan.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
unpackage/dist/dev/app-plus/__uniappsuccess.png
vendored
Normal file
BIN
unpackage/dist/dev/app-plus/__uniappsuccess.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
24
unpackage/dist/dev/app-plus/__uniappview.html
vendored
Normal file
24
unpackage/dist/dev/app-plus/__uniappview.html
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>View</title>
|
||||
<link rel="icon" href="data:,">
|
||||
<link rel="stylesheet" href="app.css" />
|
||||
<script>var __uniConfig = {"globalStyle":{},"darkmode":false}</script>
|
||||
<script>
|
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||
CSS.supports('top: constant(a)'))
|
||||
document.write(
|
||||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="uni-app-view.umd.js"></script>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
11
unpackage/dist/dev/app-plus/app-config-service.js
vendored
Normal file
11
unpackage/dist/dev/app-plus/app-config-service.js
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
;(function(){
|
||||
let u=void 0,isReady=false,onReadyCallbacks=[],isServiceReady=false,onServiceReadyCallbacks=[];
|
||||
const __uniConfig = {"pages":[],"globalStyle":{"backgroundColor":"#F8F8F8","navigationBar":{"backgroundColor":"#F8F8F8","titleText":"uni-app","type":"default","titleColor":"#000000"},"isNVue":false},"nvue":{"compiler":"uni-app","styleCompiler":"uni-app","flex-direction":"column"},"renderer":"auto","appname":"canvas","splashscreen":{"alwaysShowBeforeRender":true,"autoclose":true},"compilerVersion":"4.57","entryPagePath":"pages/index/hammerjsTest/hammerjsTest","entryPageQuery":"","realEntryPagePath":"","networkTimeout":{"request":60000,"connectSocket":60000,"uploadFile":60000,"downloadFile":60000},"locales":{},"darkmode":false,"themeConfig":{}};
|
||||
const __uniRoutes = [{"path":"pages/index/hammerjsTest/hammerjsTest","meta":{"isQuit":true,"isEntry":true,"navigationBar":{"titleText":"","type":"default"},"isNVue":false}}].map(uniRoute=>(uniRoute.meta.route=uniRoute.path,__uniConfig.pages.push(uniRoute.path),uniRoute.path='/'+uniRoute.path,uniRoute));
|
||||
__uniConfig.styles=[];//styles
|
||||
__uniConfig.onReady=function(callback){if(__uniConfig.ready){callback()}else{onReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"ready",{get:function(){return isReady},set:function(val){isReady=val;if(!isReady){return}const callbacks=onReadyCallbacks.slice(0);onReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
|
||||
__uniConfig.onServiceReady=function(callback){if(__uniConfig.serviceReady){callback()}else{onServiceReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"serviceReady",{get:function(){return isServiceReady},set:function(val){isServiceReady=val;if(!isServiceReady){return}const callbacks=onServiceReadyCallbacks.slice(0);onServiceReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
|
||||
service.register("uni-app-config",{create(a,b,c){if(!__uniConfig.viewport){var d=b.weex.config.env.scale,e=b.weex.config.env.deviceWidth,f=Math.ceil(e/d);Object.assign(__uniConfig,{viewport:f,defaultFontSize:16})}return{instance:{__uniConfig:__uniConfig,__uniRoutes:__uniRoutes,global:u,window:u,document:u,frames:u,self:u,location:u,navigator:u,localStorage:u,history:u,Caches:u,screen:u,alert:u,confirm:u,prompt:u,fetch:u,XMLHttpRequest:u,WebSocket:u,webkit:u,print:u}}}});
|
||||
})();
|
||||
|
1
unpackage/dist/dev/app-plus/app-config.js
vendored
Normal file
1
unpackage/dist/dev/app-plus/app-config.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
(function(){})();
|
131
unpackage/dist/dev/app-plus/app-service.js
vendored
Normal file
131
unpackage/dist/dev/app-plus/app-service.js
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
if (typeof Promise !== "undefined" && !Promise.prototype.finally) {
|
||||
Promise.prototype.finally = function(callback) {
|
||||
const promise = this.constructor;
|
||||
return this.then(
|
||||
(value) => promise.resolve(callback()).then(() => value),
|
||||
(reason) => promise.resolve(callback()).then(() => {
|
||||
throw reason;
|
||||
})
|
||||
);
|
||||
};
|
||||
}
|
||||
;
|
||||
if (typeof uni !== "undefined" && uni && uni.requireGlobal) {
|
||||
const global = uni.requireGlobal();
|
||||
ArrayBuffer = global.ArrayBuffer;
|
||||
Int8Array = global.Int8Array;
|
||||
Uint8Array = global.Uint8Array;
|
||||
Uint8ClampedArray = global.Uint8ClampedArray;
|
||||
Int16Array = global.Int16Array;
|
||||
Uint16Array = global.Uint16Array;
|
||||
Int32Array = global.Int32Array;
|
||||
Uint32Array = global.Uint32Array;
|
||||
Float32Array = global.Float32Array;
|
||||
Float64Array = global.Float64Array;
|
||||
BigInt64Array = global.BigInt64Array;
|
||||
BigUint64Array = global.BigUint64Array;
|
||||
}
|
||||
;
|
||||
if (uni.restoreGlobal) {
|
||||
uni.restoreGlobal(Vue, weex, plus, setTimeout, clearTimeout, setInterval, clearInterval);
|
||||
}
|
||||
(function(vue) {
|
||||
"use strict";
|
||||
function formatAppLog(type, filename, ...args) {
|
||||
if (uni.__log__) {
|
||||
uni.__log__(type, filename, ...args);
|
||||
} else {
|
||||
console[type].apply(console, [...args, filename]);
|
||||
}
|
||||
}
|
||||
const _export_sfc = (sfc, props) => {
|
||||
const target = sfc.__vccOpts || sfc;
|
||||
for (const [key, val] of props) {
|
||||
target[key] = val;
|
||||
}
|
||||
return target;
|
||||
};
|
||||
const _sfc_main$1 = {
|
||||
methods: {
|
||||
// 基本点击
|
||||
handleTap(e) {
|
||||
formatAppLog("log", "at pages/index/hammerjsTest/hammerjsTest.vue:20", "Tap:", e);
|
||||
},
|
||||
// 平移拖拽
|
||||
handlePan(e) {
|
||||
const { displacement, deltaX, deltaY } = e;
|
||||
formatAppLog("log", "at pages/index/hammerjsTest/hammerjsTest.vue:26", `Pan: X=${deltaX}, Y=${deltaY}, 位移=${displacement}px`);
|
||||
},
|
||||
// 快速滑动
|
||||
handleSwipe(e) {
|
||||
const { direction } = e;
|
||||
const dirMap = {
|
||||
left: "向左",
|
||||
right: "向右",
|
||||
up: "向上",
|
||||
down: "向下"
|
||||
};
|
||||
formatAppLog("log", "at pages/index/hammerjsTest/hammerjsTest.vue:38", `Swipe: ${dirMap[direction]}`);
|
||||
},
|
||||
// 缩放
|
||||
handlePinch(e) {
|
||||
formatAppLog("log", "at pages/index/hammerjsTest/hammerjsTest.vue:43", `缩放比例: ${e.scale.toFixed(2)}倍`);
|
||||
},
|
||||
// 长按
|
||||
handlePress(e) {
|
||||
formatAppLog("log", "at pages/index/hammerjsTest/hammerjsTest.vue:48", `长按 ${e.duration}ms`);
|
||||
},
|
||||
// 旋转
|
||||
handleRotate(e) {
|
||||
formatAppLog("log", "at pages/index/hammerjsTest/hammerjsTest.vue:53", `旋转角度: ${e.angle.toFixed(1)}°`);
|
||||
}
|
||||
}
|
||||
};
|
||||
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||
const _component_any_touch = vue.resolveComponent("any-touch");
|
||||
return vue.openBlock(), vue.createBlock(_component_any_touch, {
|
||||
"pan-options": { threshold: 10 },
|
||||
"pinch-options": { threshold: 0.1 },
|
||||
onClick: $options.handleTap,
|
||||
onPan: $options.handlePan,
|
||||
onSwipe: $options.handleSwipe,
|
||||
onPinch: $options.handlePinch,
|
||||
onPress: $options.handlePress,
|
||||
onRotate: $options.handleRotate
|
||||
}, {
|
||||
default: vue.withCtx(() => [
|
||||
vue.createElementVNode("view", { class: "gesture-box" }, "手势识别区域")
|
||||
]),
|
||||
_: 1
|
||||
/* STABLE */
|
||||
}, 8, ["onClick", "onPan", "onSwipe", "onPinch", "onPress", "onRotate"]);
|
||||
}
|
||||
const PagesIndexHammerjsTestHammerjsTest = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render], ["__file", "/Users/sunmeng/Desktop/wx/canvas/pages/index/hammerjsTest/hammerjsTest.vue"]]);
|
||||
__definePage("pages/index/hammerjsTest/hammerjsTest", PagesIndexHammerjsTestHammerjsTest);
|
||||
const _sfc_main = {
|
||||
onLaunch: function() {
|
||||
formatAppLog("log", "at App.vue:4", "App Launch");
|
||||
},
|
||||
onShow: function() {
|
||||
formatAppLog("log", "at App.vue:7", "App Show");
|
||||
},
|
||||
onHide: function() {
|
||||
formatAppLog("log", "at App.vue:10", "App Hide");
|
||||
}
|
||||
};
|
||||
const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__file", "/Users/sunmeng/Desktop/wx/canvas/App.vue"]]);
|
||||
function createApp() {
|
||||
const app = vue.createVueApp(App);
|
||||
return {
|
||||
app
|
||||
};
|
||||
}
|
||||
const { app: __app__, Vuex: __Vuex__, Pinia: __Pinia__ } = createApp();
|
||||
uni.Vuex = __Vuex__;
|
||||
uni.Pinia = __Pinia__;
|
||||
__app__.provide("__globalStyles", __uniConfig.styles);
|
||||
__app__._component.mpType = "app";
|
||||
__app__._component.render = () => {
|
||||
};
|
||||
__app__.mount("#app");
|
||||
})(Vue);
|
4
unpackage/dist/dev/app-plus/app.css
vendored
Normal file
4
unpackage/dist/dev/app-plus/app.css
vendored
Normal file
File diff suppressed because one or more lines are too long
107
unpackage/dist/dev/app-plus/manifest.json
vendored
Normal file
107
unpackage/dist/dev/app-plus/manifest.json
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
{
|
||||
"@platforms": [
|
||||
"android",
|
||||
"iPhone",
|
||||
"iPad"
|
||||
],
|
||||
"id": "__UNI__EBE2302",
|
||||
"name": "canvas",
|
||||
"version": {
|
||||
"name": "1.0.0",
|
||||
"code": "100"
|
||||
},
|
||||
"description": "",
|
||||
"developer": {
|
||||
"name": "",
|
||||
"email": "",
|
||||
"url": ""
|
||||
},
|
||||
"permissions": {
|
||||
"UniNView": {
|
||||
"description": "UniNView原生渲染"
|
||||
}
|
||||
},
|
||||
"plus": {
|
||||
"useragent": {
|
||||
"value": "uni-app",
|
||||
"concatenate": true
|
||||
},
|
||||
"splashscreen": {
|
||||
"target": "id:1",
|
||||
"autoclose": true,
|
||||
"waiting": true,
|
||||
"delay": 0
|
||||
},
|
||||
"popGesture": "close",
|
||||
"launchwebview": {
|
||||
"render": "always",
|
||||
"id": "1",
|
||||
"kernel": "WKWebview"
|
||||
},
|
||||
"usingComponents": true,
|
||||
"nvueStyleCompiler": "uni-app",
|
||||
"compilerVersion": 3,
|
||||
"distribute": {
|
||||
"google": {
|
||||
"permissions": [
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
]
|
||||
},
|
||||
"apple": {},
|
||||
"plugins": {
|
||||
"audio": {
|
||||
"mp3": {
|
||||
"description": "Android平台录音支持MP3格式文件"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"statusbar": {
|
||||
"immersed": "supportedDevice",
|
||||
"style": "dark",
|
||||
"background": "#F8F8F8"
|
||||
},
|
||||
"uniStatistics": {
|
||||
"enable": false
|
||||
},
|
||||
"allowsInlineMediaPlayback": true,
|
||||
"uni-app": {
|
||||
"control": "uni-v3",
|
||||
"vueVersion": "3",
|
||||
"compilerVersion": "4.57",
|
||||
"nvueCompiler": "uni-app",
|
||||
"renderer": "auto",
|
||||
"nvue": {
|
||||
"flex-direction": "column"
|
||||
},
|
||||
"nvueLaunchMode": "normal",
|
||||
"webView": {
|
||||
"minUserAgentVersion": "49.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"app-harmony": {
|
||||
"useragent": {
|
||||
"value": "uni-app",
|
||||
"concatenate": true
|
||||
},
|
||||
"uniStatistics": {
|
||||
"enable": false
|
||||
}
|
||||
},
|
||||
"launch_path": "__uniappview.html"
|
||||
}
|
BIN
unpackage/dist/dev/app-plus/static/logo.png
vendored
Normal file
BIN
unpackage/dist/dev/app-plus/static/logo.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
7
unpackage/dist/dev/app-plus/uni-app-view.umd.js
vendored
Normal file
7
unpackage/dist/dev/app-plus/uni-app-view.umd.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
unpackage/dist/dev/cache/.app-plus/tsc/app-android/.tsbuildInfo
vendored
Normal file
1
unpackage/dist/dev/cache/.app-plus/tsc/app-android/.tsbuildInfo
vendored
Normal file
File diff suppressed because one or more lines are too long
2
unpackage/dist/dev/mp-weixin/app.js
vendored
2
unpackage/dist/dev/mp-weixin/app.js
vendored
@ -2,7 +2,7 @@
|
||||
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
||||
const common_vendor = require("./common/vendor.js");
|
||||
if (!Math) {
|
||||
"./pages/index/hammerjsTest/hammerjsTest.js";
|
||||
"./pages/index/gesture-canvas-page.js";
|
||||
}
|
||||
const _sfc_main = {
|
||||
onLaunch: function() {
|
||||
|
2
unpackage/dist/dev/mp-weixin/app.json
vendored
2
unpackage/dist/dev/mp-weixin/app.json
vendored
@ -1,6 +1,6 @@
|
||||
{
|
||||
"pages": [
|
||||
"pages/index/hammerjsTest/hammerjsTest"
|
||||
"pages/index/gesture-canvas-page"
|
||||
],
|
||||
"window": {
|
||||
"navigationBarTextStyle": "black",
|
||||
|
837
unpackage/dist/dev/mp-weixin/common/vendor.js
vendored
837
unpackage/dist/dev/mp-weixin/common/vendor.js
vendored
File diff suppressed because it is too large
Load Diff
192
unpackage/dist/dev/mp-weixin/pages/index/gesture-canvas-page.js
vendored
Normal file
192
unpackage/dist/dev/mp-weixin/pages/index/gesture-canvas-page.js
vendored
Normal file
@ -0,0 +1,192 @@
|
||||
"use strict";
|
||||
const common_vendor = require("../../common/vendor.js");
|
||||
const pages_index_transformMatrix = require("./transform-matrix.js");
|
||||
const pages_index_gestureHandler = require("./gesture-handler.js");
|
||||
const TransformCanvas = () => "./transform-canvas.js";
|
||||
const _sfc_main = {
|
||||
components: {
|
||||
TransformCanvas
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
canvasWidth: 300,
|
||||
canvasHeight: 300,
|
||||
transformMatrix: new pages_index_transformMatrix.TransformMatrix(),
|
||||
gestureHandler: null,
|
||||
containerRect: null,
|
||||
gestureStatus: "等待手势...",
|
||||
scaleValue: 1,
|
||||
touchPoints: 0,
|
||||
lastGestureTime: 0
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
await this.getContainerPosition();
|
||||
this.initGestureHandler();
|
||||
this.$nextTick(() => {
|
||||
this.updateCanvas();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
// 独立的手势初始化方法
|
||||
initGestureHandler() {
|
||||
try {
|
||||
this.gestureHandler = new pages_index_gestureHandler.GestureHandler(this, this.transformMatrix, {
|
||||
container: this.containerRect
|
||||
});
|
||||
} catch (e) {
|
||||
common_vendor.index.__f__("error", "at pages/index/gesture-canvas-page.vue:77", "手势处理器初始化失败:", 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 = common_vendor.index.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
|
||||
};
|
||||
common_vendor.index.__f__("log", "at pages/index/gesture-canvas-page.vue:119", "成功获取容器位置:", this.containerRect);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
common_vendor.index.__f__("error", "at pages/index/gesture-canvas-page.vue:123", "获取容器位置失败:", e);
|
||||
}
|
||||
await new Promise((r) => setTimeout(r, 100));
|
||||
retryCount++;
|
||||
}
|
||||
common_vendor.index.__f__("error", "at pages/index/gesture-canvas-page.vue:131", `容器位置获取失败,已重试${maxRetries}次`);
|
||||
},
|
||||
// 事件处理(完整修复)
|
||||
async handleTouchEvent(event) {
|
||||
if (!this.gestureHandler || !this.containerRect) {
|
||||
common_vendor.index.__f__("warn", "at pages/index/gesture-canvas-page.vue:137", "手势处理器未就绪,尝试重新初始化...");
|
||||
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;
|
||||
common_vendor.index.__f__("log", "at pages/index/gesture-canvas-page.vue:160", `原始坐标: (${touch.clientX}, ${touch.clientY}) 修正后: (${x}, ${y})`);
|
||||
return {
|
||||
...touch,
|
||||
x,
|
||||
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 = "结束";
|
||||
}
|
||||
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() {
|
||||
common_vendor.index.__f__("log", "at pages/index/gesture-canvas-page.vue:235", "当前变换矩阵:", this.transformMatrix.toArray());
|
||||
common_vendor.index.__f__("log", "at pages/index/gesture-canvas-page.vue:236", "容器位置:", this.containerRect);
|
||||
if (this.$refs.canvasRef) {
|
||||
this.$refs.canvasRef.drawDebugGrid();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!Array) {
|
||||
const _component_transform_canvas = common_vendor.resolveComponent("transform-canvas");
|
||||
_component_transform_canvas();
|
||||
}
|
||||
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||
return {
|
||||
a: common_vendor.t($data.scaleValue.toFixed(2)),
|
||||
b: common_vendor.t($data.transformMatrix.tx.toFixed(1)),
|
||||
c: common_vendor.t($data.transformMatrix.ty.toFixed(1)),
|
||||
d: common_vendor.t($data.gestureStatus),
|
||||
e: common_vendor.t($data.touchPoints),
|
||||
f: common_vendor.sr("canvasRef", "2e633000-0"),
|
||||
g: common_vendor.p({
|
||||
width: $data.canvasWidth,
|
||||
height: $data.canvasHeight,
|
||||
matrix: $data.transformMatrix
|
||||
}),
|
||||
h: common_vendor.o((...args) => $options.handleTouchEvent && $options.handleTouchEvent(...args)),
|
||||
i: common_vendor.o((...args) => $options.handleTouchEvent && $options.handleTouchEvent(...args)),
|
||||
j: common_vendor.o((...args) => $options.handleTouchEvent && $options.handleTouchEvent(...args)),
|
||||
k: common_vendor.o((...args) => $options.handleTouchEvent && $options.handleTouchEvent(...args)),
|
||||
l: common_vendor.o((...args) => $options.resetCanvas && $options.resetCanvas(...args)),
|
||||
m: common_vendor.o((...args) => $options.zoomIn && $options.zoomIn(...args)),
|
||||
n: common_vendor.o((...args) => $options.zoomOut && $options.zoomOut(...args)),
|
||||
o: common_vendor.o((...args) => $options.logMatrix && $options.logMatrix(...args))
|
||||
};
|
||||
}
|
||||
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-2e633000"]]);
|
||||
wx.createPage(MiniProgramPage);
|
||||
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/index/gesture-canvas-page.js.map
|
6
unpackage/dist/dev/mp-weixin/pages/index/gesture-canvas-page.json
vendored
Normal file
6
unpackage/dist/dev/mp-weixin/pages/index/gesture-canvas-page.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"navigationBarTitleText": "",
|
||||
"usingComponents": {
|
||||
"transform-canvas": "./transform-canvas"
|
||||
}
|
||||
}
|
1
unpackage/dist/dev/mp-weixin/pages/index/gesture-canvas-page.wxml
vendored
Normal file
1
unpackage/dist/dev/mp-weixin/pages/index/gesture-canvas-page.wxml
vendored
Normal file
@ -0,0 +1 @@
|
||||
<view class="container data-v-2e633000"><view class="gesture-container data-v-2e633000" bindtouchstart="{{h}}" bindtouchmove="{{i}}" bindtouchend="{{j}}" bindtouchcancel="{{k}}"><view class="debug-info data-v-2e633000"><text class="data-v-2e633000">缩放: {{a}} | X: {{b}} | Y: {{c}}</text><text class="data-v-2e633000">手势: {{d}}</text><text class="data-v-2e633000">触点: {{e}}</text></view><transform-canvas wx:if="{{g}}" class="r data-v-2e633000" u-r="canvasRef" u-i="2e633000-0" bind:__l="__l" u-p="{{g}}"/></view><view class="controls data-v-2e633000"><button class="data-v-2e633000" bindtap="{{l}}">重置</button><button class="data-v-2e633000" bindtap="{{m}}">放大</button><button class="data-v-2e633000" bindtap="{{n}}">缩小</button><button class="data-v-2e633000" bindtap="{{o}}">调试</button></view></view>
|
37
unpackage/dist/dev/mp-weixin/pages/index/gesture-canvas-page.wxss
vendored
Normal file
37
unpackage/dist/dev/mp-weixin/pages/index/gesture-canvas-page.wxss
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
.container.data-v-2e633000 {
|
||||
padding: 20px;
|
||||
}
|
||||
.gesture-container.data-v-2e633000 {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
border: 1px solid #ccc;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.debug-info.data-v-2e633000 {
|
||||
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.data-v-2e633000 {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
button.data-v-2e633000 {
|
||||
padding: 8px 16px;
|
||||
background: #4a8cff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
}
|
136
unpackage/dist/dev/mp-weixin/pages/index/gesture-handler.js
vendored
Normal file
136
unpackage/dist/dev/mp-weixin/pages/index/gesture-handler.js
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
"use strict";
|
||||
const common_vendor = require("../../common/vendor.js");
|
||||
class GestureHandler {
|
||||
constructor(context, transformMatrix, { container }) {
|
||||
this.transformMatrix = transformMatrix;
|
||||
this.containerRect = container;
|
||||
const atOptions = {
|
||||
getPoint: (touch) => ({
|
||||
x: touch.clientX - this.containerRect.left,
|
||||
y: touch.clientY - this.containerRect.top
|
||||
}),
|
||||
preventDefault: false
|
||||
};
|
||||
if (typeof window === "undefined" || typeof HTMLElement === "undefined") {
|
||||
atOptions._element = {
|
||||
style: {},
|
||||
addEventListener: () => {
|
||||
},
|
||||
removeEventListener: () => {
|
||||
}
|
||||
};
|
||||
}
|
||||
try {
|
||||
this.at = new common_vendor.i(atOptions);
|
||||
} catch (e) {
|
||||
common_vendor.index.__f__("error", "at pages/index/gesture-handler.js:30", "AnyTouch初始化失败:", e);
|
||||
this.handleGesture = this.createSimpleGestureHandler();
|
||||
}
|
||||
if (this.at) {
|
||||
this.setupGestures();
|
||||
common_vendor.index.__f__("log", "at pages/index/gesture-handler.js:37", "AnyTouch手势处理器已初始化");
|
||||
} else {
|
||||
common_vendor.index.__f__("warn", "at pages/index/gesture-handler.js:39", "使用简化手势处理器");
|
||||
}
|
||||
}
|
||||
// 创建小程序专用的简化手势处理器
|
||||
createSimpleGestureHandler() {
|
||||
return {
|
||||
run: (event) => {
|
||||
const touches = event.touches || [];
|
||||
if (touches.length === 1) {
|
||||
this.handlePan(touches[0]);
|
||||
} else if (touches.length > 1) {
|
||||
this.handlePinch(touches);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
setupGestures() {
|
||||
this.at.on("pan", (event) => {
|
||||
if (event.type === "panstart") {
|
||||
common_vendor.index.__f__("log", "at pages/index/gesture-handler.js:65", "panstart", event);
|
||||
} else if (event.type === "panmove") {
|
||||
const currentScale = Math.sqrt(
|
||||
this.transformMatrix.a * this.transformMatrix.a + this.transformMatrix.b * this.transformMatrix.b
|
||||
);
|
||||
const dx = event.deltaX / currentScale;
|
||||
const dy = event.deltaY / currentScale;
|
||||
common_vendor.index.__f__("log", "at pages/index/gesture-handler.js:78", `平移: deltaX=${event.deltaX}, deltaY=${event.deltaY} | 修正后: dx=${dx}, dy=${dy}`);
|
||||
this.transformMatrix.translate(dx, dy);
|
||||
}
|
||||
});
|
||||
this.at.on("pinch", (event) => {
|
||||
if (event.type === "pinchstart") {
|
||||
this.lastScale = 1;
|
||||
} else if (event.type === "pinchmove") {
|
||||
const scaleChange = event.scale / this.lastScale;
|
||||
this.lastScale = event.scale;
|
||||
const centerX = event.center.x;
|
||||
const centerY = event.center.y;
|
||||
common_vendor.index.__f__("log", "at pages/index/gesture-handler.js:98", `缩放: scale=${event.scale} | 变化=${scaleChange} | 中心点: (${centerX}, ${centerY})`);
|
||||
this.transformMatrix.scale(scaleChange, scaleChange, centerX, centerY);
|
||||
}
|
||||
});
|
||||
this.at.on("tap", (event) => {
|
||||
common_vendor.index.__f__("log", "at pages/index/gesture-handler.js:107", "点击事件", event);
|
||||
});
|
||||
this.at.on("press", (event) => {
|
||||
common_vendor.index.__f__("log", "at pages/index/gesture-handler.js:112", "长按事件", event);
|
||||
});
|
||||
}
|
||||
catchEvent(event) {
|
||||
try {
|
||||
if (this.at) {
|
||||
this.at.run(event);
|
||||
} else if (this.handleGesture) {
|
||||
this.handleGesture.run(event);
|
||||
}
|
||||
} catch (e) {
|
||||
common_vendor.index.__f__("error", "at pages/index/gesture-handler.js:125", "手势处理错误:", e);
|
||||
const touches = event.touches || [];
|
||||
if (touches.length > 0) {
|
||||
this.handlePan(touches[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 基础平移手势处理
|
||||
handlePan(touch) {
|
||||
if (this.lastPoint) {
|
||||
const dx = touch.clientX - this.lastPoint.x;
|
||||
const dy = touch.clientY - this.lastPoint.y;
|
||||
const currentScale = Math.sqrt(
|
||||
this.transformMatrix.a * this.transformMatrix.a + this.transformMatrix.b * this.transformMatrix.b
|
||||
);
|
||||
const correctedDx = dx / currentScale;
|
||||
const correctedDy = dy / currentScale;
|
||||
this.transformMatrix.translate(correctedDx, correctedDy);
|
||||
}
|
||||
this.lastPoint = { x: touch.clientX, y: touch.clientY };
|
||||
}
|
||||
// 基础缩放手势处理
|
||||
handlePinch(touches) {
|
||||
const point1 = touches[0];
|
||||
const point2 = touches[1];
|
||||
if (this.lastPoints) {
|
||||
const currentDistance = Math.sqrt(
|
||||
Math.pow(point2.clientX - point1.clientX, 2) + Math.pow(point2.clientY - point1.clientY, 2)
|
||||
);
|
||||
const prevDistance = Math.sqrt(
|
||||
Math.pow(this.lastPoints[1].x - this.lastPoints[0].x, 2) + Math.pow(this.lastPoints[1].y - this.lastPoints[0].y, 2)
|
||||
);
|
||||
if (prevDistance > 0) {
|
||||
const scaleChange = currentDistance / prevDistance;
|
||||
const centerX = (point1.clientX + point2.clientX) / 2 - this.containerRect.left;
|
||||
const centerY = (point1.clientY + point2.clientY) / 2 - this.containerRect.top;
|
||||
this.transformMatrix.scale(scaleChange, scaleChange, centerX, centerY);
|
||||
}
|
||||
}
|
||||
this.lastPoints = [
|
||||
{ x: point1.clientX, y: point1.clientY },
|
||||
{ x: point2.clientX, y: point2.clientY }
|
||||
];
|
||||
}
|
||||
}
|
||||
exports.GestureHandler = GestureHandler;
|
||||
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/index/gesture-handler.js.map
|
@ -1,63 +0,0 @@
|
||||
"use strict";
|
||||
const common_vendor = require("../../../common/vendor.js");
|
||||
const _sfc_main = {
|
||||
methods: {
|
||||
// 基本点击
|
||||
handleTap(e) {
|
||||
common_vendor.index.__f__("log", "at pages/index/hammerjsTest/hammerjsTest.vue:20", "Tap:", e);
|
||||
},
|
||||
// 平移拖拽
|
||||
handlePan(e) {
|
||||
const { displacement, deltaX, deltaY } = e;
|
||||
common_vendor.index.__f__("log", "at pages/index/hammerjsTest/hammerjsTest.vue:26", `Pan: X=${deltaX}, Y=${deltaY}, 位移=${displacement}px`);
|
||||
},
|
||||
// 快速滑动
|
||||
handleSwipe(e) {
|
||||
const { direction } = e;
|
||||
const dirMap = {
|
||||
left: "向左",
|
||||
right: "向右",
|
||||
up: "向上",
|
||||
down: "向下"
|
||||
};
|
||||
common_vendor.index.__f__("log", "at pages/index/hammerjsTest/hammerjsTest.vue:38", `Swipe: ${dirMap[direction]}`);
|
||||
},
|
||||
// 缩放
|
||||
handlePinch(e) {
|
||||
common_vendor.index.__f__("log", "at pages/index/hammerjsTest/hammerjsTest.vue:43", `缩放比例: ${e.scale.toFixed(2)}倍`);
|
||||
},
|
||||
// 长按
|
||||
handlePress(e) {
|
||||
common_vendor.index.__f__("log", "at pages/index/hammerjsTest/hammerjsTest.vue:48", `长按 ${e.duration}ms`);
|
||||
},
|
||||
// 旋转
|
||||
handleRotate(e) {
|
||||
common_vendor.index.__f__("log", "at pages/index/hammerjsTest/hammerjsTest.vue:53", `旋转角度: ${e.angle.toFixed(1)}°`);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!Array) {
|
||||
const _component_any_touch = common_vendor.resolveComponent("any-touch");
|
||||
_component_any_touch();
|
||||
}
|
||||
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||
return {
|
||||
a: common_vendor.o($options.handleTap),
|
||||
b: common_vendor.o($options.handlePan),
|
||||
c: common_vendor.o($options.handleSwipe),
|
||||
d: common_vendor.o($options.handlePinch),
|
||||
e: common_vendor.o($options.handlePress),
|
||||
f: common_vendor.o($options.handleRotate),
|
||||
g: common_vendor.p({
|
||||
["pan-options"]: {
|
||||
threshold: 10
|
||||
},
|
||||
["pinch-options"]: {
|
||||
threshold: 0.1
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
|
||||
wx.createPage(MiniProgramPage);
|
||||
//# sourceMappingURL=../../../../.sourcemap/mp-weixin/pages/index/hammerjsTest/hammerjsTest.js.map
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"navigationBarTitleText": "",
|
||||
"usingComponents": {}
|
||||
}
|
@ -1 +0,0 @@
|
||||
<any-touch wx:if="{{g}}" u-s="{{['d']}}" bindtap="{{a}}" bindpan="{{b}}" bindswipe="{{c}}" bindpinch="{{d}}" bindpress="{{e}}" bindrotate="{{f}}" u-i="2f899711-0" bind:__l="__l" u-p="{{g}}"><view class="gesture-box">手势识别区域</view></any-touch>
|
115
unpackage/dist/dev/mp-weixin/pages/index/transform-canvas.js
vendored
Normal file
115
unpackage/dist/dev/mp-weixin/pages/index/transform-canvas.js
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
"use strict";
|
||||
const common_vendor = require("../../common/vendor.js");
|
||||
const _sfc_main = {
|
||||
props: {
|
||||
width: Number,
|
||||
height: Number,
|
||||
matrix: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ctx: null,
|
||||
canvasWidth: this.width,
|
||||
canvasHeight: this.height
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.initCanvas();
|
||||
},
|
||||
methods: {
|
||||
initCanvas() {
|
||||
this.ctx = common_vendor.index.createCanvasContext("gestureCanvas", this);
|
||||
const pixelRatio = common_vendor.index.getSystemInfoSync().pixelRatio;
|
||||
this.ctx.scale(pixelRatio, pixelRatio);
|
||||
},
|
||||
applyTransform(matrix) {
|
||||
if (!matrix)
|
||||
return;
|
||||
const pixelRatio = common_vendor.index.getSystemInfoSync().pixelRatio;
|
||||
const tx = matrix.tx * pixelRatio;
|
||||
const ty = matrix.ty * pixelRatio;
|
||||
this.ctx.setTransform(
|
||||
matrix.a,
|
||||
matrix.b,
|
||||
matrix.c,
|
||||
matrix.d,
|
||||
tx,
|
||||
ty
|
||||
);
|
||||
},
|
||||
// 调试网格
|
||||
drawDebugGrid() {
|
||||
if (!this.ctx)
|
||||
return;
|
||||
const pixelRatio = common_vendor.index.getSystemInfoSync().pixelRatio;
|
||||
const width = this.canvasWidth * pixelRatio;
|
||||
const height = this.canvasHeight * pixelRatio;
|
||||
this.ctx.save();
|
||||
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
this.ctx.strokeStyle = "rgba(200, 200, 200, 0.3)";
|
||||
this.ctx.lineWidth = 1;
|
||||
for (let y = 0; y <= height; y += 50) {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(0, y);
|
||||
this.ctx.lineTo(width, y);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
for (let x = 0; x <= width; x += 50) {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(x, 0);
|
||||
this.ctx.lineTo(x, height);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
this.ctx.strokeStyle = "#ff0000";
|
||||
this.ctx.lineWidth = 2;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(0, 0);
|
||||
this.ctx.lineTo(width, 0);
|
||||
this.ctx.stroke();
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(0, 0);
|
||||
this.ctx.lineTo(0, height);
|
||||
this.ctx.stroke();
|
||||
this.ctx.restore();
|
||||
this.ctx.draw();
|
||||
},
|
||||
// 绘制主内容
|
||||
draw() {
|
||||
if (!this.ctx) {
|
||||
common_vendor.index.__f__("warn", "at pages/index/transform-canvas.vue:110", "Canvas上下文未初始化");
|
||||
return;
|
||||
}
|
||||
this.clearCanvas();
|
||||
this.applyTransform(this.matrix);
|
||||
this.drawContent();
|
||||
},
|
||||
clearCanvas() {
|
||||
const pixelRatio = common_vendor.index.getSystemInfoSync().pixelRatio;
|
||||
const width = this.canvasWidth * pixelRatio;
|
||||
const height = this.canvasHeight * pixelRatio;
|
||||
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
this.ctx.clearRect(0, 0, width, height);
|
||||
},
|
||||
drawContent() {
|
||||
this.ctx.fillStyle = "#3498db";
|
||||
this.ctx.fillRect(50, 50, 100, 100);
|
||||
this.ctx.fillStyle = "#e74c3c";
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(150, 150, 50, 0, 2 * Math.PI);
|
||||
this.ctx.fill();
|
||||
this.ctx.fillStyle = "#2c3e50";
|
||||
this.ctx.font = "20px sans-serif";
|
||||
this.ctx.fillText("手势Canvas", 50, 250);
|
||||
this.ctx.draw();
|
||||
}
|
||||
}
|
||||
};
|
||||
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||
return {
|
||||
a: $data.canvasWidth + "px",
|
||||
b: $data.canvasHeight + "px"
|
||||
};
|
||||
}
|
||||
const Component = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
|
||||
wx.createComponent(Component);
|
||||
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/index/transform-canvas.js.map
|
4
unpackage/dist/dev/mp-weixin/pages/index/transform-canvas.json
vendored
Normal file
4
unpackage/dist/dev/mp-weixin/pages/index/transform-canvas.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
1
unpackage/dist/dev/mp-weixin/pages/index/transform-canvas.wxml
vendored
Normal file
1
unpackage/dist/dev/mp-weixin/pages/index/transform-canvas.wxml
vendored
Normal file
@ -0,0 +1 @@
|
||||
<canvas canvas-id="gestureCanvas" style="{{'width:' + a + ';' + ('height:' + b)}}"/>
|
0
unpackage/dist/dev/mp-weixin/pages/index/transform-canvas.wxss
vendored
Normal file
0
unpackage/dist/dev/mp-weixin/pages/index/transform-canvas.wxss
vendored
Normal file
47
unpackage/dist/dev/mp-weixin/pages/index/transform-matrix.js
vendored
Normal file
47
unpackage/dist/dev/mp-weixin/pages/index/transform-matrix.js
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
"use strict";
|
||||
class TransformMatrix {
|
||||
constructor() {
|
||||
this.reset();
|
||||
}
|
||||
reset() {
|
||||
this.a = 1;
|
||||
this.b = 0;
|
||||
this.c = 0;
|
||||
this.d = 1;
|
||||
this.tx = 0;
|
||||
this.ty = 0;
|
||||
this.stack = [];
|
||||
}
|
||||
translate(dx, dy) {
|
||||
this.tx += this.a * dx + this.c * dy;
|
||||
this.ty += this.b * dx + this.d * dy;
|
||||
}
|
||||
scale(sx, sy, cx = 0, cy = 0) {
|
||||
this.translate(cx, cy);
|
||||
this.a *= sx;
|
||||
this.b *= sx;
|
||||
this.c *= sy;
|
||||
this.d *= sy;
|
||||
this.translate(-cx, -cy);
|
||||
}
|
||||
toArray() {
|
||||
return [this.a, this.b, this.c, this.d, this.tx, this.ty];
|
||||
}
|
||||
// 用于调试的字符串表示
|
||||
toString() {
|
||||
return `[${this.a.toFixed(2)}, ${this.b.toFixed(2)}, ${this.c.toFixed(2)}, ${this.d.toFixed(2)}, ${this.tx.toFixed(1)}, ${this.ty.toFixed(1)}]`;
|
||||
}
|
||||
// 克隆方法
|
||||
clone() {
|
||||
const clone = new TransformMatrix();
|
||||
clone.a = this.a;
|
||||
clone.b = this.b;
|
||||
clone.c = this.c;
|
||||
clone.d = this.d;
|
||||
clone.tx = this.tx;
|
||||
clone.ty = this.ty;
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
exports.TransformMatrix = TransformMatrix;
|
||||
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/index/transform-matrix.js.map
|
@ -8,11 +8,18 @@
|
||||
"miniprogram": {
|
||||
"list": [
|
||||
{
|
||||
"name": "pages/index/hammerjsTest/hammerjsTest",
|
||||
"pathName": "pages/index/hammerjsTest/hammerjsTest",
|
||||
"name": "pages/index/canvasRenderer",
|
||||
"pathName": "pages/index/canvasRenderer",
|
||||
"query": "",
|
||||
"scene": null,
|
||||
"launchMode": "default"
|
||||
},
|
||||
{
|
||||
"name": "pages/index/hammerjsTest/hammerjsTest",
|
||||
"pathName": "pages/index/hammerjsTest/hammerjsTest",
|
||||
"query": "",
|
||||
"launchMode": "default",
|
||||
"scene": null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user