ctx.fillStyle = astGrad;
ctx.beginPath();
ctx.arc(ast.x, ast.y, size, 0, Math.PI * 2);
ctx.fill();
// 选中效果
if (ast === selected) {
ctx.strokeStyle = '#ffd700';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(ast.x, ast.y, size + 2, 0, Math.PI * 2);
ctx.stroke();
}
});
// 粒子
particles.forEach(p => p.draw(ctx));
// 恢复状态
ctx.restore();
}
// 大气层摧毁效果
function createAtmosphereDestruction(ast) {
for (let i = 0; i < 30; i++) {
particles.push(new Particle(
ast.x, ast.y,
(Math.random() - 0.5) * 3,
(Math.random() - 0.5) * 3,
20 + Math.random() * 20,
'#ff8c00'
));
}
notify(`大气层拦截: ${ast.name || '小行星'}`);
}
// 撞击效果
function createImpact(ast) {
impactCount++;
// 计算撞击能量(兆吨TNT当量)
const energy = (0.5 * ast.diameter * ast.diameter * ast.diameter * 3000 * 12 * 12) / 4.184e15;
// 计算陨石坑大小(千米)
const craterSize = Math.pow(energy, 0.29) * 0.1;
// 计算火球半径(千米)
const fireballRadius = Math.pow(energy, 0.4) * 0.1;
// 计算冲击波半径(千米)
const shockwaveRadius = Math.pow(energy, 0.33) * 2;
// 计算震级
const magnitude = Math.log10(energy * 4.184e15) / 1.5 - 4;
// 创建陨石坑
craters.push({
x: ast.x,
y: ast.y,
r: Math.max(5, Math.log(ast.diameter) * 2),
time: Date.now()
});
// 创建爆炸粒子
for (let i = 0; i < 100; i++) {
particles.push(new Particle(
ast.x, ast.y,
(Math.random() - 0.5) * 5,
(Math.random() - 0.5) * 5,
30 + Math.random() * 30,
i < 50 ? '#ff6b35' : '#ffd700'
));
}
// 更新撞击面板
document.getElementById('target').textContent = ast.name || '未知';
document.getElementById('energy').textContent = energy.toFixed(1) + ' MT';
document.getElementById('crater').textContent = craterSize.toFixed(1) + ' km';
document.getElementById('coords').textContent = `X:${Math.round(ast.x)} Y:${Math.round(ast.y)}`;
document.getElementById('fireball').textContent = fireballRadius.toFixed(1) + ' km';
document.getElementById('shockwave').textContent = shockwaveRadius.toFixed(1) + ' km';
document.getElementById('magnitude').textContent = magnitude.toFixed(1);
notify(`撞击发生! ${ast.name || '小行星'} - 能量: ${energy.toFixed(1)} MT`);
}
// 点击事件
function onClick(event) {
const rect = canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
// 查找点击的小行星
for (let i = asteroids.length - 1; i >= 0; i--) {
const ast = asteroids[i];
const dist = Math.sqrt((x - ast.x) ** 2 + (y - ast.y) ** 2);
if (dist <= ast.size + 5) {
select(ast);
return;
}
}
// 如果没有点击到小行星,取消选择
selected = null;
document.getElementById('impactBtn').disabled = true;
updateList();
}
// 选择小行星
function select(ast) {
selected = ast;
document.getElementById('impactBtn').disabled = false;
updateList();
notify(`选择: ${ast.name || '小行星'}`);
}
// 更新小行星列表
function updateList() {
const list = document.getElementById('list');
if (asteroids.length === 0) {
list.innerHTML = '
暂无小行星
';
return;
}
list.innerHTML = asteroids.map(ast => `
${ast.name || '小行星'}
${ast.diameter}m | ${Math.sqrt(ast.vx * ast.vx + ast.vy * ast.vy).toFixed(1)}km/s
`).join('');
}
// 添加小行星
function addAsteroid() {
const angle = Math.random() * Math.PI * 2;
const dist = 200 + Math.random() * 100;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const startX = centerX + Math.cos(angle) * dist;
const startY = centerY + Math.sin(angle) * dist;
const dirX = centerX - startX;
const dirY = centerY - startY;
const len = Math.sqrt(dirX * dirX + dirY * dirY);
const speed = 0.5 + Math.random() * 0.5;
const ast = {
id: Date.now(),
name: `小行星-${asteroids.length + 1}`,
diameter: 100 + Math.random() * 400,
danger: Math.random() > 0.7,
x: startX, y: startY,
vx: (dirX / len) * speed,
vy: (dirY / len) * speed
};
ast.size = Math.max(3, Math.log(ast.diameter) * 1.2);
asteroids.push(ast);
updateList();
notify(`生成: ${ast.name}`);
}
// 添加目标小行星
function addTarget() {
const ast = ASTEROIDS[Math.floor(Math.random() * ASTEROIDS.length)];
const angle = Math.random() * Math.PI * 2;
const dist = 250;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const startX = centerX + Math.cos(angle) * dist;
const startY = centerY + Math.sin(angle) * dist;
const dirX = centerX - startX;
const dirY = centerY - startY;
const len = Math.sqrt(dirX * dirX + dirY * dirY);
const speed = 1.0;
const newAst = {
...ast,
id: Date.now(),
x: startX, y: startY,
vx: (dirX / len) * speed,
vy: (dirY / len) * speed
};
newAst.size = Math.max(3, Math.log(newAst.diameter) * 1.2);
asteroids.push(newAst);
updateList();
notify(`目标: ${newAst.name}`);
}
// 创建自定义小行星
function createCustom() {
const size = parseInt(document.getElementById('size').value);
const speedVal = parseInt(document.getElementById('speed').value);
const angleVal = parseInt(document.getElementById('angle').value);
const angle = angleVal * Math.PI / 180;
const dist = 200;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const startX = centerX + Math.cos(angle) * dist;
const startY = centerY + Math.sin(angle) * dist;
const dirX = centerX - startX;
const dirY = centerY - startY;
const len = Math.sqrt(dirX * dirX + dirY * dirY);
const speed = speedVal / 10;
const ast = {
id: Date.now(),
name: `自定义-${size}m`,
diameter: size,
danger: size > 500,
x: startX, y: startY,
vx: (dirX / len) * speed,
vy: (dirY / len) * speed
};
ast.size = Math.max(3, Math.log(ast.diameter) * 1.2);
asteroids.push(ast);
updateList();
notify(`自定义: ${ast.name}`);
}
// 更新参数显示
function updateParams() {
document.getElementById('sizeVal').textContent = document.getElementById('size').value + 'm';
document.getElementById('speedVal').textContent = document.getElementById('speed').value + 'km/s';
document.getElementById('angleVal').textContent = document.getElementById('angle').value + '°';
}
// 撞击功能
function impact() {
if (!selected) return;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const earthRadius = 45;
// 计算撞击点(地球表面)
const angle = Math.atan2(selected.y - centerY, selected.x - centerX);
const impactX = centerX + Math.cos(angle) * earthRadius;
const impactY = centerY + Math.sin(angle) * earthRadius;
// 设置撞击模式
selected.impacting = true;
selected.impactStartTime = Date.now();
selected.impactDuration = 2000; // 2秒撞击过程
selected.startX = selected.x;
selected.startY = selected.y;
selected.targetX = impactX;
selected.targetY = impactY;
document.getElementById('impactBtn').disabled = true;
notify(`撞击模式启动: ${selected.name}`);
}
// 清除所有
function clearAll() {
asteroids = [];
particles = [];
craters = [];
selected = null;
impactCount = 0;
atmosphereDestroyCount = 0;
updateList();
document.getElementById('impactBtn').disabled = true;
notify('已清除所有小行星');
}
// 清除小行星
function clear() {
if (asteroids.length === 0) return;
asteroids.pop();
if (selected && !asteroids.includes(selected)) {
selected = null;
document.getElementById('impactBtn').disabled = true;
}
updateList();
notify('清除一个小行星');
}
// 更新UI
function updateUI() {
document.getElementById('count').textContent = asteroids.length;
document.getElementById('impacts').textContent = impactCount;
document.getElementById('atmosphere').textContent = atmosphereDestroyCount;
document.getElementById('memory').textContent = memory.toFixed(1) + ' MB';
// 威胁等级
const threat = asteroids.filter(a => a.danger).length;
document.getElementById('threat').textContent =
threat > 3 ? '极高' : threat > 1 ? '高' : threat > 0 ? '中' : '低';
document.getElementById('threat').style.color =
threat > 3 ? '#ff4757' : threat > 1 ? '#ffa502' : threat > 0 ? '#ffd700' : '#00ff88';
}
// 更新NASA面板
function updateNASAPanel() {
document.getElementById('neoCount').textContent = REAL_TIME_ASTEROIDS.length;
document.getElementById('hazardCount').textContent = REAL_TIME_ASTEROIDS.filter(a => a.danger).length;
document.getElementById('fireballCount').textContent = FIREBALL_DATA.length;
document.getElementById('lastUpdate').textContent = new Date().toLocaleTimeString();
document.getElementById('dataSource').textContent = 'NASA NeoWs API';
}
// 刷新NASA数据
function refreshNASAData() {
initNASAData();
notify('刷新NASA数据...');
}
// 切换所有面板显示
function toggleAllPanels() {
const panels = document.querySelectorAll('.panel');
const viewControls = document.getElementById('viewControls');
const toggleBtn = document.getElementById('toggleBtn');
const allHidden = panels[0].classList.contains('hidden');
panels.forEach(panel => {
if (allHidden) {
panel.classList.remove('hidden');
} else {
panel.classList.add('hidden');
}
});
if (allHidden) {
viewControls.classList.remove('hidden');
toggleBtn.textContent = '隐藏界面';
toggleBtn.classList.remove('all-hidden');
} else {
viewControls.classList.add('hidden');
toggleBtn.textContent = '显示界面';
toggleBtn.classList.add('all-hidden');
}
}
// 设置视角
function setView(viewType, evt) {
if (evt && evt.target) {
// 更新按钮状态
document.querySelectorAll('.view-btn').forEach(btn => btn.classList.remove('active'));
evt.target.classList.add('active');
}
currentView = viewType;
switch (viewType) {
case 'default':
viewScale = 1;
viewOffsetX = 0;
viewOffsetY = 0;
followTarget = null;
break;
case 'close':
viewScale = 2;
viewOffsetX = 0;
viewOffsetY = 0;
followTarget = null;
break;
case 'wide':
viewScale = 0.5;
viewOffsetX = 0;
viewOffsetY = 0;
followTarget = null;
break;
case 'follow':
if (selected) {
followTarget = selected;
viewScale = 1.5;
} else {
notify('请先选择一个小行星');
return;
}
break;
}
notify(`视角切换: ${getViewName(viewType)}`);
}
function getViewName(viewType) {
const names = {
'default': '标准视角',
'close': '近距离',
'wide': '广角视角',
'follow': '跟踪模式'
};
return names[viewType] || viewType;
}
// 暂停/继续
function togglePause() {
isPaused = !isPaused;
const pauseBtn = document.getElementById('pauseBtn');
pauseBtn.textContent = isPaused ? '继续' : '暂停';
notify(isPaused ? '模拟暂停' : '模拟继续');
}
// 页面加载完成后初始化
window.addEventListener('load', init);
// 窗口大小变化时调整canvas
window.addEventListener('resize', () => {
if (canvas) {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
});
// 初始化参数显示
updateParams();