Skip to content

Commit 4d41a23

Browse files
authored
Add files via upload
1 parent fe3b279 commit 4d41a23

4 files changed

Lines changed: 587 additions & 0 deletions

File tree

WeatherApp/animation.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// animation.js
2+
// Simple fade-in animation for weather results and forecast
3+
4+
document.addEventListener('DOMContentLoaded', () => {
5+
const weatherResult = document.getElementById('weatherResult');
6+
const forecastDiv = document.getElementById('forecast');
7+
8+
function fadeIn(element) {
9+
element.style.opacity = 0;
10+
element.style.display = 'block';
11+
let last = +new Date();
12+
const tick = function() {
13+
element.style.opacity = +element.style.opacity + (new Date() - last) / 300;
14+
last = +new Date();
15+
if (+element.style.opacity < 1) {
16+
requestAnimationFrame(tick);
17+
}
18+
};
19+
tick();
20+
}
21+
22+
// Observe changes to weatherResult and forecastDiv
23+
const observer = new MutationObserver(mutations => {
24+
mutations.forEach(mutation => {
25+
if (mutation.addedNodes.length > 0) {
26+
fadeIn(mutation.target);
27+
}
28+
});
29+
});
30+
31+
observer.observe(weatherResult, { childList: true });
32+
observer.observe(forecastDiv, { childList: true });
33+
});

WeatherApp/index.html

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Weather Forecast App</title>
7+
<link rel="stylesheet" href="style.css">
8+
</head>
9+
<body>
10+
<div class="container">
11+
<div class="floating-shape shape1"></div>
12+
<div class="floating-shape shape2"></div>
13+
<div class="floating-shape shape3"></div>
14+
<h1>Weather Forecast</h1>
15+
<form id="weatherForm">
16+
<input type="text" id="cityInput" placeholder="Enter city name" required />
17+
<button type="submit">Get Weather</button>
18+
</form>
19+
<div id="weatherResult"></div>
20+
<div id="forecast"></div>
21+
</div>
22+
<script src="script.js"></script>
23+
<script src="animation.js"></script>
24+
</body>
25+
</html>

WeatherApp/script.js

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
const API_KEY = '51016e3e4f8041d38eb193817251604';
2+
const BASE_URL = 'https://api.weatherapi.com/v1';
3+
const weatherForm = document.getElementById('weatherForm');
4+
const cityInput = document.getElementById('cityInput');
5+
const weatherResult = document.getElementById('weatherResult');
6+
const forecastDiv = document.getElementById('forecast');
7+
8+
// Typewriter effect for input placeholder with multiple words
9+
const placeholderWords = [
10+
"Enter city name",
11+
"Search weather",
12+
"Type any city",
13+
"Check forecast",
14+
"Find temperature"
15+
];
16+
let wordIndex = 0;
17+
let typeIndex = 0;
18+
let typingForward = true;
19+
20+
function typePlaceholder() {
21+
const currentWord = placeholderWords[wordIndex];
22+
if (typingForward) {
23+
typeIndex++;
24+
if (typeIndex > currentWord.length) {
25+
typingForward = false;
26+
setTimeout(typePlaceholder, 1200); // pause at end
27+
return;
28+
}
29+
} else {
30+
typeIndex--;
31+
if (typeIndex < 0) {
32+
typingForward = true;
33+
wordIndex = (wordIndex + 1) % placeholderWords.length;
34+
setTimeout(typePlaceholder, 600); // pause at start
35+
return;
36+
}
37+
}
38+
cityInput.setAttribute('placeholder', currentWord.slice(0, typeIndex));
39+
setTimeout(typePlaceholder, 90);
40+
}
41+
typePlaceholder();
42+
43+
weatherForm.addEventListener('submit', async (e) => {
44+
e.preventDefault();
45+
const city = cityInput.value.trim();
46+
if (!city) return;
47+
weatherResult.innerHTML = 'Loading...';
48+
forecastDiv.innerHTML = '';
49+
try {
50+
// Fetch current weather
51+
const weatherRes = await fetch(`${BASE_URL}/current.json?key=${API_KEY}&q=${encodeURIComponent(city)}`);
52+
if (!weatherRes.ok) throw new Error('City not found');
53+
const weatherData = await weatherRes.json();
54+
displayCurrentWeather(weatherData);
55+
// Fetch 5-day forecast (WeatherAPI provides 3-day forecast on free tier)
56+
const forecastRes = await fetch(`${BASE_URL}/forecast.json?key=${API_KEY}&q=${encodeURIComponent(city)}&days=5`);
57+
if (!forecastRes.ok) throw new Error('Forecast not found');
58+
const forecastData = await forecastRes.json();
59+
displayForecast(forecastData);
60+
} catch (err) {
61+
weatherResult.innerHTML = `<span style='color:red;'>${err.message}</span>`;
62+
}
63+
});
64+
65+
function getWeatherAnimation(condition, isNight) {
66+
const cond = condition.toLowerCase();
67+
if (isNight) {
68+
// Animated moon icon
69+
return `<div class="weather-anim moon">
70+
<div class="moon-core"></div>
71+
<div class="moon-crater crater1"></div>
72+
<div class="moon-crater crater2"></div>
73+
</div>`;
74+
} else if (cond.includes('sun') || cond.includes('clear')) {
75+
return `<div class="weather-anim sun">
76+
<div class="sun-core"></div>
77+
<div class="sun-ray ray1"></div>
78+
<div class="sun-ray ray2"></div>
79+
<div class="sun-ray ray3"></div>
80+
<div class="sun-ray ray4"></div>
81+
<div class="sun-ray ray5"></div>
82+
<div class="sun-ray ray6"></div>
83+
<div class="sun-ray ray7"></div>
84+
<div class="sun-ray ray8"></div>
85+
</div>`;
86+
} else if (cond.includes('cloud')) {
87+
return `<div class="weather-anim cloud"><div class="cloud-main"></div><div class="cloud-shadow"></div></div>`;
88+
} else if (cond.includes('rain')) {
89+
return `<div class="weather-anim rain"><div class="cloud-main"></div><div class="rain-drop drop1"></div><div class="rain-drop drop2"></div><div class="rain-drop drop3"></div></div>`;
90+
} else {
91+
return '';
92+
}
93+
}
94+
95+
function displayCurrentWeather(data) {
96+
// WeatherAPI returns is_day: 1 (day), 0 (night)
97+
const isNight = data.current.is_day === 0;
98+
const anim = getWeatherAnimation(data.current.condition.text, isNight);
99+
// Get location-based time using WeatherAPI's localtime
100+
const localTime = data.location.localtime; // format: '2025-04-17 21:30'
101+
const timeString = localTime ? localTime.split(' ')[1].slice(0,5) : '';
102+
weatherResult.innerHTML = `
103+
<h2>${data.location.name}, ${data.location.country}</h2>
104+
<p><strong>${data.current.condition.text}</strong></p>
105+
${anim}
106+
<img src="${data.current.condition.icon}" alt="icon" />
107+
<p>Temperature: ${data.current.temp_c}°C</p>
108+
<p>Humidity: ${data.current.humidity}%</p>
109+
<p>Wind: ${data.current.wind_kph} kph</p>
110+
<p style="margin-top:10px;color:#2266aa;font-weight:600;">Current Time in ${data.location.name}: ${timeString}</p>
111+
`;
112+
}
113+
114+
function displayForecast(data) {
115+
if (!data.forecast || !data.forecast.forecastday) {
116+
forecastDiv.innerHTML = '<span style="color:red;">No forecast data available.</span>';
117+
return;
118+
}
119+
forecastDiv.innerHTML = '<h3>Forecast</h3>';
120+
data.forecast.forecastday.forEach(day => {
121+
forecastDiv.innerHTML += `
122+
<div class="forecast-day">
123+
<strong>${day.date}</strong><br />
124+
<img src="${day.day.condition.icon}" alt="icon" />
125+
${day.day.condition.text}<br />
126+
Temp: ${day.day.avgtemp_c}°C, Humidity: ${day.day.avghumidity}%
127+
</div>
128+
`;
129+
});
130+
}

0 commit comments

Comments
 (0)