Merge branch 'dev' of http://git.bimwe.com/tennis/mini-programs into dev
This commit is contained in:
@@ -1,4 +1,39 @@
|
|||||||
.picker-container {
|
.picker-container {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
// 使用包装元素的伪元素创建渐变遮罩
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 20%;
|
||||||
|
background: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
#fafafa,
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 20%;
|
||||||
|
background: linear-gradient(
|
||||||
|
to top,
|
||||||
|
#fafafa,
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
:global {
|
:global {
|
||||||
.nut-popup-round {
|
.nut-popup-round {
|
||||||
position: relative !important;
|
position: relative !important;
|
||||||
@@ -7,7 +42,7 @@
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nut-picker {
|
.nut-picker {
|
||||||
&::after {
|
&::after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -16,7 +51,7 @@
|
|||||||
right: 16px !important;
|
right: 16px !important;
|
||||||
width: calc(100% - 32px);
|
width: calc(100% - 32px);
|
||||||
height: 48px;
|
height: 48px;
|
||||||
background: rgba(0, 0, 0, 0.02);
|
background: rgba(22, 24, 35, 0.05);
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|||||||
@@ -38,13 +38,13 @@ const RadarChart: React.FC = forwardRef((props, ref) => {
|
|||||||
canvas.width = res[0].width * dpr;
|
canvas.width = res[0].width * dpr;
|
||||||
canvas.height = res[0].height * dpr;
|
canvas.height = res[0].height * dpr;
|
||||||
ctx.scale(dpr, dpr);
|
ctx.scale(dpr, dpr);
|
||||||
|
|
||||||
// 启用抗锯齿,消除锯齿
|
// 启用抗锯齿,消除锯齿
|
||||||
ctx.imageSmoothingEnabled = true;
|
ctx.imageSmoothingEnabled = true;
|
||||||
ctx.imageSmoothingQuality = 'high';
|
ctx.imageSmoothingQuality = "high";
|
||||||
// 设置线条端点样式为圆形,减少锯齿
|
// 设置线条端点样式为圆形,减少锯齿
|
||||||
ctx.lineCap = 'round';
|
ctx.lineCap = "round";
|
||||||
ctx.lineJoin = 'round';
|
ctx.lineJoin = "round";
|
||||||
|
|
||||||
// === 绘制圆形网格 ===
|
// === 绘制圆形网格 ===
|
||||||
for (let i = levels; i >= 1; i--) {
|
for (let i = levels; i >= 1; i--) {
|
||||||
@@ -52,10 +52,10 @@ const RadarChart: React.FC = forwardRef((props, ref) => {
|
|||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(center.x, center.y, r, 0, Math.PI * 2);
|
ctx.arc(center.x, center.y, r, 0, Math.PI * 2);
|
||||||
if (i % 2 === 1) {
|
if (i % 2 === 1) {
|
||||||
ctx.fillStyle = "#fff";
|
ctx.fillStyle = "rgba(255, 255, 255, 0.4)";
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
} else {
|
} else {
|
||||||
ctx.fillStyle = "#CAFCF0";
|
ctx.fillStyle = "rgba(149, 249, 225, 0.4)";
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
// 根据层级设置不同的线条颜色,中间圆圈使用更浅的颜色
|
// 根据层级设置不同的线条颜色,中间圆圈使用更浅的颜色
|
||||||
@@ -70,7 +70,7 @@ const RadarChart: React.FC = forwardRef((props, ref) => {
|
|||||||
ctx.strokeStyle = "#D5D5D5";
|
ctx.strokeStyle = "#D5D5D5";
|
||||||
}
|
}
|
||||||
// 设置线条宽度为1px,确保清晰
|
// 设置线条宽度为1px,确保清晰
|
||||||
ctx.lineWidth = 1;
|
ctx.lineWidth = 0.5;
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -546,7 +546,8 @@
|
|||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
border-radius: 26px;
|
border-radius: 26px;
|
||||||
border: 4px solid #fff;
|
border: 4px solid #fff;
|
||||||
background: linear-gradient(180deg, #bfffef 0%, #f2fffc 100%), #fff;
|
// background: linear-gradient(180deg, #bfffef 0%, #f2fffc 100%), #fff;
|
||||||
|
background-size: contain;
|
||||||
box-shadow: 0 8px 64px 0 rgba(0, 0, 0, 0.1);
|
box-shadow: 0 8px 64px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
.avatarWrap {
|
.avatarWrap {
|
||||||
|
|||||||
@@ -602,10 +602,10 @@ function Result() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用 RadarV2 的 generateFullImage 方法生成完整图片
|
// 使用 RadarV2 的 generateFullImage 方法生成完整图片
|
||||||
const userNickname = (userInfo as any)?.nickname;
|
const userNickname = (userInfo as any)?.nickname;
|
||||||
const titleText = userNickname
|
const titleText = userNickname
|
||||||
? `${userNickname}的 NTRP 测试结果为`
|
? `${userNickname}的 NTRP 测试结果为`
|
||||||
: "你的 NTRP 测试结果为";
|
: "你的 NTRP 测试结果为";
|
||||||
const imageUrl = await radarV2Ref.current?.generateFullImage({
|
const imageUrl = await radarV2Ref.current?.generateFullImage({
|
||||||
@@ -658,6 +658,7 @@ function Result() {
|
|||||||
try {
|
try {
|
||||||
const url = await genCardImage();
|
const url = await genCardImage();
|
||||||
Taro.saveImageToPhotosAlbum({ filePath: url });
|
Taro.saveImageToPhotosAlbum({ filePath: url });
|
||||||
|
Taro.showToast({ title: "保存成功" });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Taro.showToast({ title: "图片保存失败", icon: "none" });
|
Taro.showToast({ title: "图片保存失败", icon: "none" });
|
||||||
}
|
}
|
||||||
@@ -691,7 +692,12 @@ function Result() {
|
|||||||
return (
|
return (
|
||||||
<View className={styles.resultContainer}>
|
<View className={styles.resultContainer}>
|
||||||
<CommonGuideBar />
|
<CommonGuideBar />
|
||||||
<View className={styles.card}>
|
<View
|
||||||
|
className={styles.card}
|
||||||
|
style={{
|
||||||
|
backgroundImage: `url(${OSS_BASE_URL}/images/f5b45cea-5015-41d6-aaf4-83b2e76678e1.png)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<View className={styles.avatarWrap}>
|
<View className={styles.avatarWrap}>
|
||||||
<View className={styles.avatar}>
|
<View className={styles.avatar}>
|
||||||
<Image
|
<Image
|
||||||
@@ -712,7 +718,7 @@ function Result() {
|
|||||||
<View className={styles.desc}>
|
<View className={styles.desc}>
|
||||||
<View className={styles.tip}>
|
<View className={styles.tip}>
|
||||||
<Text>
|
<Text>
|
||||||
{(userInfo as any)?.nickname
|
{(userInfo as any)?.nickname
|
||||||
? `${(userInfo as any).nickname}的 NTRP 测试结果为`
|
? `${(userInfo as any).nickname}的 NTRP 测试结果为`
|
||||||
: "你的 NTRP 测试结果为"}
|
: "你的 NTRP 测试结果为"}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -776,13 +782,24 @@ function Result() {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{/* 隐藏的 RadarV2 用于生成完整图片,不显示在界面上 */}
|
{/* 隐藏的 RadarV2 用于生成完整图片,不显示在界面上 */}
|
||||||
<View style={{ position: "absolute", top: "-9999px", left: "-9999px", width: "0px", height: "0px", overflow: "hidden" }}>
|
<View
|
||||||
<RadarChartV2
|
style={{
|
||||||
ref={radarV2Ref}
|
position: "absolute",
|
||||||
|
top: "-9999px",
|
||||||
|
left: "-9999px",
|
||||||
|
width: "0px",
|
||||||
|
height: "0px",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RadarChartV2
|
||||||
|
ref={radarV2Ref}
|
||||||
data={radarData}
|
data={radarData}
|
||||||
title={(userInfo as any)?.nickname
|
title={
|
||||||
? `${(userInfo as any).nickname}的 NTRP 测试结果为`
|
(userInfo as any)?.nickname
|
||||||
: "你的 NTRP 测试结果为"}
|
? `${(userInfo as any).nickname}的 NTRP 测试结果为`
|
||||||
|
: "你的 NTRP 测试结果为"
|
||||||
|
}
|
||||||
ntrpLevel={result?.ntrp_level}
|
ntrpLevel={result?.ntrp_level}
|
||||||
levelDescription={result?.level_description}
|
levelDescription={result?.level_description}
|
||||||
avatarUrl={(userInfo as any)?.avatar_url}
|
avatarUrl={(userInfo as any)?.avatar_url}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import Taro from "@tarojs/taro";
|
import Taro from "@tarojs/taro";
|
||||||
import { OSS_BASE_URL } from "@/config/api";
|
import { OSS_BASE_URL } from "@/config/api";
|
||||||
|
|
||||||
// const qrCodeUrl = `${OSS_BASE_URL}/images/5e013195-fc79-4082-bf06-9aa79aea65ae.png`;
|
|
||||||
|
|
||||||
const bgUrl = `${OSS_BASE_URL}/images/5e2c85ab-fb0c-4026-974d-1e0725181542.png`;
|
const bgUrl = `${OSS_BASE_URL}/images/5e2c85ab-fb0c-4026-974d-1e0725181542.png`;
|
||||||
|
|
||||||
const ringUrl = `${OSS_BASE_URL}/images/b635164f-ecec-434a-a00b-69614a918f2f.png`;
|
const ringUrl = `${OSS_BASE_URL}/images/b635164f-ecec-434a-a00b-69614a918f2f.png`;
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ const loadImage = (src: string): Promise<any> => {
|
|||||||
// 绘制SVG路径到Canvas
|
// 绘制SVG路径到Canvas
|
||||||
const drawSVGPathToCanvas = (ctx: any) => {
|
const drawSVGPathToCanvas = (ctx: any) => {
|
||||||
// 设置绘制样式
|
// 设置绘制样式
|
||||||
ctx.setStrokeStyle('#48D800');
|
ctx.setStrokeStyle('#00E5AD');
|
||||||
ctx.setLineWidth(scale * 3 * dpr);
|
ctx.setLineWidth(scale * 3 * dpr);
|
||||||
ctx.setLineCap('round');
|
ctx.setLineCap('round');
|
||||||
ctx.setLineJoin('round');
|
ctx.setLineJoin('round');
|
||||||
@@ -248,7 +248,7 @@ const drawSVGPathToCanvas = (ctx: any) => {
|
|||||||
ctx.save();
|
ctx.save();
|
||||||
|
|
||||||
// 移动到指定位置并缩放
|
// 移动到指定位置并缩放
|
||||||
ctx.translate(scale * 210 * dpr, scale * 90 * dpr);
|
ctx.translate(scale * 200 * dpr, scale * 90 * dpr);
|
||||||
const scaleValue = 0.8
|
const scaleValue = 0.8
|
||||||
ctx.scale(scaleValue, scaleValue);
|
ctx.scale(scaleValue, scaleValue);
|
||||||
|
|
||||||
@@ -490,8 +490,8 @@ const drawShareCard = async (ctx: any, data: ShareCardData, offscreen: any): Pro
|
|||||||
|
|
||||||
// 绘制背景 - 渐变色 已完成
|
// 绘制背景 - 渐变色 已完成
|
||||||
const gradient = ctx.createLinearGradient(0, 0, 0, canvasHeightPx)
|
const gradient = ctx.createLinearGradient(0, 0, 0, canvasHeightPx)
|
||||||
gradient.addColorStop(0, '#D8FFE5')
|
gradient.addColorStop(0, '#BFFFEF')
|
||||||
gradient.addColorStop(1, '#F9FFFB')
|
gradient.addColorStop(1, '#F2FFFC')
|
||||||
ctx.setFillStyle(gradient)
|
ctx.setFillStyle(gradient)
|
||||||
ctx.fillRect(0, 0, canvasWidthPx, canvasHeightPx)
|
ctx.fillRect(0, 0, canvasWidthPx, canvasHeightPx)
|
||||||
console.log('背景绘制完成')
|
console.log('背景绘制完成')
|
||||||
@@ -543,9 +543,9 @@ const drawShareCard = async (ctx: any, data: ShareCardData, offscreen: any): Pro
|
|||||||
drawBoldText(ctx, '邀你加入', inviteX, inviteY, inviteFontSize, '#000000', "Noto Sans SC")
|
drawBoldText(ctx, '邀你加入', inviteX, inviteY, inviteFontSize, '#000000', "Noto Sans SC")
|
||||||
|
|
||||||
// 绘制"球局"特殊样式
|
// 绘制"球局"特殊样式
|
||||||
const qiuJuX = inviteX + ctx.measureText('邀你加入').width + 5 * dpr
|
const qiuJuX = inviteX + ctx.measureText('邀你加入').width + 4 * dpr
|
||||||
const qiuJuFontSize = scale * 44 * dpr
|
const qiuJuFontSize = scale * 44 * dpr
|
||||||
drawBoldText(ctx, '球局', qiuJuX, inviteY, qiuJuFontSize, '#48D800', '"Noto Sans SC"')
|
drawBoldText(ctx, '球局', qiuJuX, inviteY, qiuJuFontSize, '#00E5AD', '"Noto Sans SC"')
|
||||||
|
|
||||||
// 测试绘制网络图片
|
// 测试绘制网络图片
|
||||||
drawSVGPathToCanvas(ctx)
|
drawSVGPathToCanvas(ctx)
|
||||||
@@ -602,20 +602,29 @@ const drawShareCard = async (ctx: any, data: ShareCardData, offscreen: any): Pro
|
|||||||
// 绘制"单打"标签
|
// 绘制"单打"标签
|
||||||
const danDaX = scale * 100
|
const danDaX = scale * 100
|
||||||
const danDaY = scale * 196
|
const danDaY = scale * 196
|
||||||
const danDaWidth = scale * 76 * dpr
|
|
||||||
const danDaHeight = scale * 40 * dpr
|
const danDaHeight = scale * 40 * dpr
|
||||||
const danDaRadius = scale * 20 * dpr
|
const danDaRadius = scale * 20 * dpr
|
||||||
const danDaFontSize = scale * 22 * dpr
|
const danDaFontSize = scale * 22 * dpr
|
||||||
|
// 根据内容动态计算标签宽度(左右内边距)
|
||||||
|
const danDaPaddingX = scale * 16 * dpr
|
||||||
|
ctx.setFontSize(danDaFontSize)
|
||||||
|
const danDaTextWidth = ctx.measureText(data.gameType).width
|
||||||
|
const danDaWidth = danDaTextWidth + danDaPaddingX * 2
|
||||||
|
|
||||||
drawLabel(ctx, danDaX, danDaY, danDaWidth, danDaHeight, danDaRadius, data.gameType, danDaFontSize)
|
drawLabel(ctx, danDaX, danDaY, danDaWidth, danDaHeight, danDaRadius, data.gameType, danDaFontSize)
|
||||||
|
|
||||||
// 绘制技能等级标签
|
// 绘制技能等级标签(基于“单打”标签实际宽度后移)
|
||||||
const skillX = scale * 190
|
const labelGap = scale * 16 // 两个标签之间的间距(不乘 dpr,保持视觉间距)
|
||||||
|
const skillX = danDaX + danDaWidth + labelGap
|
||||||
const skillY = scale * 196
|
const skillY = scale * 196
|
||||||
const skillWidth = scale * 180 * dpr
|
|
||||||
const skillHeight = scale * 40 * dpr
|
const skillHeight = scale * 40 * dpr
|
||||||
const skillRadius = scale * 20 * dpr
|
const skillRadius = scale * 20 * dpr
|
||||||
const skillFontSize = scale * 22 * dpr
|
const skillFontSize = scale * 22 * dpr
|
||||||
|
// 根据内容动态计算技能标签宽度
|
||||||
|
const skillPaddingX = scale * 20 * dpr
|
||||||
|
ctx.setFontSize(skillFontSize)
|
||||||
|
const skillTextWidth = ctx.measureText(data.skillLevel).width
|
||||||
|
const skillWidth = skillTextWidth + skillPaddingX * 2
|
||||||
|
|
||||||
drawLabel(ctx, skillX, skillY, skillWidth, skillHeight, skillRadius, data.skillLevel, skillFontSize)
|
drawLabel(ctx, skillX, skillY, skillWidth, skillHeight, skillRadius, data.skillLevel, skillFontSize)
|
||||||
|
|
||||||
@@ -627,7 +636,7 @@ const drawShareCard = async (ctx: any, data: ShareCardData, offscreen: any): Pro
|
|||||||
ctx.drawImage(calendarPath, iconX, timeInfoY, iconSize, iconSize)
|
ctx.drawImage(calendarPath, iconX, timeInfoY, iconSize, iconSize)
|
||||||
|
|
||||||
// 绘制日期(绿色)
|
// 绘制日期(绿色)
|
||||||
drawText(ctx, data.gameDate, dateX, timeInfoY + 8, 300, timeInfoFontSize, '#4CAF50')
|
drawText(ctx, data.gameDate, dateX, timeInfoY + 8, 300, timeInfoFontSize, '#00E5AD')
|
||||||
|
|
||||||
// 绘制时间(黑色)
|
// 绘制时间(黑色)
|
||||||
const timeX = textX + ctx.measureText(data.gameDate).width + 10 * dpr
|
const timeX = textX + ctx.measureText(data.gameDate).width + 10 * dpr
|
||||||
|
|||||||
Reference in New Issue
Block a user