<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>深圳—天空之城无人机导航系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
}
body {
background: linear-gradient(135deg, #0a0a2a, #1a1a3a);
color: #e0f7fa;
min-height: 100vh;
padding: 15px;
position: relative;
overflow-x: hidden;
}
.tech-grid {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
linear-gradient(rgba(0, 150, 255, 0.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(0, 150, 255, 0.1) 1px, transparent 1px);
background-size: 30px 30px;
z-index: -2;
pointer-events: none;
}
.scan-line {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 2px;
background: linear-gradient(90deg, transparent, rgba(0, 255, 255, 0.7), transparent);
box-shadow: 0 0 10px rgba(0, 255, 255, 0.8);
z-index: -1;
animation: scan 3s linear infinite;
pointer-events: none;
}
@keyframes scan {
0% { top: 0; }
100% { top: 100%; }
}
.grass-bg {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 80px;
background: linear-gradient(to top, rgba(0, 100, 0, 0.3), transparent);
z-index: -1;
}
.grass-bg::before {
content: "";
position: absolute;
top: -15px;
left: 0;
width: 100%;
height: 30px;
background:
radial-gradient(circle at 10% 20%, rgba(0, 150, 0, 0.4) 8px, transparent 8px),
radial-gradient(circle at 30% 30%, rgba(0, 150, 0, 0.4) 12px, transparent 12px),
radial-gradient(circle at 50% 10%, rgba(0, 150, 0, 0.4) 10px, transparent 10px),
radial-gradient(circle at 70% 25%, rgba(0, 150, 0, 0.4) 11px, transparent 11px),
radial-gradient(circle at 90% 15%, rgba(0, 150, 0, 0.4) 9px, transparent 9px);
}
.container {
display: flex;
max-width: 1300px;
margin: 0 auto;
gap: 30px;
height: calc(100vh - 120px);
}
header {
text-align: center;
margin-bottom: 20px;
padding: 15px;
background: rgba(0, 20, 40, 0.7);
border-radius: 12px;
box-shadow: 0 0 15px rgba(0, 200, 255, 0.3);
position: relative;
z-index: 1;
border: 1px solid rgba(0, 200, 255, 0.3);
height: 80px;
display: flex;
flex-direction: column;
justify-content: center;
}
h1 {
font-size: 2.2rem;
background: linear-gradient(90deg, #00e5ff, #00ffaa, #00e5ff);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: 0 0 10px rgba(0, 200, 255, 0.5);
letter-spacing: 2px;
animation: glow 2s ease-in-out infinite alternate;
}
@keyframes glow {
0% {
text-shadow: 0 0 10px rgba(0, 200, 255, 0.5),
0 0 20px rgba(0, 200, 255, 0.3),
0 0 30px rgba(0, 200, 255, 0.2);
}
100% {
text-shadow: 0 0 15px rgba(0, 200, 255, 0.8),
0 0 25px rgba(0, 200, 255, 0.5),
0 0 35px rgba(0, 200, 255, 0.3);
}
}
.map-container {
flex: 1.2;
background: rgba(10, 25, 47, 0.8);
border-radius: 15px;
padding: 25px;
box-shadow: 0 0 25px rgba(0, 150, 255, 0.2);
position: relative;
overflow: hidden;
border: 1px solid rgba(0, 200, 255, 0.2);
display: flex;
flex-direction: column;
}
.map-container::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border: 1px solid rgba(0, 200, 255, 0.1);
border-radius: 15px;
pointer-events: none;
}
.compass {
position: relative;
width: 380px;
height: 380px;
margin: 0 auto 20px;
background: rgba(5, 15, 30, 0.9);
border-radius: 50%;
box-shadow: 0 0 20px rgba(0, 200, 255, 0.3);
border: 1px solid rgba(0, 200, 255, 0.2);
flex-shrink: 0;
}
.compass-center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 30px;
height: 30px;
background: rgba(0, 50, 100, 0.9);
border-radius: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 10;
box-shadow: 0 0 8px rgba(0, 200, 255, 0.5);
}
.drone-icon {
width: 20px;
height: 20px;
filter: invert(70%) sepia(50%) saturate(500%) hue-rotate(160deg);
}
.compass-center span {
font-size: 0.7rem;
color: #80deea;
margin-top: 2px;
}
.compass-direction {
position: absolute;
font-weight: bold;
color: #4fc3f7;
text-shadow: 0 0 5px rgba(79, 195, 247, 0.7);
font-size: 1.1rem;
}
.north {
top: 15px;
left: 50%;
transform: translateX(-50%);
}
.south {
bottom: 15px;
left: 50%;
transform: translateX(-50%);
}
.east {
right: 15px;
top: 50%;
transform: translateY(-50%);
}
.west {
left: 15px;
top: 50%;
transform: translateY(-50%);
}
.compass-line {
position: absolute;
background: rgba(79, 195, 247, 0.3);
}
.vertical {
width: 1px;
height: 100%;
left: 50%;
top: 0;
}
.horizontal {
width: 100%;
height: 1px;
top: 50%;
left: 0;
}
.park {
position: absolute;
width: 14px;
height: 14px;
border-radius: 50%;
background: radial-gradient(circle, #00c853, #006400);
cursor: pointer;
box-shadow: 0 0 8px rgba(0, 200, 83, 0.7);
transition: transform 0.3s, box-shadow 0.3s;
z-index: 5;
}
.park:hover {
transform: scale(1.3);
box-shadow: 0 0 12px rgba(0, 255, 100, 0.9);
}
.park-label {
position: absolute;
font-size: 0.8rem;
white-space: nowrap;
background: rgba(0, 20, 40, 0.8);
padding: 3px 8px;
border-radius: 4px;
pointer-events: none;
opacity: 0.9;
border: 1px solid rgba(255, 255, 255, 0.2);
}
#xiangmi .park-label {
color: #ff6b6b;
font-family: 'Microsoft YaHei', sans-serif;
font-weight: bold;
}
#lianhua .park-label {
color: #ffe66d;
font-family: 'KaiTi', cursive;
}
#huanggang .park-label {
color: #ff9ff3;
font-family: 'SimHei', sans-serif;
font-weight: bold;
}
.connection-line {
position: absolute;
border-top: 2px dashed rgba(0, 230, 255, 0.7);
transform-origin: 0 0;
z-index: 1;
pointer-events: none;
}
.distance-label {
position: absolute;
color: #00e5ff;
font-size: 0.8rem;
background: rgba(0, 20, 40, 0.7);
padding: 3px 8px;
border-radius: 4px;
pointer-events: none;
border: 1px solid rgba(0, 200, 255, 0.3);
}
.angle-label {
position: absolute;
color: #ff4081;
font-size: 0.8rem;
background: rgba(0, 20, 40, 0.7);
padding: 3px 8px;
border-radius: 4px;
pointer-events: none;
border: 1px solid rgba(255, 64, 129, 0.3);
}
.angle-arc {
position: absolute;
border: 2px solid rgba(255, 64, 129, 0.7);
border-radius: 50%;
pointer-events: none;
z-index: 2;
}
.drone {
position: absolute;
width: 40px;
height: 40px;
z-index: 20;
pointer-events: none;
filter: invert(70%) sepia(50%) saturate(500%) hue-rotate(160deg);
transition: all 0.1s linear;
transform-origin: center;
}
.scale-container {
display: flex;
align-items: center;
justify-content: center;
margin-top: 15px;
padding: 8px;
background: rgba(0, 20, 40, 0.7);
border-radius: 6px;
border: 1px solid rgba(0, 200, 255, 0.3);
}
.scale-line {
width: 50px;
height: 6px;
background: linear-gradient(90deg, #00e5ff, #00ffaa);
position: relative;
margin: 0 10px;
box-shadow: 0 0 8px rgba(0, 200, 255, 0.5);
}
.scale-line::before, .scale-line::after {
content: "";
position: absolute;
width: 2px;
height: 12px;
background: #00e5ff;
top: -3px;
}
.scale-line::before {
left: 0;
}
.scale-line::after {
right: 0;
}
.scale-label {
color: #80deea;
font-size: 0.9rem;
white-space: nowrap;
}
.map-info {
margin-top: 10px;
text-align: center;
font-size: 0.9rem;
color: #80deea;
}
.park-buttons {
display: flex;
justify-content: space-between;
margin-top: 15px;
gap: 10px;
}
.park-btn {
flex: 1;
padding: 8px 5px;
background: rgba(0, 50, 100, 0.7);
border: 1px solid rgba(0, 200, 255, 0.3);
border-radius: 5px;
color: #e0f7fa;
cursor: pointer;
transition: all 0.3s;
font-size: 0.8rem;
text-align: center;
box-shadow: 0 0 5px rgba(0, 200, 255, 0.3);
}
.park-btn:hover {
background: rgba(0, 100, 200, 0.7);
box-shadow: 0 0 10px rgba(0, 200, 255, 0.5);
}
.park-btn.active {
background: rgba(0, 150, 255, 0.7);
box-shadow: 0 0 15px rgba(0, 200, 255, 0.7);
}
.question-container {
flex: 1;
background: rgba(10, 25, 47, 0.8);
border-radius: 15px;
padding: 20px;
box-shadow: 0 0 25px rgba(0, 150, 255, 0.2);
display: flex;
flex-direction: column;
border: 1px solid rgba(0, 200, 255, 0.2);
}
.question-box {
background: rgba(5, 15, 30, 0.9);
border-radius: 10px;
padding: 20px;
margin-bottom: 20px;
min-height: 150px;
box-shadow: 0 0 15px rgba(0, 200, 255, 0.2);
border: 1px solid rgba(0, 200, 255, 0.1);
}
.question-title {
font-size: 1.2rem;
color: #4fc3f7;
margin-bottom: 15px;
text-shadow: 0 0 5px rgba(79, 195, 247, 0.5);
}
.question-text {
font-size: 1.1rem;
line-height: 1.6;
color: #e0f7fa;
}
.blank {
display: inline-block;
min-width: 80px;
height: 35px;
background: rgba(0, 50, 100, 0.7);
border: 2px solid #00e5ff;
border-radius: 5px;
margin: 0 5px;
text-align: center;
line-height: 35px;
color: #00e5ff;
cursor: pointer;
transition: all 0.2s;
box-shadow: 0 0 5px rgba(0, 200, 255, 0.3);
}
.blank.active {
background: rgba(0, 100, 200, 0.7);
box-shadow: 0 0 10px rgba(0, 200, 255, 0.5);
border-color: #00ffaa;
}
.keyboard {
display: grid;
grid-template-columns: repeat(10, 1fr);
gap: 8px;
margin-bottom: 20px;
}
.key {
background: rgba(0, 50, 100, 0.7);
border: none;
border-radius: 5px;
color: #e0f7fa;
padding: 10px 5px;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.2s;
box-shadow: 0 0 5px rgba(0, 200, 255, 0.3);
border: 1px solid rgba(0, 200, 255, 0.2);
}
.key:hover {
background: rgba(0, 100, 200, 0.7);
box-shadow: 0 0 10px rgba(0, 200, 255, 0.5);
}
.key:active {
transform: scale(0.95);
background: rgba(0, 150, 255, 0.7);
}
.action-buttons {
display: flex;
gap: 15px;
}
.action-btn {
flex: 1;
padding: 12px;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}
.confirm {
background: linear-gradient(135deg, #00c853, #009624);
color: white;
}
.delete {
background: linear-gradient(135deg, #ff9100, #ff6d00);
color: white;
}
.clear {
background: linear-gradient(135deg, #ff1744, #d50000);
color: white;
}
.action-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
.action-btn:active {
transform: translateY(0);
}
.answer-display {
margin-top: 20px;
padding: 15px;
background: rgba(5, 15, 30, 0.9);
border-radius: 10px;
min-height: 60px;
font-size: 1.2rem;
color: #00e5ff;
box-shadow: 0 0 10px rgba(0, 200, 255, 0.2);
display: flex;
flex-wrap: wrap;
align-items: center;
border: 1px solid rgba(0, 200, 255, 0.2);
}
.feedback {
margin-top: 15px;
padding: 10px;
border-radius: 8px;
text-align: center;
font-weight: bold;
opacity: 0;
transition: opacity 0.5s;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.correct {
background: rgba(0, 200, 83, 0.2);
color: #00e5ff;
opacity: 1;
border-color: rgba(0, 200, 83, 0.3);
}
.incorrect {
background: rgba(255, 23, 68, 0.2);
color: #ff4081;
opacity: 1;
border-color: rgba(255, 23, 68, 0.3);
}
@media (max-width: 900px) {
.container {
flex-direction: column;
}
.keyboard {
grid-template-columns: repeat(6, 1fr);
}
}
</style>
</head>
<body>
<div class="tech-grid"></div>
<div class="scan-line"></div>
<div class="grass-bg"></div>
<header>
<h1>深圳—天空之城</h1>
</header>
<div class="container">
<div class="map-container">
<div class="compass">
<div class="compass-line vertical"></div>
<div class="compass-line horizontal"></div>
<div class="compass-direction north">北</div>
<div class="compass-direction south">南</div>
<div class="compass-direction east">东</div>
<div class="compass-direction west">西</div>
<div class="compass-center">
<svg class="drone-icon" viewBox="0 0 100 100">
<path d="M50,10 L60,30 L70,30 L60,50 L50,70 L40,50 L30,30 L40,30 Z" fill="#00e5ff"/>
<circle cx="50" cy="50" r="5" fill="#00e5ff"/>
<circle cx="35" cy="35" r="3" fill="#00e5ff"/>
<circle cx="65" cy="35" r="3" fill="#00e5ff"/>
<circle cx="35" cy="65" r="3" fill="#00e5ff"/>
<circle cx="65" cy="65" r="3" fill="#00e5ff"/>
</svg>
<span>基地</span>
</div>
<svg class="drone" id="drone" viewBox="0 0 100 100" style="display: none;">
<path d="M50,10 L60,30 L70,30 L60,50 L50,70 L40,50 L30,30 L40,30 Z" fill="#00e5ff"/>
<circle cx="50" cy="50" r="5" fill="#00e5ff"/>
<circle cx="35" cy="35" r="3" fill="#00e5ff"/>
<circle cx="65" cy="35" r="3" fill="#00e5ff"/>
<circle cx="35" cy="65" r="3" fill="#00e5ff"/>
<circle cx="65" cy="65" r="3" fill="#00e5ff"/>
</svg>
<div class="park" id="xiangmi" style="top: 20%; left: 55%; display: none;">
<div class="park-label" style="top: -25px; left: -10px;">香蜜湖公园</div>
</div>
<div class="park" id="lianhua" style="top: 35%; left: 65%; display: none;">
<div class="park-label" style="top: -25px; left: -10px;">莲花山公园</div>
</div>
<div class="park" id="huanggang" style="top: 70%; left: 30%; display: none;">
<div class="park-label" style="top: 20px; left: -10px;">皇岗公园</div>
</div>
</div>
<div class="scale-container">
<div class="scale-line"></div>
<div class="scale-label">图上1厘米表示实际距离1千米</div>
</div>
<div class="park-buttons">
<button class="park-btn" data-park="xiangmi">香蜜湖公园</button>
<button class="park-btn" data-park="lianhua">莲花山公园</button>
<button class="park-btn" data-park="huanggang">皇岗公园</button>
</div>
<div class="map-info">
<p>请点击下方按钮选择公园并回答问题</p>
</div>
</div>
<div class="question-container">
<div class="question-box">
<div class="question-title">当前问题</div>
<div class="question-text" id="question-text">
请点击下方按钮选择公园开始答题
</div>
</div>
<div class="answer-display" id="answer-display">
您的答案将显示在这里
</div>
<div class="keyboard">
<button class="key" data-key="东">东</button>
<button class="key" data-key="南">南</button>
<button class="key" data-key="西">西</button>
<button class="key" data-key="北">北</button>
<button class="key" data-key="偏">偏</button>
<button class="key" data-key="度">度</button>
<button class="key" data-key="米">米</button>
<button class="key" data-key="千米">千米</button>
<button class="key" data-key="1">1</button>
<button class="key" data-key="2">2</button>
<button class="key" data-key="3">3</button>
<button class="key" data-key="4">4</button>
<button class="key" data-key="5">5</button>
<button class="key" data-key="6">6</button>
<button class="key" data-key="7">7</button>
<button class="key" data-key="8">8</button>
<button class="key" data-key="9">9</button>
<button class="key" data-key="0">0</button>
</div>
<div class="action-buttons">
<button class="action-btn confirm" id="confirm-btn">确认</button>
<button class="action-btn delete" id="delete-btn">删除</button>
<button class="action-btn clear" id="clear-btn">清空</button>
</div>
<div class="feedback" id="feedback"></div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const parks = {
xiangmi: {
name: '香蜜湖公园',
angle: '北偏东10度',
distance: 6,
mapDistance: '6厘米',
question: '香蜜湖公园在基地的<span class="blank" data-index="0">□</span>,距离<span class="blank" data-index="1">□</span>。',
answers: [
'北偏东10度,距离6千米',
'北偏东10度,距离6000米'
],
baseAngle: 0,
offsetAngle: 10,
position: { top: '20%', left: '55%' },
namePosition: { top: '-25px', left: '-10px' },
distancePosition: { xOffset: 0.6, yOffset: 0.6 },
anglePosition: { radius: 65, offset: 5 }
},
lianhua: {
name: '莲花山公园',
angle: '北偏东60度',
distance: 4,
mapDistance: '4厘米',
question: '莲花山公园在基地的<span class="blank" data-index="0">□</span>,距离<span class="blank" data-index="1">□</span>。',
answers: [
'北偏东60度,距离4千米',
'北偏东60度,距离4000米'
],
baseAngle: 0,
offsetAngle: 60,
position: { top: '35%', left: '65%' },
namePosition: { top: '-25px', left: '-10px' },
distancePosition: { xOffset: 0.4, yOffset: 0.4 },
anglePosition: { radius: 65, offset: 30 }
},
huanggang: {
name: '皇岗公园',
angle: '南偏西45度',
distance: 7,
mapDistance: '7厘米',
question: '皇岗公园在基地的<span class="blank" data-index="0">□</span>,距离<span class="blank" data-index="1">□</span>。',
answers: [
'南偏西45度,距离7千米',
'南偏西45度,距离7000米'
],
baseAngle: 180,
offsetAngle: 45,
position: { top: '70%', left: '30%' },
namePosition: { top: '20px', left: '-10px' },
distancePosition: { xOffset: 0.5, yOffset: 0.5 },
anglePosition: { radius: 65, offset: -22.5 }
}
};
let currentPark = null;
let userAnswer = ['', ''];
let activeBlankIndex = 0;
const questionText = document.getElementById('question-text');
const answerDisplay = document.getElementById('answer-display');
const feedback = document.getElementById('feedback');
const confirmBtn = document.getElementById('confirm-btn');
const deleteBtn = document.getElementById('delete-btn');
const clearBtn = document.getElementById('clear-btn');
const keys = document.querySelectorAll('.key');
const parkBtns = document.querySelectorAll('.park-btn');
const drone = document.getElementById('drone');
const parksElements = document.querySelectorAll('.park');
parkBtns.forEach(btn => {
btn.addEventListener('click', function() {
const parkId = this.getAttribute('data-park');
selectPark(parkId);
parkBtns.forEach(b => b.classList.remove('active'));
this.classList.add('active');
});
});
function selectPark(parkId) {
parksElements.forEach(park => {
park.style.display = 'none';
});
document.querySelectorAll('.connection-line, .distance-label, .angle-label, .angle-arc, svg').forEach(el => {
if (el.id !== 'drone') el.remove();
});
currentPark = parks[parkId];
const selectedPark = document.getElementById(parkId);
selectedPark.style.display = 'block';
questionText.innerHTML = currentPark.question;
feedback.className = 'feedback';
feedback.textContent = '';
userAnswer = ['', ''];
updateAnswerDisplay();
document.querySelectorAll('.blank').forEach(blank => {
blank.addEventListener('click', function() {
document.querySelectorAll('.blank').forEach(b => {
b.classList.remove('active');
});
this.classList.add('active');
activeBlankIndex = parseInt(this.getAttribute('data-index'));
});
});
if (document.querySelector('.blank')) {
document.querySelector('.blank').classList.add('active');
activeBlankIndex = 0;
}
drawConnection(parkId);
playSound('click');
}
function drawConnection(parkId) {
const park = document.getElementById(parkId);
const compass = document.querySelector('.compass');
const center = document.querySelector('.compass-center');
const centerRect = center.getBoundingClientRect();
const parkRect = park.getBoundingClientRect();
const compassRect = compass.getBoundingClientRect();
const centerX = centerRect.left + centerRect.width/2 - compassRect.left;
const centerY = centerRect.top + centerRect.height/2 - compassRect.top;
const parkX = parkRect.left + parkRect.width/2 - compassRect.left;
const parkY = parkRect.top + parkRect.height/2 - compassRect.top;
const dx = parkX - centerX;
const dy = parkY - centerY;
const distance = Math.sqrt(dx*dx + dy*dy);
let angle = Math.atan2(dy, dx) * 180 / Math.PI;
if (angle < 0) angle += 360;
const line = document.createElement('div');
line.className = 'connection-line';
line.style.width = `${distance}px`;
line.style.left = `${centerX}px`;
line.style.top = `${centerY}px`;
line.style.transform = `rotate(${angle}deg)`;
const distanceLabel = document.createElement('div');
distanceLabel.className = 'distance-label';
distanceLabel.textContent = `${parks[parkId].mapDistance}`;
let distanceX, distanceY;
if (parkId === 'xiangmi') {
distanceX = centerX + dx * 0.6;
distanceY = centerY + dy * 0.6 - 15;
} else if (parkId === 'lianhua') {
distanceX = centerX + dx * 0.4;
distanceY = centerY + dy * 0.4 - 15;
} else if (parkId === 'huanggang') {
// 皇岗公园:7厘米标签向西移动1厘米
const distanceAngle = 180 + 60;
const distanceRadius = distance * 0.5;
distanceX = centerX + Math.cos((distanceAngle - 90) * Math.PI / 180) * distanceRadius - 19; // 向西移动1厘米(19px)
distanceY = centerY + Math.sin((distanceAngle - 90) * Math.PI / 180) * distanceRadius;
}
distanceLabel.style.left = `${distanceX}px`;
distanceLabel.style.top = `${distanceY}px`;
const angleLabel = document.createElement('div');
angleLabel.className = 'angle-label';
if (parkId === 'xiangmi') {
angleLabel.textContent = '10°';
} else if (parkId === 'lianhua') {
angleLabel.textContent = '60°';
} else if (parkId === 'huanggang') {
angleLabel.textContent = '45°';
}
const arc = document.createElement('div');
arc.className = 'angle-arc';
const baseAngle = parks[parkId].baseAngle;
const offsetAngle = parks[parkId].offsetAngle;
const arcRadius = 50;
arc.style.width = `${arcRadius * 2}px`;
arc.style.height = `${arcRadius * 2}px`;
arc.style.left = `${centerX - arcRadius}px`;
arc.style.top = `${centerY - arcRadius}px`;
arc.style.borderTop = 'none';
arc.style.borderRight = 'none';
arc.style.borderBottom = 'none';
arc.style.borderLeft = 'none';
createSVGArc(compass, centerX, centerY, arcRadius, baseAngle, offsetAngle, parkId, angleLabel);
compass.appendChild(line);
compass.appendChild(distanceLabel);
compass.appendChild(angleLabel);
compass.appendChild(arc);
}
function createSVGArc(container, centerX, centerY, radius, startAngle, endAngle, parkId, angleLabel) {
const svgNS = "http://www.w3.org/2000/svg";
const svg = document.createElementNS(svgNS, "svg");
svg.setAttribute("width", "380");
svg.setAttribute("height", "380");
svg.setAttribute("style", "position: absolute; top: 0; left: 0; pointer-events: none;");
let startRad, endRad;
if (parkId === 'huanggang') {
startRad = (180 - 90) * Math.PI / 180;
endRad = ((180 + 45) - 90) * Math.PI / 180;
} else if (parkId === 'lianhua') {
startRad = (startAngle - 90) * Math.PI / 180;
endRad = ((startAngle + 60) - 90) * Math.PI / 180;
} else {
startRad = (startAngle - 90) * Math.PI / 180;
endRad = ((startAngle + endAngle) - 90) * Math.PI / 180;
}
const startX = centerX + radius * Math.cos(startRad);
const startY = centerY + radius * Math.sin(startRad);
const endX = centerX + radius * Math.cos(endRad);
const endY = centerY + radius * Math.sin(endRad);
const path = document.createElementNS(svgNS, "path");
const largeArcFlag = endAngle > 180 ? 1 : 0;
let d = `M ${startX} ${startY} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${endX} ${endY}`;
path.setAttribute("d", d);
path.setAttribute("fill", "none");
path.setAttribute("stroke", "rgba(255, 64, 129, 0.7)");
path.setAttribute("stroke-width", "2");
svg.appendChild(path);
container.appendChild(svg);
// 优化角度标签位置
let labelAngle;
let labelRadius;
if (parkId === 'huanggang') {
// 皇岗公园:45°标签向西北方向移动1厘米
labelAngle = 180 + 20; // 南偏西20度
labelRadius = 80; // 4厘米对应80px
// 向西北方向移动1厘米(19px)
const moveAngle = 180 + 45 + 135; // 西北方向角度
const moveDistance = 19; // 1厘米
const baseX = centerX + Math.cos((labelAngle - 90) * Math.PI / 180) * labelRadius;
const baseY = centerY + Math.sin((labelAngle - 90) * Math.PI / 180) * labelRadius;
const finalX = baseX + Math.cos((moveAngle - 90) * Math.PI / 180) * moveDistance;
const finalY = baseY + Math.sin((moveAngle - 90) * Math.PI / 180) * moveDistance;
angleLabel.style.left = `${finalX}px`;
angleLabel.style.top = `${finalY}px`;
return; // 提前返回,不使用通用计算
} else if (parkId === 'lianhua') {
// 莲花山公园:60°标签向西移动1厘米
labelAngle = 0 + 30; // 北偏东30度
labelRadius = 57; // 3厘米对应57px
// 向西移动1厘米(19px)
const baseX = centerX + Math.cos((labelAngle - 90) * Math.PI / 180) * labelRadius;
const baseY = centerY + Math.sin((labelAngle - 90) * Math.PI / 180) * labelRadius;
const finalX = baseX - 19; // 向西移动
const finalY = baseY;
angleLabel.style.left = `${finalX}px`;
angleLabel.style.top = `${finalY}px`;
return; // 提前返回,不使用通用计算
} else {
labelAngle = startAngle + endAngle/2;
labelRadius = 65;
}
if (parkId === 'xiangmi') {
labelRadius = 60;
}
const labelX = centerX + Math.cos((labelAngle - 90) * Math.PI / 180) * labelRadius;
const labelY = centerY + Math.sin((labelAngle - 90) * Math.PI / 180) * labelRadius;
angleLabel.style.left = `${labelX}px`;
angleLabel.style.top = `${labelY}px`;
}
function flyDroneToPark(parkId) {
const park = document.getElementById(parkId);
const compass = document.querySelector('.compass');
const compassRect = compass.getBoundingClientRect();
const parkRect = park.getBoundingClientRect();
const center = document.querySelector('.compass-center');
const centerRect = center.getBoundingClientRect();
const startX = centerRect.left + centerRect.width/2 - compassRect.left - 20;
const startY = centerRect.top + centerRect.height/2 - compassRect.top - 20;
const endX = parkRect.left + parkRect.width/2 - compassRect.left - 20;
const endY = parkRect.top + parkRect.height/2 - compassRect.top - 20;
drone.style.display = 'block';
drone.style.left = `${startX}px`;
drone.style.top = `${startY}px`;
const dx = endX - startX;
const dy = endY - startY;
const angle = Math.atan2(dy, dx) * 180 / Math.PI;
drone.style.transform = `rotate(${angle}deg)`;
playSound('drone');
let progress = 0;
const duration = 2000;
const startTime = Date.now();
function animateDrone() {
const currentTime = Date.now();
progress = (currentTime - startTime) / duration;
if (progress < 1) {
const currentX = startX + dx * progress;
const currentY = startY + dy * progress;
drone.style.left = `${currentX}px`;
drone.style.top = `${currentY}px`;
requestAnimationFrame(animateDrone);
} else {
setTimeout(() => {
returnDroneToBase(startX, startY, dx, dy);
}, 1000);
}
}
animateDrone();
}
function returnDroneToBase(startX, startY, dx, dy) {
let progress = 1;
const duration = 2000;
const startTime = Date.now();
function animateReturn() {
const currentTime = Date.now();
progress = 1 - (currentTime - startTime) / duration;
if (progress > 0) {
const currentX = startX + dx * progress;
const currentY = startY + dy * progress;
drone.style.left = `${currentX}px`;
drone.style.top = `${currentY}px`;
requestAnimationFrame(animateReturn);
} else {
drone.style.display = 'none';
}
}
animateReturn();
}
keys.forEach(key => {
key.addEventListener('click', function() {
if (!currentPark) {
alert('请先选择一个公园!');
return;
}
const keyValue = this.getAttribute('data-key');
userAnswer[activeBlankIndex] += keyValue;
updateAnswerDisplay();
playSound('keypress');
this.style.transform = 'scale(0.9)';
setTimeout(() => {
this.style.transform = '';
}, 100);
});
});
function updateAnswerDisplay() {
document.querySelectorAll('.blank').forEach((blank, index) => {
blank.textContent = userAnswer[index] || '□';
});
answerDisplay.textContent = userAnswer.join(' ') || '您的答案将显示在这里';
}
deleteBtn.addEventListener('click', function() {
if (userAnswer[activeBlankIndex].length > 0) {
userAnswer[activeBlankIndex] = userAnswer[activeBlankIndex].slice(0, -1);
updateAnswerDisplay();
playSound('delete');
}
});
clearBtn.addEventListener('click', function() {
userAnswer[activeBlankIndex] = '';
updateAnswerDisplay();
playSound('clear');
});
confirmBtn.addEventListener('click', function() {
if (!currentPark) {
alert('请先选择一个公园!');
return;
}
if (!userAnswer[0] || !userAnswer[1]) {
alert('请完成所有填空!');
return;
}
const userAnswerStr = userAnswer.join(',距离') + '';
let isCorrect = false;
for (const correctAnswer of currentPark.answers) {
const normalizedUserAnswer = userAnswerStr.replace(/\s/g, '');
const normalizedCorrectAnswer = correctAnswer.replace(/\s/g, '');
if (normalizedUserAnswer === normalizedCorrectAnswer) {
isCorrect = true;
break;
}
}
if (isCorrect) {
feedback.className = 'feedback correct';
feedback.textContent = '回答正确!无人机已起飞前往目标公园!';
playSound('correct');
const activeParkBtn = document.querySelector('.park-btn.active');
if (activeParkBtn) {
const parkId = activeParkBtn.getAttribute('data-park');
flyDroneToPark(parkId);
}
} else {
feedback.className = 'feedback incorrect';
feedback.textContent = `回答错误!正确答案是:${currentPark.answers[0]} 或 ${currentPark.answers[1]}`;
playSound('incorrect');
}
});
function playSound(type) {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
let oscillator = audioContext.createOscillator();
let gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
switch(type) {
case 'click':
oscillator.frequency.setValueAtTime(800, audioContext.currentTime);
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
oscillator.type = 'sine';
break;
case 'keypress':
oscillator.frequency.setValueAtTime(600, audioContext.currentTime);
gainNode.gain.setValueAtTime(0.2, audioContext.currentTime);
oscillator.type = 'square';
break;
case 'delete':
oscillator.frequency.setValueAtTime(400, audioContext.currentTime);
gainNode.gain.setValueAtTime(0.2, audioContext.currentTime);
oscillator.type = 'sawtooth';
break;
case 'clear':
oscillator.frequency.setValueAtTime(300, audioContext.currentTime);
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
oscillator.type = 'triangle';
break;
case 'correct':
oscillator.frequency.setValueAtTime(523.25, audioContext.currentTime);
oscillator.frequency.setValueAtTime(659.25, audioContext.currentTime + 0.1);
oscillator.frequency.setValueAtTime(783.99, audioContext.currentTime + 0.2);
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
oscillator.type = 'sine';
break;
case 'incorrect':
oscillator.frequency.setValueAtTime(400, audioContext.currentTime);
oscillator.frequency.setValueAtTime(300, audioContext.currentTime + 0.1);
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
oscillator.type = 'sawtooth';
break;
case 'drone':
oscillator.frequency.setValueAtTime(150, audioContext.currentTime);
oscillator.frequency.setValueAtTime(200, audioContext.currentTime + 0.5);
oscillator.frequency.setValueAtTime(250, audioContext.currentTime + 1);
gainNode.gain.setValueAtTime(0.4, audioContext.currentTime);
oscillator.type = 'sawtooth';
break;
}
oscillator.start();
let duration = 0.2;
if (type === 'correct') duration = 0.5;
if (type === 'drone') duration = 2;
gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + duration);
oscillator.stop(audioContext.currentTime + duration);
}
});
</script>
</body>
</html>
index.html
index.html