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
|
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
|
||||||
{
|
{
|
||||||
"path" : "pages/index/hammerjsTest/hammerjsTest",
|
"path" : "pages/index/gesture-canvas-page",
|
||||||
"style" :
|
"style" :
|
||||||
{
|
{
|
||||||
"navigationBarTitleText" : ""
|
"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" });
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
||||||
const common_vendor = require("./common/vendor.js");
|
const common_vendor = require("./common/vendor.js");
|
||||||
if (!Math) {
|
if (!Math) {
|
||||||
"./pages/index/hammerjsTest/hammerjsTest.js";
|
"./pages/index/gesture-canvas-page.js";
|
||||||
}
|
}
|
||||||
const _sfc_main = {
|
const _sfc_main = {
|
||||||
onLaunch: function() {
|
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": [
|
||||||
"pages/index/hammerjsTest/hammerjsTest"
|
"pages/index/gesture-canvas-page"
|
||||||
],
|
],
|
||||||
"window": {
|
"window": {
|
||||||
"navigationBarTextStyle": "black",
|
"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": {
|
"miniprogram": {
|
||||||
"list": [
|
"list": [
|
||||||
{
|
{
|
||||||
"name": "pages/index/hammerjsTest/hammerjsTest",
|
"name": "pages/index/canvasRenderer",
|
||||||
"pathName": "pages/index/hammerjsTest/hammerjsTest",
|
"pathName": "pages/index/canvasRenderer",
|
||||||
"query": "",
|
"query": "",
|
||||||
"scene": null,
|
"scene": null,
|
||||||
"launchMode": "default"
|
"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