Files
084_sort/matching-game.html
2026-04-10 20:17:38 +08:00

801 lines
25 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>罪人与原著 - 连线游戏</title>
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* 继承index.html的样式并添加游戏特定样式 */
:root {
--primary-color: #ff4d4d;
--secondary-color: #ffd700;
--bg-color: #121212;
--text-color: #e0e0e0;
--panel-bg: #1e1e1e;
}
body {
font-family: 'Noto Serif SC', serif;
background-color: var(--bg-color);
color: var(--text-color);
margin: 0;
padding: 0;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
user-select: none;
}
.game-header {
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
background-color: rgba(30, 30, 30, 0.8);
border-bottom: 1px solid var(--secondary-color);
}
.game-title {
font-size: 1.8rem;
color: var(--secondary-color);
margin: 0;
}
.back-btn {
background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%);
border: 2px solid var(--secondary-color);
color: var(--secondary-color);
padding: 0.5rem 1.5rem;
font-family: 'Noto Serif SC', serif;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
clip-path: polygon(10% 0, 100% 0, 100% 90%, 90% 100%, 0 100%, 0 10%);
}
.back-btn:hover {
background: var(--secondary-color);
color: #000;
}
.game-container {
flex: 1;
display: flex;
padding: 20px;
gap: 20px;
position: relative;
}
.sinner-column {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
background-color: rgba(30, 30, 30, 0.5);
border-radius: 10px;
}
.column-title {
text-align: center;
font-size: 1.5rem;
color: var(--secondary-color);
margin-bottom: 20px;
text-shadow: 0 0 5px rgba(255, 215, 0, 0.3);
}
.sinner-card {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
background-color: rgba(42, 42, 42, 0.8);
border: 2px solid var(--secondary-color);
border-radius: 10px;
max-width: 1000px;
transition: all 0.3s ease;
}
.sinner-images-container {
display: flex;
justify-content: center;
gap: 20px;
margin-bottom: 20px;
}
.sinner-image-wrapper {
position: relative;
width: 400px;
height: 600px;
border-radius: 10px;
overflow: hidden;
border: 1px solid rgba(255, 215, 0, 0.3);
transition: all 0.3s ease;
cursor: pointer;
}
.sinner-image-wrapper:hover {
transform: scale(1.05);
border-color: var(--secondary-color);
}
.sinner-image {
width: 100%;
height: 100%;
object-fit: contain;
}
.ego-image-wrapper {
position: relative;
width: 400px;
height: 600px;
border-radius: 10px;
overflow: hidden;
border: 1px solid rgba(255, 215, 0, 0.3);
transition: all 0.3s ease;
cursor: pointer;
opacity: 0;
}
.ego-image-wrapper:hover {
opacity: 1;
transform: scale(1.05);
border-color: var(--secondary-color);
}
.ego-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.image-label {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.7);
color: var(--secondary-color);
text-align: center;
padding: 5px;
font-size: 0.9rem;
}
.hint-text {
font-size: 0.9rem;
color: var(--secondary-color);
margin-top: 10px;
opacity: 0.7;
}
.sources-column {
flex: 1;
display: flex;
flex-direction: column;
padding: 20px;
background-color: rgba(30, 30, 30, 0.5);
border-radius: 10px;
}
.source-item {
display: flex;
align-items: center;
padding: 15px;
margin-bottom: 10px;
background-color: rgba(42, 42, 42, 0.8);
border: 1px solid rgba(255, 215, 0, 0.3);
border-radius: 5px;
cursor: pointer;
transition: all 0.3s ease;
}
.source-item:hover {
background-color: rgba(255, 215, 0, 0.1);
border-color: var(--secondary-color);
}
.source-item.selected {
background-color: rgba(255, 77, 77, 0.2);
border-color: var(--primary-color);
box-shadow: 0 0 10px rgba(255, 77, 77, 0.3);
}
.source-item.matched {
background-color: rgba(255, 215, 0, 0.1);
border-color: var(--secondary-color);
opacity: 0.5;
pointer-events: none;
}
.source-text {
flex: 1;
font-size: 1.1rem;
}
.next-btn {
background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%);
border: 2px solid var(--secondary-color);
color: var(--secondary-color);
padding: 0.8rem 2rem;
font-family: 'Noto Serif SC', serif;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
clip-path: polygon(10% 0, 100% 0, 100% 90%, 90% 100%, 0 100%, 0 10%);
margin-top: 20px;
font-size: 1.1rem;
}
.next-btn:hover {
background: var(--secondary-color);
color: #000;
}
.next-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.message-display {
position: absolute;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(30, 30, 30, 0.9);
padding: 10px 30px;
border-radius: 5px;
border: 1px solid var(--primary-color);
color: var(--text-color);
font-size: 1.2rem;
opacity: 0;
transition: opacity 0.5s ease;
z-index: 100;
}
.message-display.show {
opacity: 1;
}
.results-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.9);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 1000;
opacity: 0;
visibility: hidden;
transition: all 0.5s ease;
overflow-y: auto;
}
.results-container.show {
opacity: 1;
visibility: visible;
}
.results-title {
font-size: 2.5rem;
color: var(--secondary-color);
margin-bottom: 30px;
text-shadow: 0 0 10px rgba(255, 215, 0, 0.3);
}
.results-list {
width: 80%;
max-width: 800px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.result-item {
display: flex;
align-items: center;
padding: 15px;
background-color: rgba(30, 30, 30, 0.8);
border: 1px solid rgba(255, 215, 0, 0.3);
border-radius: 10px;
transition: all 0.3s ease;
}
.result-item.correct {
border-color: var(--secondary-color);
box-shadow: 0 0 10px rgba(255, 215, 0, 0.2);
}
.result-item.incorrect {
border-color: var(--primary-color);
box-shadow: 0 0 10px rgba(255, 77, 77, 0.2);
}
.result-images {
display: flex;
flex-direction: column;
margin-right: 15px;
gap: 5px;
}
.result-image {
width: 60px;
height: 60px;
border-radius: 5px;
object-fit: cover;
}
.result-info {
flex: 1;
}
.result-sinner {
font-size: 1.1rem;
color: var(--text-color);
margin-bottom: 5px;
}
.result-source {
font-size: 0.9rem;
color: var(--secondary-color);
}
.result-status {
font-size: 1.2rem;
margin-left: 10px;
}
.correct-icon {
color: var(--secondary-color);
}
.incorrect-icon {
color: var(--primary-color);
}
.restart-btn {
background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%);
border: 2px solid var(--secondary-color);
color: var(--secondary-color);
padding: 1rem 2rem;
font-family: 'Noto Serif SC', serif;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
clip-path: polygon(10% 0, 100% 0, 100% 90%, 90% 100%, 0 100%, 0 10%);
font-size: 1.2rem;
margin-bottom: 30px;
}
.restart-btn:hover {
background: var(--secondary-color);
color: #000;
}
/* 响应式设计 */
@media (max-width: 768px) {
.game-container {
flex-direction: column;
overflow-y: auto;
}
.sinner-images-container {
flex-direction: column;
}
.sinner-image-wrapper, .ego-image-wrapper {
width: 150px;
height: 150px;
}
.results-list {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="game-header">
<h1 class="game-title">罪人与原著</h1>
<button class="back-btn" onclick="window.location.href='index.html'">
<i class="fas fa-arrow-left"></i> 返回
</button>
</div>
<div class="game-container">
<div class="sinner-column">
<div class="column-title">罪人</div>
<div class="sinner-card" id="sinner-card">
<div class="sinner-images-container">
<div class="sinner-image-wrapper">
<img src="" alt="" class="sinner-image" id="sinner-image">
<div class="image-label">罪人立绘</div>
</div>
<div class="ego-image-wrapper">
<img src="" alt="" class="ego-image" id="ego-image">
<div class="image-label">初始Ego</div>
</div>
</div>
<div class="hint-text">点击图片查看提示</div>
</div>
<button class="next-btn" id="next-btn" disabled>下一个</button>
</div>
<div class="sources-column">
<div class="column-title">原著</div>
<div id="sources-list">
<!-- 原著列表将通过JavaScript动态生成 -->
</div>
</div>
<div class="message-display" id="message"></div>
</div>
<div class="results-container" id="results-container">
<div class="results-title">匹配结果</div>
<div class="results-list" id="results-list">
<!-- 结果列表将通过JavaScript动态生成 -->
</div>
<button class="restart-btn" onclick="restartGame()">重新开始</button>
</div>
<script>
// 游戏数据
const gameData = [
{ id: 2, sinner: "浮士德", source: "《浮士德》", imgName: "faust", egoImgName: "faust_ego" },
{ id: 3, sinner: "堂吉诃德", source: "《堂吉诃德》", imgName: "don_quixote", egoImgName: "don_quixote_ego" },
{ id: 4, sinner: "良秀", source: "《地狱变》", imgName: "yoshihide", egoImgName: "yoshihide_ego" },
{ id: 5, sinner: "默尔索", source: "《局外人》", imgName: "meursault", egoImgName: "meursault_ego" },
{ id: 6, sinner: "鸿璐", source: "《红楼梦》", imgName: "honglu", egoImgName: "honglu_ego" },
{ id: 7, sinner: "希斯克利夫", source: "《呼啸山庄》", imgName: "heathcliff", egoImgName: "heathcliff_ego" },
{ id: 8, sinner: "以实玛利", source: "《白鲸》", imgName: "ishmael", egoImgName: "ishmael_ego" },
{ id: 9, sinner: "罗佳", source: "《罪与罚》", imgName: "rodya", egoImgName: "rodya_ego" },
{ id: 10, sinner: "但丁", source: "《神曲》", imgName: "dante", egoImgName: "dante_ego" },
{ id: 11, sinner: "辛克莱", source: "《德米安》", imgName: "sinclair", egoImgName: "sinclair_ego" },
{ id: 12, sinner: "奥提斯", source: "《奥德修斯》", imgName: "outis", egoImgName: "outis_ego" },
{ id: 13, sinner: "格里高尔", source: "《变形记》", imgName: "gregor", egoImgName: "gregor_ego" },
];
// 游戏状态
let currentSinnerIndex = 0;
let selectedSourceId = null;
let userAnswers = [];
let shuffledSinners = [];
let shuffledSources = [];
// 初始化游戏
function initGame() {
// 随机打乱罪人和原著顺序
shuffledSinners = shuffleArray([...gameData]);
shuffledSources = shuffleArray([...gameData]);
// 重置游戏状态
currentSinnerIndex = 0;
selectedSourceId = null;
userAnswers = [];
// 显示第一个罪人
displaySinner();
// 生成原著列表
generateSourcesList();
// 禁用"下一个"按钮
document.getElementById('next-btn').disabled = true;
}
// 显示当前罪人
function displaySinner() {
const sinner = shuffledSinners[currentSinnerIndex];
const sinnerImage = document.getElementById('sinner-image');
const egoImage = document.getElementById('ego-image');
const egoImageWrapper = document.querySelector('.ego-image-wrapper');
// 根据罪人名字构建图片路径假设图片在images目录下
// 使用罪人名字作为文件名,支持常见图片格式
const imageExtensions = ['.png'];
let sinnerImageLoaded = false;
let egoImageLoaded = false;
// 尝试加载罪人立绘
for (const ext of imageExtensions) {
const imagePath = `images/sinners/${sinner.imgName}${ext}`;
sinnerImage.src = imagePath;
// 检查图片是否可以加载
sinnerImage.onload = function() {
if (!sinnerImageLoaded) {
sinnerImageLoaded = true;
sinnerImage.style.display = 'block';
}
};
sinnerImage.onerror = function() {
// 如果所有格式都尝试过且都失败,显示默认图标
if (!sinnerImageLoaded && ext === imageExtensions[imageExtensions.length - 1]) {
this.style.display = 'none';
const icon = document.createElement('i');
icon.className = 'fas fa-user';
icon.style.fontSize = '100px';
document.querySelector('.sinner-image-wrapper').appendChild(icon);
}
};
}
// 尝试加载Ego立绘
for (const ext of imageExtensions) {
const egoPath = `images/egos/${sinner.egoImgName}${ext}`;
egoImage.src = egoPath;
// 检查图片是否可以加载
egoImage.onload = function() {
if (!egoImageLoaded) {
egoImageLoaded = true;
egoImage.style.display = 'block';
}
};
egoImage.onerror = function() {
// 如果所有格式都尝试过且都失败,显示默认图标
if (!egoImageLoaded && ext === imageExtensions[imageExtensions.length - 1]) {
this.style.display = 'none';
const icon = document.createElement('i');
icon.className = 'fas fa-ghost';
icon.style.fontSize = '100px';
document.querySelector('.ego-image-wrapper').appendChild(icon);
}
};
}
// 重置Ego图片的透明度
egoImageWrapper.style.opacity = '0';
// 添加点击事件显示Ego立绘
egoImageWrapper.onclick = function() {
egoImageWrapper.style.opacity = egoImageWrapper.style.opacity === '0' ? '1' : '0';
};
}
// 生成原著列表
function generateSourcesList() {
const sourcesList = document.getElementById('sources-list');
sourcesList.innerHTML = '';
shuffledSources.forEach(source => {
const div = document.createElement('div');
div.className = 'source-item';
div.dataset.id = source.id;
const text = document.createElement('div');
text.className = 'source-text';
text.textContent = source.source;
div.appendChild(text);
div.addEventListener('click', handleSourceClick);
sourcesList.appendChild(div);
});
}
// 处理原著点击事件
function handleSourceClick(e) {
const sourceItem = e.currentTarget;
const sourceId = parseInt(sourceItem.dataset.id);
// 如果已经匹配过,不允许再次选择
if (sourceItem.classList.contains('matched')) {
return;
}
// 取消之前的选择
if (selectedSourceId !== null) {
document.querySelector(`.source-item[data-id="${selectedSourceId}"]`).classList.remove('selected');
}
// 标记当前选择
selectedSourceId = sourceId;
sourceItem.classList.add('selected');
// 启用"下一个"按钮
document.getElementById('next-btn').disabled = false;
}
// 下一个罪人
function nextSinner() {
// 记录用户答案
const currentSinner = shuffledSinners[currentSinnerIndex];
userAnswers.push({
sinnerId: currentSinner.id,
sinnerName: currentSinner.sinner,
selectedSourceId: selectedSourceId
});
// 标记已选择的原著为已匹配
document.querySelector(`.source-item[data-id="${selectedSourceId}"]`).classList.add('matched');
// 重置选择
selectedSourceId = null;
// 移动到下一个罪人
currentSinnerIndex++;
// 检查是否还有罪人需要匹配
if (currentSinnerIndex < shuffledSinners.length) {
displaySinner();
document.getElementById('next-btn').disabled = true;
document.querySelector('.ego-image-wrapper').style.opacity = '0';
} else {
// 所有罪人已匹配完成,显示结果
showResults();
}
}
// 显示结果
function showResults() {
const resultsContainer = document.getElementById('results-container');
const resultsList = document.getElementById('results-list');
// 清空结果列表
resultsList.innerHTML = '';
// 计算得分
let correctCount = 0;
// 显示每个罪人的匹配结果
userAnswers.forEach(answer => {
const sinner = gameData.find(s => s.id === answer.sinnerId);
const selectedSource = gameData.find(s => s.id === answer.selectedSourceId);
const isCorrect = answer.sinnerId === answer.selectedSourceId;
if (isCorrect) {
correctCount++;
}
const resultItem = document.createElement('div');
resultItem.className = `result-item ${isCorrect ? 'correct' : 'incorrect'}`;
// 添加罪人立绘和Ego立绘
const resultImages = document.createElement('div');
resultImages.className = 'result-images';
// 尝试加载罪人立绘
const imageExtensions = ['.png'];
let sinnerImageFound = false;
for (const ext of imageExtensions) {
const testImage = new Image();
testImage.src = `images/sinners/${answer.sinnerName}${ext}`;
testImage.onload = function() {
if (!sinnerImageFound) {
sinnerImageFound = true;
const resultImage = document.createElement('img');
resultImage.src = `images/sinners/${answer.sinnerName}${ext}`;
resultImage.alt = answer.sinnerName;
resultImage.className = 'result-image';
resultImages.appendChild(resultImage);
}
};
if (sinnerImageFound) break;
}
// 尝试加载Ego立绘
let egoImageFound = false;
for (const ext of imageExtensions) {
const testImage = new Image();
testImage.src = `images/egos/${answer.sinnerName}${ext}`;
testImage.onload = function() {
if (!egoImageFound) {
egoImageFound = true;
const resultImage = document.createElement('img');
resultImage.src = `images/egos/${answer.sinnerName}${ext}`;
resultImage.alt = answer.sinnerName + ' Ego';
resultImage.className = 'result-image';
resultImages.appendChild(resultImage);
}
};
if (egoImageFound) break;
}
// 如果没有找到图片,显示默认图标
if (!sinnerImageFound) {
const icon = document.createElement('i');
icon.className = 'fas fa-user';
icon.style.fontSize = '30px';
resultImages.appendChild(icon);
}
if (!egoImageFound) {
const icon = document.createElement('i');
icon.className = 'fas fa-ghost';
icon.style.fontSize = '30px';
resultImages.appendChild(icon);
}
const resultInfo = document.createElement('div');
resultInfo.className = 'result-info';
const resultSinner = document.createElement('div');
resultSinner.className = 'result-sinner';
resultSinner.textContent = answer.sinnerName;
const resultSource = document.createElement('div');
resultSource.className = 'result-source';
resultSource.textContent = `匹配: ${selectedSource.source}`;
resultInfo.appendChild(resultSinner);
resultInfo.appendChild(resultSource);
const resultStatus = document.createElement('div');
resultStatus.className = 'result-status';
if (isCorrect) {
resultStatus.innerHTML = '<i class="fas fa-check correct-icon"></i>';
} else {
resultStatus.innerHTML = '<i class="fas fa-times incorrect-icon"></i>';
}
resultItem.appendChild(resultImages);
resultItem.appendChild(resultInfo);
resultItem.appendChild(resultStatus);
resultsList.appendChild(resultItem);
});
// 显示结果容器
resultsContainer.classList.add('show');
// 显示得分消息
showMessage(`匹配完成!正确率: ${Math.round((correctCount / userAnswers.length) * 100)}%`);
}
// 重新开始游戏
function restartGame() {
// 隐藏结果容器
document.getElementById('results-container').classList.remove('show');
// 重新初始化游戏
initGame();
}
// 显示消息
function showMessage(msg) {
const messageEl = document.getElementById('message');
messageEl.textContent = msg;
messageEl.classList.add('show');
setTimeout(() => {
messageEl.classList.remove('show');
}, 3000);
}
// 数组洗牌函数
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
// 绑定"下一个"按钮点击事件
document.getElementById('next-btn').addEventListener('click', nextSinner);
// 初始化游戏
initGame();
</script>
</body>
</html>