This commit is contained in:
Evelyn 2025-12-15 01:25:49 +07:00
parent 27fdee7566
commit 19d3eb26cd
7 changed files with 307 additions and 2 deletions

View File

@ -281,6 +281,45 @@ body {
</div> </div>
<script> <script>
// === SIMPAN SKOR KE LEADERBOARD ===
function saveScoreToLeaderboard(totalScore) {
let leaderboard = JSON.parse(localStorage.getItem("leaderboard")) || [];
let record = {
player: localStorage.getItem("username") || "Guest",
score: totalScore,
date: new Date().toLocaleString()
};
leaderboard.push(record);
leaderboard.sort((a, b) => b.score - a.score);
leaderboard = leaderboard.slice(0, 10); // max 10 skor terbaik
localStorage.setItem("leaderboard", JSON.stringify(leaderboard));
}
// === GANTI BAGIAN showEndScreen() DENGAN TAMBAHAN SAVE ===
function showEndScreen() {
clearInterval(countdown);
let baseScore = score;
let timeBonus = time * 5;
let moveBonus = Math.max(0, 200 - moves * 10);
let total = baseScore + timeBonus + moveBonus;
document.getElementById("baseScoreEnd").textContent = baseScore;
document.getElementById("timeBonusEnd").textContent = "+" + timeBonus;
document.getElementById("moveBonusEnd").textContent = "+" + moveBonus;
document.getElementById("totalScoreEnd").textContent = total;
document.getElementById("endScreen").style.display = "flex";
saveScoreToLeaderboard(total); // ⬅ SIMPAN SKOR
}
// === KLIK TOMBOL LEADERBOARD KE HALAMAN LEADERBOARD ===
document.querySelector(".leaderboard").addEventListener("click", () => {
window.location.href = "leaderboard.html";
});
const images = [ const images = [
"images/fruit1.png", "images/fruit1.png",
"images/fruit2.png", "images/fruit2.png",

View File

@ -200,6 +200,16 @@ body {
font-weight: 600; font-weight: 600;
} }
.gameboard {
flex: 1;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
padding: 20px 50px;
box-sizing: border-box;
place-items: center;
}
.play-again { background: #b700ff; color: white; } .play-again { background: #b700ff; color: white; }
.leaderboard { background: gold; } .leaderboard { background: gold; }
.back-menu { background: #444; color: white; } .back-menu { background: #444; color: white; }

View File

@ -7,8 +7,6 @@ body {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
/* Pink → Ungu Gradient */
background: linear-gradient(135deg, #ff9ed1, #d784ff); background: linear-gradient(135deg, #ff9ed1, #d784ff);
} }

109
leaderboard.css Normal file
View File

@ -0,0 +1,109 @@
body {
margin: 0;
font-family: Poppins, Arial, sans-serif;
height: 100vh;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(135deg, #ff9ed1, #d784ff);
}
/* animasi fruit */
.fruit {
position: absolute;
width: 95px;
opacity: 0.85;
animation: float 7s infinite ease-in-out;
pointer-events: none;
filter: drop-shadow(0 4px 6px rgba(0,0,0,0.12));
}
/* Posisi lebih seimbang & tidak menutupi teks */
.f1 { top: 6%; left: 12%; animation-delay: .3s; }
.f2 { top: 12%; right: 18%; animation-delay: 1.2s; }
.f3 { top: 48%; right: 8%; animation-delay: .7s; }
.f4 { top: 72%; left: 20%; animation-delay: 1.6s; }
.f5 { top: 28%; left: 60%; animation-delay: .5s; }
.f6 { top: 68%; right: 22%; animation-delay: 1s; }
.f7 { top: 40%; left: 10%; animation-delay: .9s; }
.f8 { top: 82%; left: 70%; animation-delay: 1.4s; }
.f9 { top: 18%; right: 46%; animation-delay: .6s; }
.f10 { top: 80%; right: 10%; animation-delay: 1.8s; }
/* Smooth floating animation */
@keyframes float {
0% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-18px) rotate(6deg); }
100% { transform: translateY(0) rotate(0deg); }
}
@keyframes drift {
0% { transform: translateX(0); }
50% { transform: translateX(22px); }
100% { transform: translateX(0); }
}
.fruit {
width: clamp(70px, 7vw, 105px);
}
/* leaderboard */
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.rank-up {
animation: rankUp 0.6s ease;
background: #d2ffdf !important; /* hijau */
}
.rank-down {
animation: rankDown 0.6s ease;
background: #ffd2d2 !important; /* merah */
}
@keyframes rankUp {
from { transform: translateY(10px); opacity: 0.4; }
to { transform: translateY(0); opacity: 1; }
}
@keyframes rankDown {
from { transform: translateY(-10px); opacity: 0.4; }
to { transform: translateY(0); opacity: 1; }
}
@keyframes rankFlashUp {
0% { background: rgba(0,255,0,0.7); }
100% { background: rgba(0,255,0,0.2); }
}
@keyframes rankFlashDown {
0% { background: rgba(255,0,0,0.7); }
100% { background: rgba(255,0,0,0.2); }
}
/* Difficulty Tabs */
.difficulty-tabs {
display: flex;
justify-content: center;
gap: 10px;
margin-bottom: 14px;
}
.difficulty-tabs button {
padding: 8px 16px;
border: none;
border-radius: 12px;
font-size: 16px;
cursor: pointer;
background: #d2b6ff;
font-weight: 600;
transition: 0.25s;
}
.difficulty-tabs button.active {
background: #8e00ff;
color: white;
transform: scale(1.1);
}

36
leaderboard.html Normal file
View File

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Leaderboard Memory Card</title>
<link rel="stylesheet" href="leaderboard.css" />
</head>
<body>
<div id="leaderboardPopup" class="leaderboard-popup">
<div class="leaderboard-box">
<h2>🏆 Leaderboard</h2>
<div class="difficulty-tabs">
<button onclick="changeDifficulty('easy')" id="tab-easy">Easy</button>
<button onclick="changeDifficulty('medium')" id="tab-medium">Medium</button>
<button onclick="changeDifficulty('hard')" id="tab-hard">Hard</button>
</div>
<table>
<thead>
<tr>
<th>Rank</th>
<th>Nama</th>
<th>Score</th>
</tr>
</thead>
<tbody id="leaderboard-body"></tbody>
</table>
<button class="close-leaderboard" onclick="closeLeaderboard()">✖ Tutup</button>
</div>
</div>
<script src="leaderboard.js"></script>
</body>
</html>

108
leaderboard.js Normal file
View File

@ -0,0 +1,108 @@
const leaderboardFile = "leaderboard.json";
// 🔹 Buka popup leaderboard
function openLeaderboard() {
document.getElementById("leaderboardPopup").style.display = "flex";
loadLeaderboard();
}
// 🔹 Tutup popup leaderboard
function closeLeaderboard() {
document.getElementById("leaderboardPopup").style.display = "none";
}
// 🔹 Muat leaderboard dari JSON
async function loadLeaderboard() {
const difficulty = localStorage.getItem("difficulty") || "easy";
try {
const res = await fetch(leaderboardFile + "?v=" + Date.now());
const data = await res.json();
// Ambil list sesuai difficulty
let list = data[difficulty] || [];
// Urutkan score dari paling tinggi
list.sort((a, b) => b.score - a.score);
const tbody = document.getElementById("leaderboard-body");
tbody.innerHTML = "";
newOrder.length = 0;
list.forEach((p, i) => {
newOrder.push(p.name);
let movementClass = "";
if (previousOrder.length > 0) {
const oldIndex = previousOrder.indexOf(p.name);
if (oldIndex !== -1) {
if (oldIndex > i) movementClass = "rank-up";
if (oldIndex < i) movementClass = "rank-down";
}
}
tbody.innerHTML += `
<tr class="${movementClass}">
<td class="rank">#${i + 1}</td>
<td>${p.name}</td>
<td>${p.score}</td>
</tr>`;
});
highlightTab(difficulty);
// Simpan posisi lama → jadi perbandingan load berikutnya
localStorage.setItem("prev_ranking", JSON.stringify(newOrder));
} catch (error) {
document.getElementById("leaderboard-body").innerHTML =
`<tr><td colspan="3">⚠ Gagal memuat leaderboard</td></tr>`;
}
}
// 🔹 Menyimpan skor ke leaderboard.json
async function saveScore(name, score, difficulty) {
const res = await fetch(leaderboardFile);
let data = await res.json();
let board = data[difficulty];
const player = board.find(e => e.name.toLowerCase() === name.toLowerCase());
if (player) {
if (score > player.score) player.score = score;
} else {
board.push({ name, score });
}
await fetch(leaderboardFile, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data, null, 2),
});
}
const difficulty = localStorage.getItem("difficulty") || "easy";
localStorage.setItem("playerName", username);
let previousOrder = JSON.parse(localStorage.getItem("prev_ranking") || "[]");
let newOrder = [];
function highlightTab(diff) {
document.querySelectorAll(".difficulty-tabs button").forEach(btn => btn.classList.remove("active"));
document.getElementById("tab-" + diff).classList.add("active");
}
function changeDifficulty(diff) {
localStorage.setItem("difficulty", diff);
previousOrder = JSON.parse(localStorage.getItem("prev_ranking")) || [];
loadLeaderboard();
highlightTab(diff);
}

5
leaderboard.json Normal file
View File

@ -0,0 +1,5 @@
{
"easy": [],
"medium": [],
"hard": []
}