// API ключ от WeatherAPI const API_KEY = "API_KEY_HERE"; // Глобальные переменные для темы const themeButtons = { light: document.getElementById("theme-light"), auto: document.getElementById("theme-auto"), dark: document.getElementById("theme-dark"), }; 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", // Ночные условия "Clear night": "moon", "Partly cloudy night": "cloud-moon", "Cloudy night": "cloud", "Sunny night": "moon", "Light rain night": "cloud-rain", "Moderate rain night": "cloud-rain", "Heavy rain night": "cloud-rain", "Light snow night": "cloud-snow", "Moderate snow night": "cloud-snow", "Heavy snow night": "cloud-snow", "Thunderstorm night": "cloud-lightning", "Patchy rain possible night": "cloud-drizzle", "Patchy snow possible night": "cloud-snow", "Mist night": "cloud-fog", "Fog night": "cloud-fog", // Дополнительные типы погоды "Light sleet": "cloud-snow", "Moderate or heavy sleet": "cloud-snow", "Ice pellets": "cloud-hail", "Light showers of ice pellets": "cloud-hail", "Moderate or heavy showers of ice pellets": "cloud-hail", Hail: "cloud-hail", "Light hail": "cloud-hail", "Moderate or heavy hail": "cloud-hail", Tornado: "tornado", Hurricane: "hurricane", Squall: "wind", Dust: "wind", Sand: "wind", "Volcanic ash": "wind", "Thundery outbreaks possible": "cloud-lightning", "Thundery outbreaks in nearby": "cloud-lightning", }; // Маппинг русских погодных условий на английские ключи для иконок const russianToEnglishWeather = { Солнечно: "Sunny", Ясно: "Clear", "Частично облачно": "Partly cloudy", Облачно: "Cloudy", Пасмурно: "Overcast", Туман: "Mist", "Слабый туман": "Mist", "Легкий дождь": "Light rain", "Умеренный дождь": "Moderate rain", "Сильный дождь": "Heavy rain", "Легкий снег": "Light snow", "Умеренный снег": "Moderate snow", "Сильный снег": "Heavy snow", Гроза: "Thunderstorm", "Возможен небольшой дождь": "Patchy rain possible", "Возможен небольшой снег": "Patchy snow possible", "Местами дождь": "Patchy rain possible", "Местами снег": "Patchy snow possible", Морось: "Patchy light drizzle", "Легкая морось": "Light drizzle", "Замерзающая морось": "Freezing drizzle", Ливень: "Light rain shower", "Умеренный или сильный ливень": "Moderate or heavy rain shower", "Снег с дождем": "Light sleet", "Умеренный или сильный снег с дождем": "Moderate or heavy sleet", Град: "Hail", "Легкий град": "Light hail", "Умеренный или сильный град": "Moderate or heavy hail", Торнадо: "Tornado", Ураган: "Hurricane", Шквал: "Squall", Пыль: "Dust", Песок: "Sand", "Вулканический пепел": "Volcanic ash", "Возможны грозы": "Thundery outbreaks possible", "Грозы в округе": "Thundery outbreaks in nearby", // Ночные условия "Ясно ночь": "Clear night", "Частично облачно ночь": "Partly cloudy night", "Облачно ночь": "Cloudy night", "Солнечно ночь": "Sunny night", "Легкий дождь ночь": "Light rain night", "Умеренный дождь ночь": "Moderate rain night", "Сильный дождь ночь": "Heavy rain night", "Легкий снег ночь": "Light snow night", "Умеренный снег ночь": "Moderate snow night", "Сильный снег ночь": "Heavy snow night", "Гроза ночь": "Thunderstorm night", "Возможен небольшой дождь ночь": "Patchy rain possible night", "Возможен небольшой снег ночь": "Patchy snow possible night", "Туман ночь": "Mist night", "Слабый туман ночь": "Fog night", }; // Красивые русские названия для отображения const beautifulRussianNames = { Sunny: "Солнечно", Clear: "Ясно", "Partly cloudy": "Переменная облачность", Cloudy: "Облачно", Overcast: "Пасмурно", Mist: "Туман", Fog: "Густой туман", "Light rain": "Небольшой дождь", "Moderate rain": "Дождь", "Heavy rain": "Сильный дождь", "Light snow": "Небольшой снег", "Moderate snow": "Снег", "Heavy snow": "Сильный снег", Thunderstorm: "Гроза", "Patchy rain possible": "Местами дождь", "Patchy snow possible": "Местами снег", "Patchy sleet possible": "Местами мокрый снег", "Patchy freezing drizzle possible": "Местами изморось", "Blowing snow": "Поземок", Blizzard: "Метель", "Freezing fog": "Ледяной туман", "Patchy light drizzle": "Морось", "Light drizzle": "Легкая морось", "Freezing drizzle": "Замерзающая морось", "Heavy freezing drizzle": "Сильная изморось", "Patchy light rain": "Кратковременный дождь", "Light rain shower": "Ливень", "Moderate or heavy rain shower": "Сильный ливень", "Torrential rain shower": "Ливневой дождь", "Patchy light snow": "Кратковременный снег", "Light snow showers": "Снеговые заряды", "Moderate or heavy snow showers": "Сильные снегопады", "Patchy light rain with thunder": "Гроза с дождем", "Moderate or heavy rain with thunder": "Гроза с сильным дождем", "Patchy light snow with thunder": "Гроза со снегом", "Moderate or heavy snow with thunder": "Гроза с сильным снегом", "Clear night": "Ясная ночь", "Partly cloudy night": "Переменная облачность ночью", "Cloudy night": "Облачная ночь", "Sunny night": "Солнечная ночь", "Light rain night": "Дождь ночью", "Moderate rain night": "Дождь ночью", "Heavy rain night": "Сильный дождь ночью", "Light snow night": "Снег ночью", "Moderate snow night": "Снег ночью", "Heavy snow night": "Сильный снег ночью", "Thunderstorm night": "Гроза ночью", "Patchy rain possible night": "Местами дождь ночью", "Patchy snow possible night": "Местами снег ночью", "Mist night": "Туман ночью", "Fog night": "Густой туман ночью", "Light sleet": "Мокрый снег", "Moderate or heavy sleet": "Сильный мокрый снег", "Ice pellets": "Ледяная крупа", "Light showers of ice pellets": "Ледяная крупа", "Moderate or heavy showers of ice pellets": "Сильная ледяная крупа", Hail: "Град", "Light hail": "Мелкий град", "Moderate or heavy hail": "Крупный град", Tornado: "Торнадо", Hurricane: "Ураган", Squall: "Шквал", Dust: "Пыль", Sand: "Песок", "Volcanic ash": "Вулканический пепел", "Thundery outbreaks possible": "Возможны грозы", "Thundery outbreaks in nearby": "Грозы в округе", }; // Функция для получения иконки погоды с умным fallback function getWeatherIcon(conditionText) { // Сначала проверяем русские условия const englishCondition = russianToEnglishWeather[conditionText] || conditionText; // Прямой маппинг if (weatherIcons[englishCondition]) { return weatherIcons[englishCondition]; } // Умный fallback на основе ключевых слов const lowerCondition = englishCondition.toLowerCase(); if ( lowerCondition.includes("rain") || lowerCondition.includes("drizzle") || lowerCondition.includes("shower") ) { return "cloud-rain"; } if ( lowerCondition.includes("snow") || lowerCondition.includes("sleet") || lowerCondition.includes("hail") || lowerCondition.includes("ice") ) { return "cloud-snow"; } if (lowerCondition.includes("thunder") || lowerCondition.includes("storm")) { return "cloud-lightning"; } if (lowerCondition.includes("fog") || lowerCondition.includes("mist")) { return "cloud-fog"; } if (lowerCondition.includes("cloud") || lowerCondition.includes("overcast")) { return "cloud"; } if (lowerCondition.includes("sun") || lowerCondition.includes("clear")) { return "sun"; } if ( lowerCondition.includes("wind") || lowerCondition.includes("blowing") || lowerCondition.includes("blizzard") ) { return "wind"; } // Общий fallback return "cloud"; } // Функция для получения красивого русского названия погоды function getBeautifulRussianName(conditionText) { return beautifulRussianNames[conditionText] || conditionText; } // Функция для получения цвета иконки погоды function getWeatherIconColor(iconName) { const isDark = document.documentElement.classList.contains("dark") || document.body.classList.contains("dark"); const colorMap = { sun: isDark ? "sun-dark" : "sun", moon: isDark ? "sun-dark" : "sun", "cloud-sun": isDark ? "sun-dark" : "sun", "cloud-moon": isDark ? "sun-dark" : "sun", cloud: isDark ? "cloud-dark" : "cloud", "cloud-rain": isDark ? "rain-dark" : "rain", "cloud-drizzle": isDark ? "drizzle-dark" : "drizzle", "cloud-snow": isDark ? "snow-dark" : "snow", "cloud-lightning": isDark ? "thunder-dark" : "thunder", "cloud-fog": isDark ? "fog-dark" : "fog", wind: isDark ? "wind-dark" : "wind", "cloud-hail": isDark ? "hail-dark" : "hail", tornado: isDark ? "thunder-dark" : "thunder", hurricane: isDark ? "thunder-dark" : "thunder", }; return colorMap[iconName] || (isDark ? "cloud-dark" : "cloud"); } // Функция для отображения текущей даты 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(); // Инициализируем тему initializeTheme(); watchSystemTheme(); // Сохраняем данные для графиков window.currentHourlyData = null; // Ждем загрузки 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`; console.log("Запрос к API:", url); console.log("Выбранный город:", city); try { const response = await fetch(url); console.log("Статус ответа:", response.status, response.statusText); if (!response.ok) { throw new Error(`Ошибка сети: ${response.status} ${response.statusText}`); } const data = await response.json(); console.log("Данные получены успешно"); // Отображаем текущую погоду displayCurrentWeather(data); // Пересоздаем иконки после обновления DOM setTimeout(() => { lucide.createIcons(); }, 100); // Сохраняем данные для графиков window.currentHourlyData = data.forecast.forecastday[0].hour; // Таймлапс на сегодня 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); console.error("Детали ошибки:", error.message); if (error.response) { console.error("Статус ответа:", error.response.status); console.error("Текст ответа:", error.response.statusText); } alert("Не удалось загрузить данные о погоде. Проверьте API ключ."); } }); function displayCurrentWeather(data) { const current = data.current; const iconName = getWeatherIcon(current.condition.text); const iconColorClass = getWeatherIconColor(iconName); const beautifulName = getBeautifulRussianName(current.condition.text); currentTempEl.textContent = `${Math.round(current.temp_c)}°C`; currentDescEl.innerHTML = `${beautifulName}`; currentHumidityEl.textContent = `${current.humidity}%`; currentWindEl.textContent = `${current.wind_kph} км/ч`; // Показываем блок текущей погоды currentWeatherDiv.classList.remove("hidden"); currentWeatherDiv.classList.add("animate-fade-in"); // Пересоздаем иконки setTimeout(() => { lucide.createIcons(); }, 100); } 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 dark:from-gray-700 dark:to-gray-800 p-3 rounded-xl shadow-md border ${ isCurrent ? "border-blue-500 bg-gradient-to-br from-blue-100 to-indigo-100 dark:from-blue-900/30 dark:to-indigo-900/30 ring-2 ring-blue-300 dark:ring-blue-600" : "border-gray-100 dark:border-gray-600" }`; slide.style.width = "140px"; const iconName = getWeatherIcon(item.condition.text); const iconColorClass = getWeatherIconColor(iconName); // Отладочная информация console.log( `Timelapse: ${hour}:00 - Условие: "${item.condition.text}" - Иконка: "${iconName}" - Цвет: "${iconColorClass}"` ); slide.innerHTML = `

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

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

${getBeautifulRussianName(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 = getWeatherIcon(day.day.condition.text); const iconColorClass = getWeatherIconColor(iconName); const isToday = index === 0; // Отладочная информация console.log( `Weekly: ${isToday ? "Сегодня" : dayOfWeek} - Условие: "${ day.day.condition.text }" - Иконка: "${iconName}" - Цвет: "${iconColorClass}"` ); 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 dark:bg-blue-900/20 border-blue-300 dark:border-blue-700 ring-2 ring-blue-200 dark:ring-blue-600" : "bg-gray-50 dark:bg-gray-700 border-gray-200 dark:border-gray-600 hover:border-blue-300 dark:hover:border-blue-600" }`; card.innerHTML = `

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

${dayNumber} ${month}

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

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

${getBeautifulRussianName(day.day.condition.text)}

`; weeklyContainer.appendChild(card); }); // Пересоздаем иконки для недельного прогноза setTimeout(() => { lucide.createIcons(); }, 100); } // Функция создания графика температуры function createTemperatureChart(hourlyData) { const ctx = document.getElementById("temperatureChart").getContext("2d"); const colors = getThemeColors(); const isDark = document.documentElement.classList.contains("dark") || document.body.classList.contains("dark"); // Подготавливаем данные 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: colors.backgroundColor, titleColor: colors.titleColor, bodyColor: colors.bodyColor, 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: colors.textColor, font: { size: 12, color: colors.textColor, }, }, }, y: { grid: { color: colors.gridColor, lineWidth: 1, }, ticks: { color: colors.textColor, font: { size: 12, color: colors.textColor, }, callback: function (value) { return value + "°C"; }, }, }, }, animation: { duration: 2000, easing: "easeInOutQuart", }, interaction: { intersect: false, mode: "index", }, }, }); } // Функция для получения цветов в зависимости от темы function getThemeColors() { const isDark = document.documentElement.classList.contains("dark") || document.body.classList.contains("dark"); return { textColor: isDark ? "#d1d5db" : "#ffffff", gridColor: isDark ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.1)", backgroundColor: isDark ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.8)", titleColor: isDark ? "#ffffff" : "#ffffff", bodyColor: isDark ? "#ffffff" : "#ffffff", }; } // Функции для работы с темами function initializeTheme() { const savedTheme = localStorage.getItem("theme") || "auto"; setTheme(savedTheme); updateThemeButtons(savedTheme); // Если выбрана авто-тема, проверяем системную тему при загрузке if (savedTheme === "auto") { const prefersDark = window.matchMedia( "(prefers-color-scheme: dark)" ).matches; updateThemeButtons(prefersDark ? "dark" : "light"); } // Добавляем обработчики событий для кнопок темы if (themeButtons.light) { themeButtons.light.addEventListener("click", () => { setTheme("light"); updateThemeButtons("light"); }); } if (themeButtons.auto) { themeButtons.auto.addEventListener("click", () => { setTheme("auto"); updateThemeButtons("auto"); }); } if (themeButtons.dark) { themeButtons.dark.addEventListener("click", () => { setTheme("dark"); updateThemeButtons("dark"); }); } } function setTheme(theme) { const html = document.documentElement; // Удаляем предыдущие настройки темы html.removeAttribute("data-theme"); html.classList.remove("dark"); document.body.classList.remove("dark"); if (theme === "dark") { html.setAttribute("data-theme", "dark"); html.classList.add("dark"); document.body.classList.add("dark"); } else if (theme === "auto") { // Определяем системную тему const prefersDark = window.matchMedia( "(prefers-color-scheme: dark)" ).matches; if (prefersDark) { html.setAttribute("data-theme", "dark"); html.classList.add("dark"); document.body.classList.add("dark"); } else { html.setAttribute("data-theme", "light"); } } else { // Светлая тема html.setAttribute("data-theme", "light"); } // Сохраняем выбранную тему localStorage.setItem("theme", theme); // Обновляем иконки после смены темы setTimeout(() => { if (typeof lucide !== "undefined") { lucide.createIcons(); } }, 100); // Принудительно перестраиваем Swiper если он существует if (window.weatherSwiper) { setTimeout(() => { window.weatherSwiper.update(); window.weatherSwiper.updateSlides(); }, 150); } // Обновляем графики если они существуют if (temperatureChart) { temperatureChart.destroy(); createTemperatureChart(window.currentHourlyData || []); } if (precipitationChart) { precipitationChart.destroy(); createPrecipitationChart(window.currentHourlyData || []); } } function updateThemeButtons(activeTheme) { Object.keys(themeButtons).forEach((theme) => { if (themeButtons[theme]) { if (theme === activeTheme) { themeButtons[theme].classList.add("active"); } else { themeButtons[theme].classList.remove("active"); } } }); } // Слушаем изменения системной темы function watchSystemTheme() { const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); mediaQuery.addEventListener("change", (e) => { const currentTheme = localStorage.getItem("theme"); if (currentTheme === "auto") { setTheme("auto"); } }); } // Функция создания графика осадков function createPrecipitationChart(hourlyData) { const ctx = document.getElementById("precipitationChart").getContext("2d"); const colors = getThemeColors(); // Подготавливаем данные 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: colors.backgroundColor, titleColor: colors.titleColor, bodyColor: colors.bodyColor, 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: colors.textColor, font: { size: 12, color: colors.textColor, }, }, }, y: { type: "linear", display: true, position: "left", grid: { color: colors.gridColor, lineWidth: 1, }, ticks: { color: colors.textColor, font: { size: 12, color: colors.textColor, }, 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, color: "#ef4444", }, 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", }, }, }); }