Skip to content

Commit c36a295

Browse files
authored
Add files via upload
1 parent d459a40 commit c36a295

4 files changed

Lines changed: 627 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: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 class="fancy-heading">
15+
<span class="heading-anim">🌦️</span>
16+
<span class="heading-text">Weather Forecast</span>
17+
<span class="heading-anim"></span>
18+
</h1>
19+
<form id="weatherForm">
20+
<input type="text" id="cityInput" placeholder="Enter city name" required />
21+
<button type="submit">Get Weather</button>
22+
</form>
23+
<div id="weatherResult"></div>
24+
<div id="forecast"></div>
25+
</div>
26+
<script src="script.js"></script>
27+
<script src="animation.js"></script>
28+
</body>
29+
</html>

WeatherApp/script.js

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
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+
// Heading text typewriter animation
44+
const headingText = document.querySelector('.heading-text');
45+
const headingPhrases = [
46+
'Weather Forecast',
47+
'Live Weather Updates',
48+
'Your City Weather',
49+
'Forecast & Temperature',
50+
'Check Weather Instantly'
51+
];
52+
let headingWordIndex = 0;
53+
let headingCharIndex = 0;
54+
let headingTypingForward = true;
55+
56+
function typeHeading() {
57+
const currentPhrase = headingPhrases[headingWordIndex];
58+
if (headingTypingForward) {
59+
headingCharIndex++;
60+
if (headingCharIndex > currentPhrase.length) {
61+
headingTypingForward = false;
62+
setTimeout(typeHeading, 1200);
63+
return;
64+
}
65+
} else {
66+
headingCharIndex--;
67+
if (headingCharIndex < 0) {
68+
headingTypingForward = true;
69+
headingWordIndex = (headingWordIndex + 1) % headingPhrases.length;
70+
setTimeout(typeHeading, 600);
71+
return;
72+
}
73+
}
74+
headingText.textContent = currentPhrase.slice(0, headingCharIndex);
75+
setTimeout(typeHeading, 90);
76+
}
77+
typeHeading();
78+
79+
weatherForm.addEventListener('submit', async (e) => {
80+
e.preventDefault();
81+
const city = cityInput.value.trim();
82+
if (!city) return;
83+
weatherResult.innerHTML = 'Loading...';
84+
forecastDiv.innerHTML = '';
85+
try {
86+
// Fetch current weather
87+
const weatherRes = await fetch(`${BASE_URL}/current.json?key=${API_KEY}&q=${encodeURIComponent(city)}`);
88+
if (!weatherRes.ok) throw new Error('City not found');
89+
const weatherData = await weatherRes.json();
90+
displayCurrentWeather(weatherData);
91+
// Fetch 5-day forecast (WeatherAPI provides 3-day forecast on free tier)
92+
const forecastRes = await fetch(`${BASE_URL}/forecast.json?key=${API_KEY}&q=${encodeURIComponent(city)}&days=5`);
93+
if (!forecastRes.ok) throw new Error('Forecast not found');
94+
const forecastData = await forecastRes.json();
95+
displayForecast(forecastData);
96+
} catch (err) {
97+
weatherResult.innerHTML = `<span style='color:red;'>${err.message}</span>`;
98+
}
99+
});
100+
101+
function getWeatherAnimation(condition, isNight) {
102+
const cond = condition.toLowerCase();
103+
if (isNight) {
104+
// Animated moon icon
105+
return `<div class="weather-anim moon">
106+
<div class="moon-core"></div>
107+
<div class="moon-crater crater1"></div>
108+
<div class="moon-crater crater2"></div>
109+
</div>`;
110+
} else if (cond.includes('sun') || cond.includes('clear')) {
111+
return `<div class="weather-anim sun">
112+
<div class="sun-core"></div>
113+
<div class="sun-ray ray1"></div>
114+
<div class="sun-ray ray2"></div>
115+
<div class="sun-ray ray3"></div>
116+
<div class="sun-ray ray4"></div>
117+
<div class="sun-ray ray5"></div>
118+
<div class="sun-ray ray6"></div>
119+
<div class="sun-ray ray7"></div>
120+
<div class="sun-ray ray8"></div>
121+
</div>`;
122+
} else if (cond.includes('cloud')) {
123+
return `<div class="weather-anim cloud"><div class="cloud-main"></div><div class="cloud-shadow"></div></div>`;
124+
} else if (cond.includes('rain')) {
125+
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>`;
126+
} else {
127+
return '';
128+
}
129+
}
130+
131+
function displayCurrentWeather(data) {
132+
// WeatherAPI returns is_day: 1 (day), 0 (night)
133+
const isNight = data.current.is_day === 0;
134+
const anim = getWeatherAnimation(data.current.condition.text, isNight);
135+
// Get location-based time using WeatherAPI's localtime
136+
const localTime = data.location.localtime; // format: '2025-04-17 21:30'
137+
const timeString = localTime ? localTime.split(' ')[1].slice(0,5) : '';
138+
weatherResult.innerHTML = `
139+
<h2>${data.location.name}, ${data.location.country}</h2>
140+
<p><strong>${data.current.condition.text}</strong></p>
141+
${anim}
142+
<img src="${data.current.condition.icon}" alt="icon" />
143+
<p>Temperature: ${data.current.temp_c}°C</p>
144+
<p>Humidity: ${data.current.humidity}%</p>
145+
<p>Wind: ${data.current.wind_kph} kph</p>
146+
<p style="margin-top:10px;color:#2266aa;font-weight:600;">Current Time in ${data.location.name}: ${timeString}</p>
147+
`;
148+
}
149+
150+
function displayForecast(data) {
151+
if (!data.forecast || !data.forecast.forecastday) {
152+
forecastDiv.innerHTML = '<span style="color:red;">No forecast data available.</span>';
153+
return;
154+
}
155+
forecastDiv.innerHTML = '<h3>Forecast</h3>';
156+
data.forecast.forecastday.forEach(day => {
157+
forecastDiv.innerHTML += `
158+
<div class="forecast-day">
159+
<strong>${day.date}</strong><br />
160+
<img src="${day.day.condition.icon}" alt="icon" />
161+
${day.day.condition.text}<br />
162+
Temp: ${day.day.avgtemp_c}°C, Humidity: ${day.day.avghumidity}%
163+
</div>
164+
`;
165+
});
166+
}

0 commit comments

Comments
 (0)