Files
shoot-miniprograms/src/util.js
T
2026-02-10 18:13:11 +08:00

325 lines
8.8 KiB
JavaScript

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 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;