diff --git a/public/src/js/leaderboard.js b/public/src/js/leaderboard.js index e216bfb..1b78280 100644 --- a/public/src/js/leaderboard.js +++ b/public/src/js/leaderboard.js @@ -108,11 +108,11 @@ class Leaderboard { var x = (event.offsetX || event.touches[0].pageX - rect.left) var y = (event.offsetY || event.touches[0].pageY - rect.top) - // Check if clicked outside modal (approximately) + // Check if clicked outside modal - updated dimensions var centerX = this.canvas.width / 2 var centerY = this.canvas.height / 2 - var modalWidth = 800 - var modalHeight = 600 + var modalWidth = 880 // Updated from 800 + var modalHeight = 640 // Updated from 600 if (x < centerX - modalWidth / 2 || x > centerX + modalWidth / 2 || y < centerY - modalHeight / 2 || y > centerY + modalHeight / 2) { @@ -158,119 +158,222 @@ class Leaderboard { this.ctx.scale(ratio, ratio) - // Draw modal background - var modalX = 240 - var modalY = 60 - var modalW = 800 - var modalH = 600 + // Draw modal background with gradient + var modalX = 200 + var modalY = 40 + var modalW = 880 + var modalH = 640 - this.ctx.fillStyle = "#ffffff" + // Gradient background + var bgGradient = this.ctx.createLinearGradient(modalX, modalY, modalX, modalY + modalH) + bgGradient.addColorStop(0, "#ffffff") + bgGradient.addColorStop(1, "#f0f4f8") + this.ctx.fillStyle = bgGradient + this.ctx.shadowColor = "rgba(0, 0, 0, 0.3)" + this.ctx.shadowBlur = 30 + this.ctx.shadowOffsetX = 0 + this.ctx.shadowOffsetY = 10 this.ctx.fillRect(modalX, modalY, modalW, modalH) + this.ctx.shadowBlur = 0 - this.ctx.strokeStyle = "#333333" - this.ctx.lineWidth = 4 + // Modal border with gradient + var borderGradient = this.ctx.createLinearGradient(modalX, modalY, modalX + modalW, modalY + modalH) + borderGradient.addColorStop(0, "#4a90e2") + borderGradient.addColorStop(0.5, "#7b68ee") + borderGradient.addColorStop(1, "#ff6b9d") + this.ctx.strokeStyle = borderGradient + this.ctx.lineWidth = 6 this.ctx.strokeRect(modalX, modalY, modalW, modalH) - // Draw title - this.ctx.fillStyle = "#000000" - this.ctx.font = "bold 40px " + (strings.font || "sans-serif") + // Draw title with gradient + var titleGradient = this.ctx.createLinearGradient(0, 90, 0, 130) + titleGradient.addColorStop(0, "#4a90e2") + titleGradient.addColorStop(1, "#7b68ee") + this.ctx.fillStyle = titleGradient + this.ctx.font = "bold 48px " + (strings.font || "sans-serif") this.ctx.textAlign = "center" - this.ctx.fillText("🏆 排行榜 Leaderboard", 640, 110) + this.ctx.shadowColor = "rgba(0, 0, 0, 0.2)" + this.ctx.shadowBlur = 5 + this.ctx.shadowOffsetX = 2 + this.ctx.shadowOffsetY = 2 + this.ctx.fillText("🏆 排行榜 Leaderboard", 640, 100) + this.ctx.shadowBlur = 0 - // Draw difficulty selector + // Draw difficulty selector with improved styling var diffX = 640 - var diffY = 160 + var diffY = 155 var difficulties = [ - { id: "easy", name: "簡単", color: "#00a0e9" }, - { id: "normal", name: "普通", color: "#00a040" }, - { id: "hard", name: "難しい", color: "#ff8c00" }, - { id: "oni", name: "鬼", color: "#dc143c" }, - { id: "ura", name: "裏", color: "#9400d3" } + { id: "easy", name: "簡単", color: "#00a0e9", glow: "#00d4ff" }, + { id: "normal", name: "普通", color: "#00a040", glow: "#00ff66" }, + { id: "hard", name: "難しい", color: "#ff8c00", glow: "#ffb347" }, + { id: "oni", name: "鬼", color: "#dc143c", glow: "#ff6b9d" }, + { id: "ura", name: "裏", color: "#9400d3", glow: "#da70d6" } ] - this.ctx.font = "24px " + (strings.font || "sans-serif") + this.ctx.font = "bold 22px " + (strings.font || "sans-serif") for (var i = 0; i < difficulties.length; i++) { var diff = difficulties[i] - var x = diffX - 200 + i * 100 + var x = diffX - 220 + i * 110 if (diff.id === this.difficulty) { - this.ctx.fillStyle = diff.color - this.ctx.fillRect(x - 45, diffY - 25, 90, 40) + // Selected difficulty with glow effect + this.ctx.shadowColor = diff.glow + this.ctx.shadowBlur = 15 + var gradient = this.ctx.createLinearGradient(x - 50, diffY - 30, x + 50, diffY + 20) + gradient.addColorStop(0, diff.color) + gradient.addColorStop(1, diff.glow) + this.ctx.fillStyle = gradient + this.ctx.fillRect(x - 50, diffY - 30, 100, 50) + this.ctx.shadowBlur = 0 this.ctx.fillStyle = "#ffffff" + this.ctx.shadowColor = "rgba(0, 0, 0, 0.5)" + this.ctx.shadowBlur = 3 } else { + // Unselected difficulty this.ctx.strokeStyle = diff.color - this.ctx.lineWidth = 2 - this.ctx.strokeRect(x - 45, diffY - 25, 90, 40) + this.ctx.lineWidth = 3 + this.ctx.strokeRect(x - 50, diffY - 30, 100, 50) this.ctx.fillStyle = diff.color } this.ctx.textAlign = "center" - this.ctx.fillText(diff.name, x, diffY + 5) + this.ctx.fillText(diff.name, x, diffY) + this.ctx.shadowBlur = 0 } - // Draw month info + // Draw month info with style this.ctx.fillStyle = "#666666" - this.ctx.font = "18px " + (strings.font || "sans-serif") + this.ctx.font = "20px " + (strings.font || "sans-serif") this.ctx.textAlign = "center" - this.ctx.fillText("当月排行 " + this.currentMonth, 640, 210) + this.ctx.fillText("📅 当月排行 " + this.currentMonth, 640, 215) + + // Header bar + var headerY = 250 + var headerGradient = this.ctx.createLinearGradient(modalX, headerY, modalX, headerY + 35) + headerGradient.addColorStop(0, "#e8eef5") + headerGradient.addColorStop(1, "#d0dae8") + this.ctx.fillStyle = headerGradient + this.ctx.fillRect(modalX + 20, headerY, modalW - 40, 35) + + this.ctx.fillStyle = "#333333" + this.ctx.font = "bold 18px " + (strings.font || "sans-serif") + this.ctx.textAlign = "center" + this.ctx.fillText("排名", modalX + 80, headerY + 23) + this.ctx.textAlign = "left" + this.ctx.fillText("玩家", modalX + 140, headerY + 23) + this.ctx.textAlign = "right" + this.ctx.fillText("分数", modalX + modalW - 60, headerY + 23) // Draw leaderboard entries - var startY = 240 - var rowHeight = 35 + var startY = 295 + var rowHeight = 38 - this.ctx.font = "20px " + (strings.font || "sans-serif") + this.ctx.font = "22px " + (strings.font || "sans-serif") this.ctx.textAlign = "left" if (this.leaderboardData.length === 0) { this.ctx.fillStyle = "#999999" + this.ctx.font = "24px " + (strings.font || "sans-serif") this.ctx.textAlign = "center" - this.ctx.fillText("暂无排行数据", 640, startY + 100) + this.ctx.fillText("暂无排行数据", 640, startY + 120) } else { for (var i = 0; i < Math.min(this.leaderboardData.length, 15); i++) { var entry = this.leaderboardData[i] var y = startY + i * rowHeight var rank = entry.rank - // Rank background color + // Rank background color with gradient + var gradient if (rank === 1) { - this.ctx.fillStyle = "#ffd700" // Gold + // Gold gradient for 1st place + gradient = this.ctx.createLinearGradient(modalX + 20, y - 25, modalX + 20, y + 10) + gradient.addColorStop(0, "#ffd700") + gradient.addColorStop(1, "#ffed4e") + this.ctx.fillStyle = gradient + this.ctx.shadowColor = "#ffd700" + this.ctx.shadowBlur = 20 } else if (rank === 2) { - this.ctx.fillStyle = "#c0c0c0" // Silver + // Silver gradient for 2nd place + gradient = this.ctx.createLinearGradient(modalX + 20, y - 25, modalX + 20, y + 10) + gradient.addColorStop(0, "#c0c0c0") + gradient.addColorStop(1, "#e8e8e8") + this.ctx.fillStyle = gradient + this.ctx.shadowColor = "#c0c0c0" + this.ctx.shadowBlur = 15 } else if (rank === 3) { - this.ctx.fillStyle = "#cd7f32" // Bronze + // Bronze gradient for 3rd place + gradient = this.ctx.createLinearGradient(modalX + 20, y - 25, modalX + 20, y + 10) + gradient.addColorStop(0, "#cd7f32") + gradient.addColorStop(1, "#e9a86a") + this.ctx.fillStyle = gradient + this.ctx.shadowColor = "#cd7f32" + this.ctx.shadowBlur = 12 } else { - this.ctx.fillStyle = "#f5f5f5" + // Regular entries with subtle gradient + gradient = this.ctx.createLinearGradient(modalX + 20, y - 25, modalX + 20, y + 10) + gradient.addColorStop(0, "#ffffff") + gradient.addColorStop(1, "#f8f9fa") + this.ctx.fillStyle = gradient } - this.ctx.fillRect(modalX + 20, y - 20, modalW - 40, 30) - // Rank - this.ctx.fillStyle = rank <= 3 ? "#ffffff" : "#333333" - this.ctx.font = "bold 20px " + (strings.font || "sans-serif") - this.ctx.textAlign = "center" - this.ctx.fillText(rank, modalX + 60, y) + // Rounded rectangle effect + this.ctx.fillRect(modalX + 20, y - 25, modalW - 40, 35) + this.ctx.shadowBlur = 0 + + // Border for entries + if (rank <= 3) { + this.ctx.strokeStyle = rank === 1 ? "#ffd700" : rank === 2 ? "#c0c0c0" : "#cd7f32" + this.ctx.lineWidth = 2 + this.ctx.strokeRect(modalX + 20, y - 25, modalW - 40, 35) + } + + // Rank with medal emoji for top 3 + if (rank === 1) { + this.ctx.fillStyle = "#8b6914" + this.ctx.font = "bold 24px " + (strings.font || "sans-serif") + this.ctx.textAlign = "center" + this.ctx.fillText("🥇", modalX + 80, y + 2) + } else if (rank === 2) { + this.ctx.fillStyle = "#5c5c5c" + this.ctx.font = "bold 24px " + (strings.font || "sans-serif") + this.ctx.textAlign = "center" + this.ctx.fillText("🥈", modalX + 80, y + 2) + } else if (rank === 3) { + this.ctx.fillStyle = "#6d4423" + this.ctx.font = "bold 24px " + (strings.font || "sans-serif") + this.ctx.textAlign = "center" + this.ctx.fillText("🥉", modalX + 80, y + 2) + } else { + this.ctx.fillStyle = rank <= 3 ? "#ffffff" : "#555555" + this.ctx.font = "bold 22px " + (strings.font || "sans-serif") + this.ctx.textAlign = "center" + this.ctx.fillText("#" + rank, modalX + 80, y + 2) + } // Display name - this.ctx.fillStyle = "#000000" - this.ctx.font = "20px " + (strings.font || "sans-serif") + this.ctx.fillStyle = rank <= 3 ? "#1a1a1a" : "#333333" + this.ctx.font = (rank <= 3 ? "bold " : "") + "20px " + (strings.font || "sans-serif") this.ctx.textAlign = "left" var displayName = entry.display_name || entry.username - if (displayName.length > 15) { - displayName = displayName.substring(0, 15) + "..." + if (displayName.length > 18) { + displayName = displayName.substring(0, 18) + "..." } - this.ctx.fillText(displayName, modalX + 100, y) + this.ctx.fillText(displayName, modalX + 140, y + 2) - // Score + // Score with formatting var score = entry.score && entry.score.score ? entry.score.score : 0 + this.ctx.fillStyle = rank <= 3 ? "#1a1a1a" : "#555555" + this.ctx.font = (rank <= 3 ? "bold " : "") + "20px " + (strings.font || "sans-serif") this.ctx.textAlign = "right" - this.ctx.fillText(score.toLocaleString(), modalX + modalW - 40, y) + this.ctx.fillText(score.toLocaleString(), modalX + modalW - 60, y + 2) } } - // Draw close hint - this.ctx.fillStyle = "#666666" + // Draw close hint with icon + this.ctx.fillStyle = "#888888" this.ctx.font = "18px " + (strings.font || "sans-serif") this.ctx.textAlign = "center" - this.ctx.fillText("按ESC或点击外部关闭 Press ESC or click outside to close", 640, 680) + this.ctx.fillText("⌨️ 按ESC或点击外部关闭 Press ESC or click outside to close", 640, 665) this.ctx.restore() }