canvas/pages/index/step2.vue
2025-07-14 10:54:06 +08:00

221 lines
5.1 KiB
Vue

<template>
<!-- 模板部分保持不变 -->
<view class="container">
<canvas
type="2d"
id="seatCanvas"
class="canvas"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchend="handleTouchEnd"
></canvas>
<view class="controls">
<button @click="zoomIn">放大</button>
<button @click="zoomOut">缩小</button>
<button @click="resetView">重置</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
seatData: {
"xCoords": [6,7,8,9,10,11,12,13,14,15],
"yCoords": [6,6,6,6,6,6,6,6,6,6],
"statuses": [1,1,1,1,1,1,1,1,1,1],
"levels": ["B","B","B","B","B","B","A","A","A","A"]
},
ctx: null,
canvas: null,
img: null,
scale: 1.0,
offsetX: 0,
offsetY: 0,
lastTouch: { x: 0, y: 0 }
};
},
onReady() {
this.initCanvas();
},
methods: {
async initCanvas() {
const query = uni.createSelectorQuery().in(this);
query.select('#seatCanvas')
.fields({ node: true, size: true })
.exec(async (res) => {
if (!res || !res[0]) return;
const canvas = res[0].node;
const ctx = canvas.getContext('2d');
const dpr = uni.getSystemInfoSync().pixelRatio;
canvas.width = res[0].width * dpr;
canvas.height = res[0].height * dpr;
ctx.scale(dpr, dpr);
this.canvas = canvas;
this.ctx = ctx;
// 加载座位图片
try {
const imagePath = await this.loadImageForMiniProgram(
'https://assets.tech.troyrc.com/sx25/images/events/dingwei1.png'
);
this.img = imagePath;
this.renderSeats();
} catch (error) {
console.error('图片加载失败:', error);
}
});
},
// 专为微信小程序优化的图片加载方法
loadImageForMiniProgram(url) {
return new Promise((resolve, reject) => {
// 第一步:下载图片到临时文件
uni.downloadFile({
url,
success: downloadRes => {
// 第二步:获取图片信息(宽高等)
uni.getImageInfo({
src: downloadRes.tempFilePath,
success: imageInfo => {
resolve({
path: downloadRes.tempFilePath,
width: imageInfo.width,
height: imageInfo.height
});
},
fail: err => reject(`获取图片信息失败: ${JSON.stringify(err)}`)
});
},
fail: err => reject(`下载图片失败: ${JSON.stringify(err)}`)
});
});
},
renderSeats() {
if (!this.ctx || !this.img) return;
const { ctx } = this;
const GRID_SIZE = 30 * this.scale;
// 清空画布
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// 绘制所有座位
this.seatData.xCoords.forEach((x, i) => {
const y = this.seatData.yCoords[i];
const status = this.seatData.statuses[i];
// 计算实际绘制位置
const drawX = (x - 6) * GRID_SIZE + this.offsetX;
const drawY = (y - 6) * GRID_SIZE + this.offsetY;
// 绘制座位图片 - 直接使用临时文件路径
ctx.drawImage(
this.img.path,
drawX,
drawY,
GRID_SIZE,
GRID_SIZE
);
// 根据状态添加效果
if (status === 2) { // 不可用状态
ctx.fillStyle = 'rgba(0,0,0,0.5)';
ctx.fillRect(drawX, drawY, GRID_SIZE, GRID_SIZE);
}
// 绘制座位信息
ctx.fillStyle = '#ffffff';
ctx.font = `${8 * this.scale}px sans-serif`;
ctx.textAlign = 'center';
ctx.fillText(
`${this.seatData.rowNames[i]}${this.seatData.seatNames[i]}`,
drawX + GRID_SIZE / 2,
drawY + GRID_SIZE / 2 + 3 * this.scale
);
});
// 在微信小程序中必须调用 draw() 方法才能更新画布
ctx.draw();
},
// 手势操作(保持不变)
handleTouchStart(e) {
this.lastTouch = {
x: e.touches[0].x,
y: e.touches[0].y
};
},
handleTouchMove(e) {
const touchX = e.touches[0].x;
const touchY = e.touches[0].y;
this.offsetX += (touchX - this.lastTouch.x) * 1.5;
this.offsetY += (touchY - this.lastTouch.y) * 1.5;
this.lastTouch = { x: touchX, y: touchY };
this.renderSeats();
},
handleTouchEnd() {
// 可添加惯性滑动效果
},
// 缩放控制(保持不变)
zoomIn() {
this.scale = Math.min(this.scale * 1.2, 3.0);
this.renderSeats();
},
zoomOut() {
this.scale = Math.max(this.scale * 0.8, 0.5);
this.renderSeats();
},
resetView() {
this.scale = 1.0;
this.offsetX = 0;
this.offsetY = 0;
this.renderSeats();
}
}
};
</script>
<style scoped>
/* 样式保持不变 */
.container {
position: relative;
width: 100%;
height: 80vh;
}
.canvas {
width: 100%;
height: 100%;
background-color: #f5f5f5;
}
.controls {
position: absolute;
bottom: 20px;
left: 0;
right: 0;
display: flex;
justify-content: center;
gap: 15px;
}
.controls button {
padding: 8px 16px;
background-color: #4a7afe;
color: white;
border-radius: 4px;
font-size: 14px;
}
</style>