// API ключ от WeatherAPI const API_KEY = '485eff906f7d473b913104046250710'; const citySelect = document.getElementById('city'); const getWeatherBtn = document.getElementById('get-weather'); const timelapseContainer = document.getElementById('timelapse-container'); const currentWeatherDiv = document.getElementById('current-weather'); const currentTempEl = document.getElementById('current-temp'); const currentDescEl = document.getElementById('current-desc'); const currentHumidityEl = document.getElementById('current-humidity'); const currentWindEl = document.getElementById('current-wind'); // Глобальная переменная для Swiper let weatherSwiper; // Глобальные переменные для графиков let temperatureChart = null; let precipitationChart = null; // Иконки погоды const weatherIcons = { 'Sunny': 'sun', 'Clear': 'sun', 'Partly cloudy': 'cloud-sun', 'Cloudy': 'cloud', 'Overcast': 'cloud', 'Mist': 'cloud-fog', 'Fog': 'cloud-fog', 'Light rain': 'cloud-rain', 'Moderate rain': 'cloud-rain', 'Heavy rain': 'cloud-rain', 'Light snow': 'cloud-snow', 'Moderate snow': 'cloud-snow', 'Heavy snow': 'cloud-snow', 'Thunderstorm': 'cloud-lightning', 'Patchy rain possible': 'cloud-drizzle', 'Patchy snow possible': 'cloud-snow', 'Patchy sleet possible': 'cloud-snow', 'Patchy freezing drizzle possible': 'cloud-drizzle', 'Blowing snow': 'wind', 'Blizzard': 'wind', 'Freezing fog': 'cloud-fog', 'Patchy light drizzle': 'cloud-drizzle', 'Light drizzle': 'cloud-drizzle', 'Freezing drizzle': 'cloud-drizzle', 'Heavy freezing drizzle': 'cloud-drizzle', 'Patchy light rain': 'cloud-rain', 'Light rain shower': 'cloud-rain', 'Moderate or heavy rain shower': 'cloud-rain', 'Torrential rain shower': 'cloud-rain', 'Patchy light snow': 'cloud-snow', 'Light snow showers': 'cloud-snow', 'Moderate or heavy snow showers': 'cloud-snow', 'Patchy light rain with thunder': 'cloud-lightning', 'Moderate or heavy rain with thunder': 'cloud-lightning', 'Patchy light snow with thunder': 'cloud-lightning', 'Moderate or heavy snow with thunder': 'cloud-lightning' }; // Функция для отображения текущей даты function displayCurrentDate() { const currentDateEl = document.getElementById('current-date'); const now = new Date(); const daysOfWeek = ['воскресенье', 'понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота']; const months = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря']; const dayOfWeek = daysOfWeek[now.getDay()]; const day = now.getDate(); const month = months[now.getMonth()]; currentDateEl.textContent = `${dayOfWeek}, ${day} ${month}`; } // Функция для обновления заголовка страницы function updatePageTitle() { const selectedCity = citySelect.options[citySelect.selectedIndex].text; document.title = `Погода в ${selectedCity}`; } // Автоматическая загрузка погоды при загрузке страницы document.addEventListener('DOMContentLoaded', () => { // Отображаем текущую дату displayCurrentDate(); // Загружаем сохраненный город из localStorage const savedCity = localStorage.getItem('selectedCity'); if (savedCity) { citySelect.value = savedCity; } // Обновляем заголовок страницы updatePageTitle(); // Ждем загрузки Swiper if (typeof Swiper !== 'undefined') { getWeatherBtn.click(); } else { // Если Swiper еще не загрузился, ждем его const checkSwiper = setInterval(() => { if (typeof Swiper !== 'undefined') { clearInterval(checkSwiper); getWeatherBtn.click(); } }, 100); } }); getWeatherBtn.addEventListener('click', async () => { const city = citySelect.value; // Сохраняем выбранный город в localStorage localStorage.setItem('selectedCity', city); const url = `https://api.weatherapi.com/v1/forecast.json?key=${API_KEY}&q=${city}&days=7&hourly=true&lang=ru`; try { const response = await fetch(url); if (!response.ok) { throw new Error('Ошибка сети'); } const data = await response.json(); // Отображаем текущую погоду displayCurrentWeather(data); // Пересоздаем иконки после обновления DOM setTimeout(() => { lucide.createIcons(); }, 100); // Таймлапс на сегодня displayTimelapse(data.forecast.forecastday[0].hour, data.location.localtime); // Погода на неделю displayWeeklyWeather(data.forecast.forecastday); // Графики температуры и осадков createTemperatureChart(data.forecast.forecastday[0].hour); createPrecipitationChart(data.forecast.forecastday[0].hour); // Обновляем заголовок страницы с выбранным городом updatePageTitle(); } catch (error) { console.error('Ошибка загрузки данных:', error); alert('Не удалось загрузить данные о погоде. Проверьте API ключ.'); } }); function displayCurrentWeather(data) { const current = data.current; currentTempEl.textContent = `${Math.round(current.temp_c)}°C`; currentDescEl.textContent = current.condition.text; currentHumidityEl.textContent = `${current.humidity}%`; currentWindEl.textContent = `${current.wind_kph} км/ч`; // Показываем блок текущей погоды currentWeatherDiv.classList.remove('hidden'); currentWeatherDiv.classList.add('animate-fade-in'); } function displayTimelapse(hourlyData, localtime) { timelapseContainer.innerHTML = ''; const currentHour = new Date(localtime).getHours(); let currentSlideIndex = -1; hourlyData.forEach((item, index) => { const slide = document.createElement('div'); const hour = new Date(item.time).getHours(); const isCurrent = hour === currentHour; if (isCurrent) currentSlideIndex = index; slide.className = `swiper-slide timelapse-item bg-gradient-to-br from-blue-50 to-indigo-50 p-3 rounded-xl shadow-md border ${isCurrent ? 'border-blue-500 bg-gradient-to-br from-blue-100 to-indigo-100 ring-2 ring-blue-300' : 'border-gray-100'}`; slide.style.width = '140px'; const iconName = weatherIcons[item.condition.text] || 'cloud'; slide.innerHTML = `

${hour}:00 ${isCurrent ? '(сейчас)' : ''}

${Math.round(item.temp_c)}°C

${item.condition.text}

💧 ${item.precip_mm} мм

☔ ${item.chance_of_rain}%

`; timelapseContainer.appendChild(slide); }); // Пересоздаем иконки setTimeout(() => { lucide.createIcons(); }, 100); // Инициализируем Swiper если он еще не создан setTimeout(() => { if (!weatherSwiper) { weatherSwiper = new Swiper('.swiper', { slidesPerView: window.innerWidth < 768 ? 3 : 6, spaceBetween: 12, grabCursor: true, freeMode: true, mousewheel: { enabled: true, sensitivity: 1, }, slidesOffsetBefore: 0, slidesOffsetAfter: 0, centeredSlides: false, watchSlidesProgress: true }); } else { weatherSwiper.update(); weatherSwiper.updateSlides(); } // Фокус на текущем времени города if (currentSlideIndex !== -1) { setTimeout(() => { weatherSwiper.slideTo(currentSlideIndex, 800); }, 200); } }, 50); } function displayWeeklyWeather(forecastData) { const weeklyContainer = document.getElementById('weekly-container'); weeklyContainer.innerHTML = ''; const daysOfWeek = ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб']; forecastData.forEach((day, index) => { const date = new Date(day.date); const dayOfWeek = daysOfWeek[date.getDay()]; const dayNumber = date.getDate(); const month = date.toLocaleDateString('ru-RU', { month: 'short' }); const iconName = weatherIcons[day.day.condition.text] || 'cloud'; const isToday = index === 0; const card = document.createElement('div'); card.className = `text-center p-4 rounded-xl border transition-all duration-200 hover:shadow-lg ${ isToday ? 'bg-blue-50 border-blue-300 ring-2 ring-blue-200' : 'bg-gray-50 border-gray-200 hover:border-blue-300' }`; card.innerHTML = `

${isToday ? 'Сегодня' : dayOfWeek}

${dayNumber} ${month}

${Math.round(day.day.maxtemp_c)}°

${Math.round(day.day.mintemp_c)}°

${day.day.condition.text}

`; weeklyContainer.appendChild(card); }); // Пересоздаем иконки для недельного прогноза setTimeout(() => { lucide.createIcons(); }, 100); } // Функция создания графика температуры function createTemperatureChart(hourlyData) { const ctx = document.getElementById('temperatureChart').getContext('2d'); // Подготавливаем данные const labels = hourlyData.map(item => { const hour = new Date(item.time).getHours(); return `${hour}:00`; }); const temperatures = hourlyData.map(item => Math.round(item.temp_c)); // Уничтожаем предыдущий график если он существует if (temperatureChart) { temperatureChart.destroy(); } // Создаем новый график temperatureChart = new Chart(ctx, { type: 'line', data: { labels: labels, datasets: [{ label: 'Температура (°C)', data: temperatures, borderColor: '#3b82f6', backgroundColor: 'rgba(59, 130, 246, 0.1)', borderWidth: 3, fill: true, tension: 0.4, pointBackgroundColor: '#3b82f6', pointBorderColor: '#ffffff', pointBorderWidth: 2, pointRadius: 4, pointHoverRadius: 6, pointHoverBackgroundColor: '#2563eb', pointHoverBorderColor: '#ffffff', pointHoverBorderWidth: 2 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { backgroundColor: 'rgba(0, 0, 0, 0.8)', titleColor: '#ffffff', bodyColor: '#ffffff', cornerRadius: 8, displayColors: false, callbacks: { title: function(context) { return `Время: ${context[0].label}`; }, label: function(context) { return `Температура: ${context.parsed.y}°C`; } } } }, scales: { x: { grid: { display: false }, ticks: { color: '#6b7280', font: { size: 12 } } }, y: { grid: { color: 'rgba(0, 0, 0, 0.1)', lineWidth: 1 }, ticks: { color: '#6b7280', font: { size: 12 }, callback: function(value) { return value + '°C'; } } } }, animation: { duration: 2000, easing: 'easeInOutQuart' }, interaction: { intersect: false, mode: 'index' } } }); } // Функция создания графика осадков function createPrecipitationChart(hourlyData) { const ctx = document.getElementById('precipitationChart').getContext('2d'); // Подготавливаем данные const labels = hourlyData.map(item => { const hour = new Date(item.time).getHours(); return `${hour}:00`; }); const precipitations = hourlyData.map(item => item.precip_mm); const chances = hourlyData.map(item => item.chance_of_rain); // Уничтожаем предыдущий график если он существует if (precipitationChart) { precipitationChart.destroy(); } // Создаем новый график precipitationChart = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Осадки (мм)', data: precipitations, backgroundColor: 'rgba(59, 130, 246, 0.6)', borderColor: '#3b82f6', borderWidth: 1, borderRadius: 4, borderSkipped: false, barThickness: 'flex', maxBarThickness: 30 }, { label: 'Вероятность (%)', data: chances, type: 'line', borderColor: '#ef4444', backgroundColor: 'rgba(239, 68, 68, 0.1)', borderWidth: 2, fill: false, tension: 0.4, pointBackgroundColor: '#ef4444', pointBorderColor: '#ffffff', pointBorderWidth: 2, pointRadius: 3, pointHoverRadius: 5, pointHoverBackgroundColor: '#dc2626', pointHoverBorderColor: '#ffffff', pointHoverBorderWidth: 2, yAxisID: 'y1' }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { backgroundColor: 'rgba(0, 0, 0, 0.8)', titleColor: '#ffffff', bodyColor: '#ffffff', cornerRadius: 8, displayColors: true, callbacks: { title: function(context) { return `Время: ${context[0].label}`; }, label: function(context) { if (context.datasetIndex === 0) { return `Осадки: ${context.parsed.y} мм`; } else { return `Вероятность: ${context.parsed.y}%`; } } } } }, scales: { x: { grid: { display: false }, ticks: { color: '#6b7280', font: { size: 12 } } }, y: { type: 'linear', display: true, position: 'left', grid: { color: 'rgba(0, 0, 0, 0.1)', lineWidth: 1 }, ticks: { color: '#6b7280', font: { size: 12 }, callback: function(value) { return value + ' мм'; } }, title: { display: true, text: 'Осадки (мм)', color: '#3b82f6', font: { size: 14, weight: 'bold' } } }, y1: { type: 'linear', display: true, position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#ef4444', font: { size: 12 }, callback: function(value) { return value + '%'; } }, title: { display: true, text: 'Вероятность (%)', color: '#ef4444', font: { size: 14, weight: 'bold' } } } }, animation: { duration: 2000, easing: 'easeInOutQuart', delay: function(context) { return context.dataIndex * 100; } }, interaction: { intersect: false, mode: 'index' } } }); }