469 lines
17 KiB
JavaScript
469 lines
17 KiB
JavaScript
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$2 = {
|
|
data() {
|
|
return {
|
|
ctx: null,
|
|
canvasWidth: 300,
|
|
canvasHeight: 500,
|
|
seatSize: 30,
|
|
gap: 10,
|
|
rows: 8,
|
|
cols: 10,
|
|
seats: [],
|
|
selectedSeats: [],
|
|
touchStartX: 0,
|
|
touchStartY: 0,
|
|
offsetX: 0,
|
|
offsetY: 0
|
|
};
|
|
},
|
|
onReady() {
|
|
this.initSeats();
|
|
this.initAndroidCanvas();
|
|
},
|
|
methods: {
|
|
initSeats() {
|
|
for (let i = 0; i < this.rows; i++) {
|
|
this.seats[i] = [];
|
|
for (let j = 0; j < this.cols; j++) {
|
|
const isSold = Math.random() < 0.2;
|
|
this.seats[i][j] = {
|
|
row: i,
|
|
col: j,
|
|
status: isSold ? "sold" : "available",
|
|
x: 0,
|
|
y: 0
|
|
};
|
|
}
|
|
}
|
|
},
|
|
// 替换原来的 initAndroidCanvas 方法
|
|
initAndroidCanvas() {
|
|
return new Promise((resolve) => {
|
|
this.ctx = uni.createCanvasContext("seatCanvas", this);
|
|
formatAppLog("log", "at pages/index/step2.vue:77", this.ctx, "ctx123123123");
|
|
const query = uni.createSelectorQuery().in(this);
|
|
query.select(".seat-canvas").boundingClientRect((rect) => {
|
|
if (!rect) {
|
|
formatAppLog("error", "at pages/index/step2.vue:83", "获取Canvas尺寸失败");
|
|
return resolve(false);
|
|
}
|
|
this.canvasWidth = rect.width;
|
|
this.canvasHeight = rect.height;
|
|
const totalWidth = this.cols * (this.seatSize + this.gap) - this.gap;
|
|
const totalHeight = this.rows * (this.seatSize + this.gap) - this.gap;
|
|
this.offsetX = (this.canvasWidth - totalWidth) / 2;
|
|
this.offsetY = (this.canvasHeight - totalHeight) / 2;
|
|
this.drawSeats();
|
|
resolve(true);
|
|
}).exec();
|
|
});
|
|
},
|
|
drawSeats() {
|
|
if (!this.ctx)
|
|
return;
|
|
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
|
|
for (let i = 0; i < this.rows; i++) {
|
|
for (let j = 0; j < this.cols; j++) {
|
|
const seat = this.seats[i][j];
|
|
const x = j * (this.seatSize + this.gap) + this.offsetX;
|
|
const y = i * (this.seatSize + this.gap) + this.offsetY;
|
|
seat.x = x;
|
|
seat.y = y;
|
|
switch (seat.status) {
|
|
case "available":
|
|
this.ctx.setFillStyle("#4CAF50");
|
|
break;
|
|
case "selected":
|
|
this.ctx.setFillStyle("#2196F3");
|
|
break;
|
|
case "sold":
|
|
this.ctx.setFillStyle("#9E9E9E");
|
|
break;
|
|
}
|
|
this.ctx.fillRect(x, y, this.seatSize, this.seatSize);
|
|
this.ctx.setFillStyle("#FFFFFF");
|
|
this.ctx.setFontSize(12);
|
|
this.ctx.setTextAlign("center");
|
|
this.ctx.fillText(
|
|
`${String.fromCharCode(65 + i)}${j + 1}`,
|
|
x + this.seatSize / 2,
|
|
y + this.seatSize / 2 + 4
|
|
// 安卓平台文字垂直对齐需要微调
|
|
);
|
|
}
|
|
}
|
|
this.ctx.draw();
|
|
},
|
|
// 触摸事件处理(安卓专用优化)
|
|
handleTouchStart(e) {
|
|
this.touchStartX = e.touches[0].x;
|
|
this.touchStartY = e.touches[0].y;
|
|
},
|
|
handleTouchMove(e) {
|
|
const touchX = e.touches[0].x;
|
|
const touchY = e.touches[0].y;
|
|
const dx = touchX - this.touchStartX;
|
|
const dy = touchY - this.touchStartY;
|
|
this.offsetX += dx;
|
|
this.offsetY += dy;
|
|
const minX = this.canvasWidth - (this.cols * (this.seatSize + this.gap) - this.gap);
|
|
const minY = this.canvasHeight - (this.rows * (this.seatSize + this.gap) - this.gap);
|
|
this.offsetX = Math.max(minX, Math.min(0, this.offsetX));
|
|
this.offsetY = Math.max(minY, Math.min(0, this.offsetY));
|
|
this.touchStartX = touchX;
|
|
this.touchStartY = touchY;
|
|
this.drawSeats();
|
|
},
|
|
handleTouchEnd(e) {
|
|
const touchX = e.changedTouches[0].x;
|
|
const touchY = e.changedTouches[0].y;
|
|
for (let i = 0; i < this.rows; i++) {
|
|
for (let j = 0; j < this.cols; j++) {
|
|
const seat = this.seats[i][j];
|
|
if (touchX >= seat.x && touchX <= seat.x + this.seatSize && touchY >= seat.y && touchY <= seat.y + this.seatSize) {
|
|
this.toggleSeatSelection(seat);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
toggleSeatSelection(seat) {
|
|
if (seat.status === "sold")
|
|
return;
|
|
if (seat.status === "available") {
|
|
seat.status = "selected";
|
|
this.selectedSeats.push(`${String.fromCharCode(65 + seat.row)}${seat.col + 1}`);
|
|
} else {
|
|
seat.status = "available";
|
|
this.selectedSeats = this.selectedSeats.filter(
|
|
(s) => s !== `${String.fromCharCode(65 + seat.row)}${seat.col + 1}`
|
|
);
|
|
}
|
|
this.drawSeats();
|
|
}
|
|
}
|
|
};
|
|
function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
|
|
return vue.openBlock(), vue.createElementBlock("view", { class: "container" }, [
|
|
vue.createElementVNode("view", { class: "screen" }, "银幕"),
|
|
vue.createElementVNode(
|
|
"canvas",
|
|
{
|
|
"canvas-id": "seatCanvas",
|
|
type: "2d",
|
|
class: "seat-canvas",
|
|
onTouchstart: _cache[0] || (_cache[0] = (...args) => $options.handleTouchStart && $options.handleTouchStart(...args)),
|
|
onTouchmove: _cache[1] || (_cache[1] = (...args) => $options.handleTouchMove && $options.handleTouchMove(...args)),
|
|
onTouchend: _cache[2] || (_cache[2] = (...args) => $options.handleTouchEnd && $options.handleTouchEnd(...args))
|
|
},
|
|
null,
|
|
32
|
|
/* NEED_HYDRATION */
|
|
),
|
|
vue.createElementVNode("view", { class: "legend" }, [
|
|
vue.createElementVNode("view", { class: "legend-item" }, [
|
|
vue.createElementVNode("view", { class: "seat-icon available" }),
|
|
vue.createElementVNode("text", null, "可选")
|
|
]),
|
|
vue.createElementVNode("view", { class: "legend-item" }, [
|
|
vue.createElementVNode("view", { class: "seat-icon selected" }),
|
|
vue.createElementVNode("text", null, "已选")
|
|
]),
|
|
vue.createElementVNode("view", { class: "legend-item" }, [
|
|
vue.createElementVNode("view", { class: "seat-icon sold" }),
|
|
vue.createElementVNode("text", null, "已售")
|
|
])
|
|
]),
|
|
vue.createElementVNode("view", { class: "selected-seats" }, [
|
|
vue.createElementVNode(
|
|
"text",
|
|
null,
|
|
"已选座位:" + vue.toDisplayString($data.selectedSeats.join(", ") || "无"),
|
|
1
|
|
/* TEXT */
|
|
)
|
|
])
|
|
]);
|
|
}
|
|
const PagesIndexStep2 = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$1], ["__file", "/Users/sunmeng/Desktop/wx/canvas/pages/index/step2.vue"]]);
|
|
const _sfc_main$1 = {
|
|
data() {
|
|
return {
|
|
ctx: null,
|
|
canvasWidth: 0,
|
|
canvasHeight: 0,
|
|
seatSize: 30,
|
|
gap: 10,
|
|
rows: 8,
|
|
cols: 10,
|
|
seats: [],
|
|
selectedSeats: [],
|
|
touchStartX: 0,
|
|
touchStartY: 0,
|
|
offsetX: 0,
|
|
offsetY: 0,
|
|
scale: 1
|
|
};
|
|
},
|
|
onReady() {
|
|
this.initSeats();
|
|
this.initCanvas();
|
|
},
|
|
methods: {
|
|
initSeats() {
|
|
for (let i = 0; i < this.rows; i++) {
|
|
this.seats[i] = [];
|
|
for (let j = 0; j < this.cols; j++) {
|
|
const isSold = Math.random() < 0.2;
|
|
this.seats[i][j] = {
|
|
row: i,
|
|
col: j,
|
|
status: isSold ? "sold" : "available",
|
|
// available, selected, sold
|
|
x: 0,
|
|
y: 0
|
|
};
|
|
}
|
|
}
|
|
},
|
|
async initCanvas() {
|
|
const { canvas, width, height } = await this.getCanvasNode("seatCanvas");
|
|
this.canvasWidth = width;
|
|
this.canvasHeight = height;
|
|
this.ctx = canvas.getContext("2d");
|
|
const totalWidth = this.cols * (this.seatSize + this.gap) - this.gap;
|
|
const totalHeight = this.rows * (this.seatSize + this.gap) - this.gap;
|
|
this.offsetX = (this.canvasWidth - totalWidth) / 2;
|
|
this.offsetY = (this.canvasHeight - totalHeight) / 2;
|
|
this.drawSeats();
|
|
},
|
|
getCanvasNode(id) {
|
|
return new Promise((resolve) => {
|
|
const query = uni.createSelectorQuery().in(this);
|
|
query.select(`#${id}`).fields({ node: true, size: true }).exec((res) => {
|
|
formatAppLog("log", "at pages/index/index.vue:94", "给我看看", res);
|
|
formatAppLog("log", "at pages/index/index.vue:95", "给我看看", res);
|
|
formatAppLog("log", "at pages/index/index.vue:96", "给我看看", res);
|
|
formatAppLog("log", "at pages/index/index.vue:97", "给我看看", res);
|
|
formatAppLog("log", "at pages/index/index.vue:98", "给我看看", res);
|
|
formatAppLog("log", "at pages/index/index.vue:99", "给我看看", res);
|
|
const canvas = res[0].node;
|
|
const width = res[0].width;
|
|
const height = res[0].height;
|
|
canvas.width = width * uni.getSystemInfoSync().pixelRatio;
|
|
canvas.height = height * uni.getSystemInfoSync().pixelRatio;
|
|
resolve({ canvas, width, height });
|
|
});
|
|
});
|
|
},
|
|
drawSeats() {
|
|
if (!this.ctx)
|
|
return;
|
|
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
|
|
this.ctx.save();
|
|
this.ctx.translate(this.offsetX, this.offsetY);
|
|
this.ctx.scale(this.scale, this.scale);
|
|
for (let i = 0; i < this.rows; i++) {
|
|
for (let j = 0; j < this.cols; j++) {
|
|
const seat = this.seats[i][j];
|
|
const x = j * (this.seatSize + this.gap);
|
|
const y = i * (this.seatSize + this.gap);
|
|
seat.x = x;
|
|
seat.y = y;
|
|
switch (seat.status) {
|
|
case "available":
|
|
this.ctx.fillStyle = "#4CAF50";
|
|
break;
|
|
case "selected":
|
|
this.ctx.fillStyle = "#2196F3";
|
|
break;
|
|
case "sold":
|
|
this.ctx.fillStyle = "#9E9E9E";
|
|
break;
|
|
}
|
|
this.ctx.beginPath();
|
|
this.ctx.roundRect(x, y, this.seatSize, this.seatSize, 4);
|
|
this.ctx.fill();
|
|
this.ctx.fillStyle = "#FFFFFF";
|
|
this.ctx.font = "12px Arial";
|
|
this.ctx.textAlign = "center";
|
|
this.ctx.textBaseline = "middle";
|
|
this.ctx.fillText(
|
|
`${String.fromCharCode(65 + i)}${j + 1}`,
|
|
x + this.seatSize / 2,
|
|
y + this.seatSize / 2
|
|
);
|
|
}
|
|
}
|
|
this.ctx.restore();
|
|
},
|
|
handleTouchStart(e) {
|
|
this.touchStartX = e.touches[0].x;
|
|
this.touchStartY = e.touches[0].y;
|
|
},
|
|
handleTouchMove(e) {
|
|
const touchX = e.touches[0].x;
|
|
const touchY = e.touches[0].y;
|
|
const dx = touchX - this.touchStartX;
|
|
const dy = touchY - this.touchStartY;
|
|
this.offsetX += dx;
|
|
this.offsetY += dy;
|
|
const minX = this.canvasWidth - (this.cols * (this.seatSize + this.gap) - this.gap) * this.scale;
|
|
const minY = this.canvasHeight - (this.rows * (this.seatSize + this.gap) - this.gap) * this.scale;
|
|
this.offsetX = Math.max(minX, Math.min(0, this.offsetX));
|
|
this.offsetY = Math.max(minY, Math.min(0, this.offsetY));
|
|
this.touchStartX = touchX;
|
|
this.touchStartY = touchY;
|
|
this.drawSeats();
|
|
},
|
|
handleTouchEnd(e) {
|
|
const touchX = e.changedTouches[0].x;
|
|
const touchY = e.changedTouches[0].y;
|
|
const canvasX = (touchX - this.offsetX) / this.scale;
|
|
const canvasY = (touchY - this.offsetY) / this.scale;
|
|
for (let i = 0; i < this.rows; i++) {
|
|
for (let j = 0; j < this.cols; j++) {
|
|
const seat = this.seats[i][j];
|
|
if (canvasX >= seat.x && canvasX <= seat.x + this.seatSize && canvasY >= seat.y && canvasY <= seat.y + this.seatSize) {
|
|
this.toggleSeatSelection(seat);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
toggleSeatSelection(seat) {
|
|
if (seat.status === "sold")
|
|
return;
|
|
if (seat.status === "available") {
|
|
seat.status = "selected";
|
|
this.selectedSeats.push(`${String.fromCharCode(65 + seat.row)}${seat.col + 1}`);
|
|
} else {
|
|
seat.status = "available";
|
|
this.selectedSeats = this.selectedSeats.filter(
|
|
(s) => s !== `${String.fromCharCode(65 + seat.row)}${seat.col + 1}`
|
|
);
|
|
}
|
|
this.drawSeats();
|
|
}
|
|
}
|
|
};
|
|
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
return vue.openBlock(), vue.createElementBlock("view", { class: "container" }, [
|
|
vue.createElementVNode("view", { class: "screen" }, "银幕"),
|
|
vue.createElementVNode(
|
|
"canvas",
|
|
{
|
|
"canvas-id": "seatCanvas",
|
|
type: "2d",
|
|
class: "seat-canvas",
|
|
onTouchstart: _cache[0] || (_cache[0] = (...args) => $options.handleTouchStart && $options.handleTouchStart(...args)),
|
|
onTouchmove: _cache[1] || (_cache[1] = (...args) => $options.handleTouchMove && $options.handleTouchMove(...args)),
|
|
onTouchend: _cache[2] || (_cache[2] = (...args) => $options.handleTouchEnd && $options.handleTouchEnd(...args))
|
|
},
|
|
null,
|
|
32
|
|
/* NEED_HYDRATION */
|
|
),
|
|
vue.createElementVNode("view", { class: "legend" }, [
|
|
vue.createElementVNode("view", { class: "legend-item" }, [
|
|
vue.createElementVNode("view", { class: "seat-icon available" }),
|
|
vue.createElementVNode("text", null, "可选")
|
|
]),
|
|
vue.createElementVNode("view", { class: "legend-item" }, [
|
|
vue.createElementVNode("view", { class: "seat-icon selected" }),
|
|
vue.createElementVNode("text", null, "已选")
|
|
]),
|
|
vue.createElementVNode("view", { class: "legend-item" }, [
|
|
vue.createElementVNode("view", { class: "seat-icon sold" }),
|
|
vue.createElementVNode("text", null, "已售")
|
|
])
|
|
]),
|
|
vue.createElementVNode("view", { class: "selected-seats" }, [
|
|
vue.createElementVNode(
|
|
"text",
|
|
null,
|
|
"已选座位:" + vue.toDisplayString($data.selectedSeats.join(", ") || "无"),
|
|
1
|
|
/* TEXT */
|
|
)
|
|
])
|
|
]);
|
|
}
|
|
const PagesIndexIndex = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render], ["__file", "/Users/sunmeng/Desktop/wx/canvas/pages/index/index.vue"]]);
|
|
__definePage("pages/index/step2", PagesIndexStep2);
|
|
__definePage("pages/index/index", PagesIndexIndex);
|
|
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);
|