348 lines
9.3 KiB
JavaScript
348 lines
9.3 KiB
JavaScript
import { getUserGameState, getGameAPI } from "@/apis";
|
||
|
||
export const formatTimestamp = (timestamp) => {
|
||
const date = new Date(timestamp * 1000);
|
||
const year = date.getFullYear();
|
||
const month = date.getMonth() + 1;
|
||
const day = date.getDate();
|
||
|
||
return `${year}-${month}-${day}`;
|
||
};
|
||
|
||
export const debounce = (fn, delay = 300) => {
|
||
let timer = null;
|
||
return async (...args) => {
|
||
if (timer) clearTimeout(timer);
|
||
return new Promise((resolve) => {
|
||
timer = setTimeout(async () => {
|
||
try {
|
||
const result = await fn(...args);
|
||
resolve(result);
|
||
} finally {
|
||
timer = null;
|
||
}
|
||
}, delay);
|
||
});
|
||
};
|
||
};
|
||
|
||
export const wxShare = async (canvasId = "shareCanvas") => {
|
||
try {
|
||
// 先尝试按 id 查找 <canvas type="2d"> 节点
|
||
const { pixelRatio: dpr = 1 } = uni.getSystemInfoSync() || {};
|
||
const info = await new Promise((resolve) => {
|
||
const query = uni.createSelectorQuery();
|
||
query
|
||
.select(`#${canvasId}`)
|
||
.fields({ node: true, size: true })
|
||
.exec((res) => resolve(res && res[0]));
|
||
});
|
||
|
||
const canvas = info?.node;
|
||
const cssWidth = info?.width;
|
||
const cssHeight = info?.height;
|
||
|
||
if (canvas && typeof canvas.getContext === "function") {
|
||
// 2D 画布导出:传入 canvas 节点
|
||
const tempPath = await new Promise((resolve, reject) => {
|
||
uni.canvasToTempFilePath({
|
||
canvas,
|
||
width: cssWidth,
|
||
height: cssHeight,
|
||
// 按 DPR 导出更清晰的图片
|
||
destWidth: Math.round(cssWidth * dpr),
|
||
destHeight: Math.round(cssHeight * dpr),
|
||
fileType: "png",
|
||
quality: 1,
|
||
success: (r) => {
|
||
const p = r.tempFilePath || r.apFilePath || r.filePath;
|
||
resolve(p);
|
||
},
|
||
fail: reject,
|
||
});
|
||
});
|
||
wx.showShareImageMenu({
|
||
entrancePath: "pages/index",
|
||
path: tempPath,
|
||
});
|
||
return tempPath;
|
||
}
|
||
|
||
// 回退:旧版非 2D 画布(通过 canvasId 导出)
|
||
const res = await uni.canvasToTempFilePath({
|
||
canvasId,
|
||
fileType: "png",
|
||
quality: 1,
|
||
});
|
||
wx.showShareImageMenu({
|
||
entrancePath: "pages/index",
|
||
path: res.tempFilePath,
|
||
});
|
||
return res.tempFilePath;
|
||
} catch (error) {
|
||
console.log("生成图片失败:", error);
|
||
uni.showToast({
|
||
title: "生成图片失败",
|
||
icon: "error",
|
||
});
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
export const isGameEnded = async (battleId) => {
|
||
const state = await getUserGameState();
|
||
if (!state.gaming) {
|
||
const result = await getGameAPI(battleId);
|
||
if (result.mode) {
|
||
uni.redirectTo({
|
||
url: `/pages/battle-result?battleId=${battleId}`,
|
||
});
|
||
} else {
|
||
uni.showToast({
|
||
title: "比赛已结束",
|
||
icon: "none",
|
||
});
|
||
setTimeout(() => {
|
||
uni.navigateBack();
|
||
}, 1000);
|
||
}
|
||
}
|
||
return !state.gaming;
|
||
};
|
||
|
||
// 获取元素尺寸和位置信息
|
||
export const getElementRect = (classname) => {
|
||
return new Promise((resolve) => {
|
||
const query = uni.createSelectorQuery();
|
||
query
|
||
.select(classname)
|
||
.boundingClientRect((rect) => {
|
||
resolve(rect);
|
||
})
|
||
.exec();
|
||
});
|
||
};
|
||
|
||
const calcNormalBowTarget = (x, y, diameter, arrowRadius) => {
|
||
// 将弓箭左上角坐标转换为圆心坐标
|
||
const arrowCenterX = x + arrowRadius;
|
||
const arrowCenterY = y + arrowRadius;
|
||
|
||
// 计算靶心坐标(靶纸中心)
|
||
const centerX = (diameter + arrowRadius * 2) / 2;
|
||
const centerY = (diameter + arrowRadius * 2) / 2;
|
||
|
||
// 计算弓箭圆心到靶心的距离
|
||
const deltaX = arrowCenterX - centerX;
|
||
const deltaY = arrowCenterY - centerY;
|
||
const distanceToCenter = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||
|
||
// 计算弓箭边缘到靶心的最近距离
|
||
const distance = Math.max(0, distanceToCenter - arrowRadius);
|
||
|
||
// 计算靶纸半径(取宽高中较小值的一半)
|
||
const targetRadius = diameter / 2;
|
||
|
||
// 计算相对距离(0-1之间)
|
||
let relativeDistance = distance / targetRadius;
|
||
|
||
// 全环靶有10个环,每个环占半径的10%
|
||
// 从外到内:1环到10环
|
||
// 距离越近靶心,环数越高
|
||
if (relativeDistance <= 0.05) return "X";
|
||
if (relativeDistance <= 0.1) return 10;
|
||
if (relativeDistance <= 0.2) return 9;
|
||
if (relativeDistance <= 0.3) return 8;
|
||
if (relativeDistance <= 0.4) return 7;
|
||
if (relativeDistance <= 0.5) return 6;
|
||
if (relativeDistance <= 0.6) return 5;
|
||
if (relativeDistance <= 0.7) return 4;
|
||
if (relativeDistance <= 0.8) return 3;
|
||
if (relativeDistance <= 0.9) return 2;
|
||
if (relativeDistance <= 1) return 1;
|
||
return 0; // 脱靶
|
||
};
|
||
|
||
const calcHalfBowTarget = (x, y, diameter, arrowRadius, noX = false) => {
|
||
// 将弓箭左上角坐标转换为圆心坐标
|
||
const arrowCenterX = x + arrowRadius;
|
||
const arrowCenterY = y + arrowRadius;
|
||
|
||
// 计算靶心坐标(靶纸中心)
|
||
const centerX = (diameter + arrowRadius * 2) / 2;
|
||
const centerY = (diameter + arrowRadius * 2) / 2;
|
||
|
||
// 计算弓箭圆心到靶心的距离
|
||
const deltaX = arrowCenterX - centerX;
|
||
const deltaY = arrowCenterY - centerY;
|
||
const distanceToCenter = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||
|
||
// 计算弓箭边缘到靶心的最近距离
|
||
const distance = Math.max(0, distanceToCenter - arrowRadius);
|
||
|
||
// 计算靶纸半径(取宽高中较小值的一半)
|
||
const targetRadius = diameter / 2;
|
||
|
||
// 计算相对距离(0-1之间)
|
||
let relativeDistance = distance / targetRadius;
|
||
if (relativeDistance <= 0.1) return noX ? 10 : "X";
|
||
if (relativeDistance <= 0.2) return noX ? 9 : 10;
|
||
if (relativeDistance <= 0.4) return 9;
|
||
if (relativeDistance <= 0.6) return 8;
|
||
if (relativeDistance <= 0.8) return 7;
|
||
if (relativeDistance <= 1) return 6;
|
||
|
||
return 0; // 脱靶
|
||
};
|
||
|
||
export const calcTripleBowTarget = (
|
||
x,
|
||
y,
|
||
diameter,
|
||
arrowRadius,
|
||
noX = false
|
||
) => {
|
||
const side = diameter * 0.325;
|
||
if (y / diameter >= 0.661) {
|
||
return calcHalfBowTarget(
|
||
x - diameter * 0.336,
|
||
y - diameter * 0.675,
|
||
side,
|
||
arrowRadius,
|
||
noX
|
||
);
|
||
}
|
||
if (y / diameter >= 0.323) {
|
||
return calcHalfBowTarget(
|
||
x - diameter * 0.336,
|
||
y - diameter * 0.337,
|
||
side,
|
||
arrowRadius,
|
||
noX
|
||
);
|
||
}
|
||
if (y / diameter >= -0.026) {
|
||
return calcHalfBowTarget(
|
||
x - diameter * 0.336,
|
||
y - diameter * 0.0,
|
||
side,
|
||
arrowRadius,
|
||
noX
|
||
);
|
||
}
|
||
return 0;
|
||
};
|
||
|
||
export const calcPinBowTarget = (x, y, diameter, arrowRadius, noX = false) => {
|
||
const side = diameter * 0.482;
|
||
let r1 = 0;
|
||
let r2 = 0;
|
||
let r3 = 0;
|
||
if (x / diameter >= 0.23 && y / diameter >= 0.005) {
|
||
r1 = calcHalfBowTarget(
|
||
x - diameter * 0.26,
|
||
y - diameter * 0.034,
|
||
side,
|
||
arrowRadius,
|
||
noX
|
||
);
|
||
}
|
||
if (x / diameter >= -0.03 && y / diameter >= 0.456) {
|
||
r2 = calcHalfBowTarget(x, y - diameter * 0.484, side, arrowRadius, noX);
|
||
}
|
||
if (x / diameter >= 0.49 && y / diameter >= 0.456) {
|
||
r3 = calcHalfBowTarget(
|
||
x - diameter * 0.52,
|
||
y - diameter * 0.485,
|
||
side,
|
||
arrowRadius,
|
||
noX
|
||
);
|
||
}
|
||
return r1 || r2 || r3;
|
||
};
|
||
|
||
export const calcRing = (bowtargetId, x, y, diameter, arrowRadius = 5) => {
|
||
if (bowtargetId < 4) {
|
||
return calcNormalBowTarget(x, y, diameter, arrowRadius);
|
||
} else if (bowtargetId < 7) {
|
||
return calcHalfBowTarget(x, y, diameter, arrowRadius);
|
||
} else if (bowtargetId === 7) {
|
||
return calcTripleBowTarget(x, y, diameter, arrowRadius);
|
||
} else if (bowtargetId === 8) {
|
||
return calcPinBowTarget(x, y, diameter, arrowRadius);
|
||
} else if (bowtargetId === 9) {
|
||
return calcTripleBowTarget(x, y, diameter, arrowRadius, true);
|
||
} else if (bowtargetId === 10) {
|
||
return calcPinBowTarget(x, y, diameter, arrowRadius, true);
|
||
}
|
||
return 0;
|
||
};
|
||
|
||
export const getDirectionText = (angle = 0) => {
|
||
if (angle < 0) return "";
|
||
if (angle >= 337.5 || angle < 22.5) {
|
||
return "下";
|
||
} else if (angle >= 22.5 && angle < 67.5) {
|
||
return "左下";
|
||
} else if (angle >= 67.5 && angle < 112.5) {
|
||
return "左";
|
||
} else if (angle >= 112.5 && angle < 157.5) {
|
||
return "左上";
|
||
} else if (angle >= 157.5 && angle < 202.5) {
|
||
return "上";
|
||
} else if (angle >= 202.5 && angle < 247.5) {
|
||
return "右上";
|
||
} else if (angle >= 247.5 && angle < 292.5) {
|
||
return "右";
|
||
} else if (angle >= 292.5 && angle < 337.5) {
|
||
return "右下";
|
||
}
|
||
};
|
||
|
||
export const wxLogin = () => {
|
||
return new Promise((resolve, reject) => {
|
||
uni.login({
|
||
provider: "weixin",
|
||
success: resolve,
|
||
fail: (err) => {
|
||
uni.showToast({
|
||
title: "登录失败",
|
||
icon: "none",
|
||
});
|
||
console.error("登录失败:", err);
|
||
reject(err);
|
||
},
|
||
});
|
||
});
|
||
};
|
||
|
||
export const canEenter = (user, device, online, page = "") => {
|
||
const { miniProgram } = uni.getAccountInfoSync();
|
||
if (!device.deviceId) {
|
||
uni.showToast({
|
||
title: "请先绑定设备",
|
||
icon: "none",
|
||
});
|
||
return false;
|
||
}
|
||
if (miniProgram.envVersion !== "release") return true;
|
||
if (!online) {
|
||
uni.showToast({
|
||
title: "智能弓未连接",
|
||
icon: "none",
|
||
});
|
||
return false;
|
||
}
|
||
if (!user.trio && page !== "/pages/first-try") {
|
||
uni.showToast({
|
||
title: "请先完成新手试炼",
|
||
icon: "none",
|
||
});
|
||
return false;
|
||
}
|
||
return true;
|
||
};
|
||
|
||
export const capsuleHeight = uni.getMenuButtonBoundingClientRect().top - 9;
|