🌩️ 构建闹鬼天气应用:一次进入 3D Web 开发的惊悚之旅

发布: (2025年12月4日 GMT+8 11:17)
5 min read
原文: Dev.to

Source: Dev.to

(请提供您希望翻译的正文内容,我将为您翻译成简体中文,同时保留原始的格式、Markdown 语法和技术术语。)

Introduction

当雾气弥漫、闪电划破天际时,你会知道自己已经不在堪萨斯了。我想打造一种将氛围视觉与实用功能相结合的东西——一个带有恐怖氛围的天气应用。于是诞生了 Eerie Weather App,这是一款 3D 天气可视化应用,漂浮的闹鬼小屋会根据全球各城市的实时天气做出反应。雨天会使天空变暗并出现雨滴,雷暴会释放锯齿状的闪电,每种天气类型都有其独特的粒子系统和环境音效。

设计

配色方案

  • 背景色: #1a1a2e(深蓝紫)—— 如暴风雨前的天空。
  • 强调色: #460809#f4320b(悬停时)—— 血红色的高亮。
  • 文字发光: 以飘渺的红色并配以脉动的文字阴影。
h3 {
  color: #ff6b6b;
  text-shadow: 0 0 10px rgba(255, 107, 107, 0.5);
}

排版

“October Crow” 字体营造出锯齿状、手绘的感觉。

@font-face {
  font-family: "October Crow";
  src: url("/fonts/October Crow.ttf") format("truetype");
}
body {
  font-family: "October Crow", Arial, sans-serif;
}

自定义按钮

按钮使用内联 SVG 背景进行样式化,看起来像风化的石板。

/* Example background image */
background-image: url('data:image/svg+xml,');

Three.js 设置

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);
scene.fog = new THREE.Fog(0x1a1a2e, 10, 50);

雾效增加了深度感,并营造出一种“有什么东西潜伏”的氛围。

鬼屋模型

loader.load("models/forest_house.glb", (gltf) => {
  house = gltf.scene;
  house.userData.centerY = -center.y - 2.5;
  scene.add(house);
});

// Animation loop
house.position.y = house.userData.centerY + Math.sin(time * 0.5) * 0.2;

房子轻轻上下摇摆,随天气变化而响应。

灯光

const ambientLight = new THREE.AmbientLight(0x9999cc, 0.4);      // Moonlit base
const directionalLight = new THREE.DirectionalLight(0xaaaadd, 0.3); // Soft moonlight
const rimLight = new THREE.DirectionalLight(0x6666aa, 0.2);          // Subtle backlight

闪电效果

function createLightningBolt(startX, startY, startZ, endX, endY, endZ) {
  const points = [];
  const segments = 8 + Math.floor(Math.random() * 6);

  for (let i = 0; i < segments; i++) {
    // generate jittered points between start and end
    const t = i / (segments - 1);
    const x = THREE.MathUtils.lerp(startX, endX, t) + (Math.random() - 0.5) * 0.5;
    const y = THREE.MathUtils.lerp(startY, endY, t) + (Math.random() - 0.5) * 0.5;
    const z = THREE.MathUtils.lerp(startZ, endZ, t) + (Math.random() - 0.5) * 0.5;
    points.push(new THREE.Vector3(x, y, z));
  }

  const geometry = new THREE.BufferGeometry().setFromPoints(points);
  const material = new THREE.LineBasicMaterial({ color: 0xffffff, linewidth: 2 });
  const line = new THREE.Line(geometry, material);
  scene.add(line);

  // Fade out after a short duration
  setTimeout(() => scene.remove(line), 200);
}

天气数据集成

const url = `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&current=temperature_2m,weather_code...`;
const data = await fetch(url).then(r => r.json());

将 Open‑Meteo 天气代码映射到恐怖效果:

let condition;
if ([95, 96, 99].includes(weatherCode)) condition = "Thunderstorm";
else if ([61, 63, 65].includes(weatherCode)) condition = "Rain";
else if ([71, 73, 75].includes(weatherCode)) condition = "Snow";

用户界面

  • 搜索栏 – 查找任意城市,观看天气实时变化。
  • 底部栏 – 快速访问按钮:
    • 🏠 首页 – 重置相机。
    • ℹ️ 关于 – 打开一个包含应用信息和致谢的模态框。
document.getElementById("home-btn").addEventListener("click", () => {
  camera.position.copy(initialCameraPosition);
  controls.target.copy(initialCameraTarget);
  controls.update();
});

面板可以最小化:

function togglePanel(panelId) {
  const panel = document.getElementById(panelId);
  panel.classList.toggle("minimized");
}

故障排除

问题解决方案
闪电导致“Computed radius is NaN”错误在创建几何体之前验证坐标:`if (isNaN(startX)
快速的天气变化触发 play()/pause() 冲突跟踪当前声音,仅暂停其他声音:Object.values(weatherSounds).forEach(s => { if (s !== sound) { s.pause(); s.currentTime = 0; } });
城市搜索覆盖手动天气选择使用在显示新 API 数据之前重置的 manualOverride 标志:manualOverride = false; displayWeatherInfo(data);

结论

Eerie Weather App 已上线。搜索你的城市,调高雷暴强度,让闪电照亮你的屏幕。无论你是想寻找 Three.js 灵感,还是仅仅想要最戏剧性的天气查询方式,这款应用都能满足你的需求。

🌩️ 启动 Eerie Weather App

Back to Blog

相关文章

阅读更多 »

棱镜分形

Forem Feed !Forem Logo https://media2.dev.to/dynamic/image/width=65,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.co...

Reatom:随你成长的状态管理

碎片化问题 现代前端开发有一个常见的模式: - 从简单的 useState hook 开始 - 需要共享状态?添加 Context - Context re‑...