Enhanced leaderboard UI with gradients, glows, medals and modern design
This commit is contained in:
@@ -108,11 +108,11 @@ class Leaderboard {
|
|||||||
var x = (event.offsetX || event.touches[0].pageX - rect.left)
|
var x = (event.offsetX || event.touches[0].pageX - rect.left)
|
||||||
var y = (event.offsetY || event.touches[0].pageY - rect.top)
|
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 centerX = this.canvas.width / 2
|
||||||
var centerY = this.canvas.height / 2
|
var centerY = this.canvas.height / 2
|
||||||
var modalWidth = 800
|
var modalWidth = 880 // Updated from 800
|
||||||
var modalHeight = 600
|
var modalHeight = 640 // Updated from 600
|
||||||
|
|
||||||
if (x < centerX - modalWidth / 2 || x > centerX + modalWidth / 2 ||
|
if (x < centerX - modalWidth / 2 || x > centerX + modalWidth / 2 ||
|
||||||
y < centerY - modalHeight / 2 || y > centerY + modalHeight / 2) {
|
y < centerY - modalHeight / 2 || y > centerY + modalHeight / 2) {
|
||||||
@@ -158,119 +158,222 @@ class Leaderboard {
|
|||||||
|
|
||||||
this.ctx.scale(ratio, ratio)
|
this.ctx.scale(ratio, ratio)
|
||||||
|
|
||||||
// Draw modal background
|
// Draw modal background with gradient
|
||||||
var modalX = 240
|
var modalX = 200
|
||||||
var modalY = 60
|
var modalY = 40
|
||||||
var modalW = 800
|
var modalW = 880
|
||||||
var modalH = 600
|
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.fillRect(modalX, modalY, modalW, modalH)
|
||||||
|
this.ctx.shadowBlur = 0
|
||||||
|
|
||||||
this.ctx.strokeStyle = "#333333"
|
// Modal border with gradient
|
||||||
this.ctx.lineWidth = 4
|
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)
|
this.ctx.strokeRect(modalX, modalY, modalW, modalH)
|
||||||
|
|
||||||
// Draw title
|
// Draw title with gradient
|
||||||
this.ctx.fillStyle = "#000000"
|
var titleGradient = this.ctx.createLinearGradient(0, 90, 0, 130)
|
||||||
this.ctx.font = "bold 40px " + (strings.font || "sans-serif")
|
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.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 diffX = 640
|
||||||
var diffY = 160
|
var diffY = 155
|
||||||
var difficulties = [
|
var difficulties = [
|
||||||
{ id: "easy", name: "簡単", color: "#00a0e9" },
|
{ id: "easy", name: "簡単", color: "#00a0e9", glow: "#00d4ff" },
|
||||||
{ id: "normal", name: "普通", color: "#00a040" },
|
{ id: "normal", name: "普通", color: "#00a040", glow: "#00ff66" },
|
||||||
{ id: "hard", name: "難しい", color: "#ff8c00" },
|
{ id: "hard", name: "難しい", color: "#ff8c00", glow: "#ffb347" },
|
||||||
{ id: "oni", name: "鬼", color: "#dc143c" },
|
{ id: "oni", name: "鬼", color: "#dc143c", glow: "#ff6b9d" },
|
||||||
{ id: "ura", name: "裏", color: "#9400d3" }
|
{ 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++) {
|
for (var i = 0; i < difficulties.length; i++) {
|
||||||
var diff = difficulties[i]
|
var diff = difficulties[i]
|
||||||
var x = diffX - 200 + i * 100
|
var x = diffX - 220 + i * 110
|
||||||
|
|
||||||
if (diff.id === this.difficulty) {
|
if (diff.id === this.difficulty) {
|
||||||
this.ctx.fillStyle = diff.color
|
// Selected difficulty with glow effect
|
||||||
this.ctx.fillRect(x - 45, diffY - 25, 90, 40)
|
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.fillStyle = "#ffffff"
|
||||||
|
this.ctx.shadowColor = "rgba(0, 0, 0, 0.5)"
|
||||||
|
this.ctx.shadowBlur = 3
|
||||||
} else {
|
} else {
|
||||||
|
// Unselected difficulty
|
||||||
this.ctx.strokeStyle = diff.color
|
this.ctx.strokeStyle = diff.color
|
||||||
this.ctx.lineWidth = 2
|
this.ctx.lineWidth = 3
|
||||||
this.ctx.strokeRect(x - 45, diffY - 25, 90, 40)
|
this.ctx.strokeRect(x - 50, diffY - 30, 100, 50)
|
||||||
this.ctx.fillStyle = diff.color
|
this.ctx.fillStyle = diff.color
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ctx.textAlign = "center"
|
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.fillStyle = "#666666"
|
||||||
this.ctx.font = "18px " + (strings.font || "sans-serif")
|
this.ctx.font = "20px " + (strings.font || "sans-serif")
|
||||||
this.ctx.textAlign = "center"
|
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
|
// Draw leaderboard entries
|
||||||
var startY = 240
|
var startY = 295
|
||||||
var rowHeight = 35
|
var rowHeight = 38
|
||||||
|
|
||||||
this.ctx.font = "20px " + (strings.font || "sans-serif")
|
this.ctx.font = "22px " + (strings.font || "sans-serif")
|
||||||
this.ctx.textAlign = "left"
|
this.ctx.textAlign = "left"
|
||||||
|
|
||||||
if (this.leaderboardData.length === 0) {
|
if (this.leaderboardData.length === 0) {
|
||||||
this.ctx.fillStyle = "#999999"
|
this.ctx.fillStyle = "#999999"
|
||||||
|
this.ctx.font = "24px " + (strings.font || "sans-serif")
|
||||||
this.ctx.textAlign = "center"
|
this.ctx.textAlign = "center"
|
||||||
this.ctx.fillText("暂无排行数据", 640, startY + 100)
|
this.ctx.fillText("暂无排行数据", 640, startY + 120)
|
||||||
} else {
|
} else {
|
||||||
for (var i = 0; i < Math.min(this.leaderboardData.length, 15); i++) {
|
for (var i = 0; i < Math.min(this.leaderboardData.length, 15); i++) {
|
||||||
var entry = this.leaderboardData[i]
|
var entry = this.leaderboardData[i]
|
||||||
var y = startY + i * rowHeight
|
var y = startY + i * rowHeight
|
||||||
var rank = entry.rank
|
var rank = entry.rank
|
||||||
|
|
||||||
// Rank background color
|
// Rank background color with gradient
|
||||||
|
var gradient
|
||||||
if (rank === 1) {
|
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) {
|
} 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) {
|
} 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 {
|
} 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
|
// Rounded rectangle effect
|
||||||
this.ctx.fillStyle = rank <= 3 ? "#ffffff" : "#333333"
|
this.ctx.fillRect(modalX + 20, y - 25, modalW - 40, 35)
|
||||||
this.ctx.font = "bold 20px " + (strings.font || "sans-serif")
|
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.textAlign = "center"
|
||||||
this.ctx.fillText(rank, modalX + 60, y)
|
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
|
// Display name
|
||||||
this.ctx.fillStyle = "#000000"
|
this.ctx.fillStyle = rank <= 3 ? "#1a1a1a" : "#333333"
|
||||||
this.ctx.font = "20px " + (strings.font || "sans-serif")
|
this.ctx.font = (rank <= 3 ? "bold " : "") + "20px " + (strings.font || "sans-serif")
|
||||||
this.ctx.textAlign = "left"
|
this.ctx.textAlign = "left"
|
||||||
var displayName = entry.display_name || entry.username
|
var displayName = entry.display_name || entry.username
|
||||||
if (displayName.length > 15) {
|
if (displayName.length > 18) {
|
||||||
displayName = displayName.substring(0, 15) + "..."
|
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
|
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.textAlign = "right"
|
||||||
this.ctx.fillText(score.toLocaleString(), modalX + modalW - 40, y)
|
this.ctx.fillText(score.toLocaleString(), modalX + modalW - 60, y + 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw close hint
|
// Draw close hint with icon
|
||||||
this.ctx.fillStyle = "#666666"
|
this.ctx.fillStyle = "#888888"
|
||||||
this.ctx.font = "18px " + (strings.font || "sans-serif")
|
this.ctx.font = "18px " + (strings.font || "sans-serif")
|
||||||
this.ctx.textAlign = "center"
|
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()
|
this.ctx.restore()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user