Files
shoot-miniprograms/src/util.js

348 lines
9.3 KiB
JavaScript
Raw Normal View History

2025-07-29 11:04:19 +08:00
import { isGamingAPI, getGameAPI } from "@/apis";
2025-07-01 10:57:49 +08:00
export const formatTimestamp = (timestamp) => {
2025-07-12 17:12:24 +08:00
const date = new Date(timestamp * 1000);
2025-07-01 10:57:49 +08:00
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
2025-07-28 15:05:19 +08:00
return `${year}-${month}-${day}`;
2025-07-01 10:57:49 +08:00
};
2025-06-22 15:04:10 +08:00
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);
});
};
};
2025-11-04 17:41:50 +08:00
export const wxShare = async (canvasId = "shareCanvas") => {
2025-06-21 21:40:31 +08:00
try {
2025-11-17 12:02:07 +08:00
// 先尝试按 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 导出)
2025-06-21 21:40:31 +08:00
const res = await uni.canvasToTempFilePath({
2025-11-04 17:41:50 +08:00
canvasId,
2025-11-17 12:02:07 +08:00
fileType: "png",
quality: 1,
2025-06-21 21:40:31 +08:00
});
wx.showShareImageMenu({
2025-09-19 16:26:47 +08:00
entrancePath: "pages/index",
2025-06-21 21:40:31 +08:00
path: res.tempFilePath,
});
2025-11-17 12:02:07 +08:00
return res.tempFilePath;
2025-06-21 21:40:31 +08:00
} catch (error) {
2025-11-11 10:13:14 +08:00
console.log("生成图片失败:", error);
2025-06-21 21:40:31 +08:00
uni.showToast({
title: "生成图片失败",
icon: "error",
});
2025-11-17 12:02:07 +08:00
throw error;
2025-06-21 21:40:31 +08:00
}
};
export const isGameEnded = async (battleId) => {
const isGaming = await isGamingAPI();
if (!isGaming) {
2025-07-29 11:04:19 +08:00
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 !isGaming;
};
2025-07-31 14:32:14 +08:00
// 获取元素尺寸和位置信息
2025-08-07 18:13:14 +08:00
export const getElementRect = (classname) => {
2025-07-31 14:32:14 +08:00
return new Promise((resolve) => {
const query = uni.createSelectorQuery();
query
2025-08-07 18:13:14 +08:00
.select(classname)
2025-07-31 14:32:14 +08:00
.boundingClientRect((rect) => {
resolve(rect);
})
.exec();
});
};
2025-10-31 18:29:53 +08:00
const calcNormalBowTarget = (x, y, diameter, arrowRadius) => {
2025-08-11 16:31:58 +08:00
// 将弓箭左上角坐标转换为圆心坐标
const arrowCenterX = x + arrowRadius;
const arrowCenterY = y + arrowRadius;
2025-08-01 09:20:10 +08:00
// 计算靶心坐标(靶纸中心)
2025-12-05 15:24:42 +08:00
const centerX = (diameter + arrowRadius * 2) / 2;
const centerY = (diameter + arrowRadius * 2) / 2;
2025-08-01 09:20:10 +08:00
2025-08-11 16:31:58 +08:00
// 计算弓箭圆心到靶心的距离
const deltaX = arrowCenterX - centerX;
const deltaY = arrowCenterY - centerY;
const distanceToCenter = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
2025-08-01 09:20:10 +08:00
2025-08-11 16:31:58 +08:00
// 计算弓箭边缘到靶心的最近距离
const distance = Math.max(0, distanceToCenter - arrowRadius);
2025-08-01 09:20:10 +08:00
2025-08-11 16:31:58 +08:00
// 计算靶纸半径(取宽高中较小值的一半)
const targetRadius = diameter / 2;
2025-08-01 09:20:10 +08:00
// 计算相对距离0-1之间
2025-08-12 10:16:11 +08:00
let relativeDistance = distance / targetRadius;
2025-08-01 09:20:10 +08:00
// 全环靶有10个环每个环占半径的10%
// 从外到内1环到10环
// 距离越近靶心,环数越高
2025-08-04 16:28:34 +08:00
if (relativeDistance <= 0.05) return "X";
if (relativeDistance <= 0.1) return 10;
2025-08-01 09:20:10 +08:00
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;
2025-08-11 16:31:58 +08:00
if (relativeDistance <= 1) return 1;
2025-08-01 09:20:10 +08:00
return 0; // 脱靶
2025-07-31 14:32:14 +08:00
};
2025-10-31 18:29:53 +08:00
const calcHalfBowTarget = (x, y, diameter, arrowRadius, noX = false) => {
2025-08-11 16:31:58 +08:00
// 将弓箭左上角坐标转换为圆心坐标
const arrowCenterX = x + arrowRadius;
const arrowCenterY = y + arrowRadius;
// 计算靶心坐标(靶纸中心)
2025-12-05 15:24:42 +08:00
const centerX = (diameter + arrowRadius * 2) / 2;
const centerY = (diameter + arrowRadius * 2) / 2;
2025-08-11 16:31:58 +08:00
// 计算弓箭圆心到靶心的距离
const deltaX = arrowCenterX - centerX;
const deltaY = arrowCenterY - centerY;
const distanceToCenter = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
2025-08-11 16:31:58 +08:00
// 计算弓箭边缘到靶心的最近距离
const distance = Math.max(0, distanceToCenter - arrowRadius);
2025-08-11 16:31:58 +08:00
// 计算靶纸半径(取宽高中较小值的一半)
const targetRadius = diameter / 2;
// 计算相对距离0-1之间
2025-08-12 10:16:11 +08:00
let relativeDistance = distance / targetRadius;
2025-08-04 16:28:34 +08:00
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;
2025-12-04 14:11:56 +08:00
if (relativeDistance <= 1) return 6;
return 0; // 脱靶
};
2025-10-31 18:29:53 +08:00
export const calcTripleBowTarget = (
x,
y,
diameter,
arrowRadius,
noX = false
) => {
2025-12-05 15:37:02 +08:00
const side = diameter * 0.325;
if (y / diameter >= 0.661) {
2025-12-04 14:11:56 +08:00
return calcHalfBowTarget(
2025-12-05 15:37:02 +08:00
x - diameter * 0.336,
2025-12-05 15:24:42 +08:00
y - diameter * 0.675,
2025-12-04 14:11:56 +08:00
side,
arrowRadius,
noX
);
}
2025-12-05 15:37:02 +08:00
if (y / diameter >= 0.323) {
2025-12-04 14:11:56 +08:00
return calcHalfBowTarget(
2025-12-05 15:37:02 +08:00
x - diameter * 0.336,
2025-12-05 15:24:42 +08:00
y - diameter * 0.337,
2025-12-04 14:11:56 +08:00
side,
arrowRadius,
noX
);
}
2025-12-05 15:24:42 +08:00
if (y / diameter >= -0.026) {
2025-12-04 14:11:56 +08:00
return calcHalfBowTarget(
2025-12-05 15:37:02 +08:00
x - diameter * 0.336,
y - diameter * 0.0,
2025-12-04 14:11:56 +08:00
side,
arrowRadius,
noX
);
2025-08-04 16:28:34 +08:00
}
return 0;
};
2025-10-31 18:29:53 +08:00
export const calcPinBowTarget = (x, y, diameter, arrowRadius, noX = false) => {
2025-12-04 14:11:56 +08:00
const side = diameter * 0.482;
2025-08-21 11:39:40 +08:00
let r1 = 0;
let r2 = 0;
let r3 = 0;
2025-08-11 16:31:58 +08:00
if (x / diameter >= 0.23 && y / diameter >= 0.005) {
2025-08-21 11:39:40 +08:00
r1 = calcHalfBowTarget(
2025-08-11 16:31:58 +08:00
x - diameter * 0.26,
2025-12-04 14:11:56 +08:00
y - diameter * 0.034,
2025-08-04 16:28:34 +08:00
side,
2025-10-31 18:29:53 +08:00
arrowRadius,
2025-08-04 16:28:34 +08:00
noX
);
}
2025-08-21 11:39:40 +08:00
if (x / diameter >= -0.03 && y / diameter >= 0.456) {
2025-12-04 14:11:56 +08:00
r2 = calcHalfBowTarget(x, y - diameter * 0.484, side, arrowRadius, noX);
2025-08-21 11:39:40 +08:00
}
if (x / diameter >= 0.49 && y / diameter >= 0.456) {
2025-10-31 18:29:53 +08:00
r3 = calcHalfBowTarget(
x - diameter * 0.52,
2025-12-04 14:11:56 +08:00
y - diameter * 0.485,
2025-10-31 18:29:53 +08:00
side,
arrowRadius,
noX
);
2025-08-21 11:39:40 +08:00
}
return r1 || r2 || r3;
};
2025-12-05 15:24:42 +08:00
export const calcRing = (bowtargetId, x, y, diameter, arrowRadius = 5) => {
2025-08-04 16:28:34 +08:00
if (bowtargetId < 4) {
2025-10-31 18:29:53 +08:00
return calcNormalBowTarget(x, y, diameter, arrowRadius);
2025-08-04 16:28:34 +08:00
} else if (bowtargetId < 7) {
2025-12-04 14:11:56 +08:00
return calcHalfBowTarget(x, y, diameter, arrowRadius);
2025-08-04 16:28:34 +08:00
} else if (bowtargetId === 7) {
2025-10-31 18:29:53 +08:00
return calcTripleBowTarget(x, y, diameter, arrowRadius);
2025-08-04 16:28:34 +08:00
} else if (bowtargetId === 8) {
2025-10-31 18:29:53 +08:00
return calcPinBowTarget(x, y, diameter, arrowRadius);
2025-08-04 16:28:34 +08:00
} else if (bowtargetId === 9) {
2025-10-31 18:29:53 +08:00
return calcTripleBowTarget(x, y, diameter, arrowRadius, true);
2025-08-04 16:28:34 +08:00
} else if (bowtargetId === 10) {
2025-10-31 18:29:53 +08:00
return calcPinBowTarget(x, y, diameter, arrowRadius, true);
}
return 0;
};
2025-11-04 16:49:56 +08:00
2025-11-12 16:14:48 +08:00
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 "右下";
}
};
2025-12-03 15:19:17 +08:00
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);
},
});
});
};
2026-01-04 11:36:31 +08:00
export const canEenter = (user, device, online, page = "") => {
const { miniProgram } = uni.getAccountInfoSync();
if (!device.deviceId) {
uni.showToast({
title: "请先绑定设备",
icon: "none",
});
return false;
}
2026-01-05 09:23:26 +08:00
if (miniProgram.envVersion !== "release") return true;
2026-01-04 11:36:31 +08:00
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;
};
2026-01-05 15:59:23 +08:00
export const capsuleHeight = uni.getMenuButtonBoundingClientRect().top - 9;