Files
shoot-miniprograms/src/util.js

348 lines
9.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;