/
varsl
/
HTML-CSS
Обзор
Документация
Войти
/
varsl
/
HTML-CSS
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
frontend/js/animals.js
643 строки
27 KB
varsl
Изменение логики загрузки видео
21 дек 2025, 23:46
21 дек 2025, 23:46
860dced
Код
Авторство
О чём код?
class AnimalsManager { constructor() { this.animals = []; this.filteredAnimals = []; this.currentFilter = 'all'; this.currentSort = 'name'; this.youtubeVideos = { 'lion': 'https://youtu.be/gnO-UjL5HII', 'elephant': 'https://youtu.be/Gh_dM9gpCp0', 'panda': 'https://youtu.be/_roD2Qjx7eY', 'tiger': 'https://youtu.be/RSzbwqp-LsY', 'giraffe': 'https://youtu.be/A5AHExS_ch0', 'penguin': 'https://youtu.be/IdEa5_JKSoM', 'monkey': 'https://youtu.be/TxLejciGDtg' }; this.init(); } async init() { await this.loadAnimals(); this.renderAnimals(); this.setupEventListeners(); this.initAnimalSounds(); this.initAnimalVideos(); } async loadAnimals() { try { const response = await fetch('assets/data/animals.json'); this.animals = await response.json(); this.filteredAnimals = [...this.animals]; } catch (error) { console.error('Ошибка загрузки данных о животных:', error); this.loadDefaultAnimals(); } } loadDefaultAnimals() { this.animals = [ { id: 'lion', name: 'Лев', scientificName: 'Panthera leo', type: 'mammal', habitat: 'Саванна', diet: 'Хищник', description: 'Крупная кошка, известная как "царь зверей". Социальные животные, живут в прайдах.', size: 'Длина: 1.7-2.5м, Вес: 150-250кг', lifespan: '10-14 лет в дикой природе', conservation: 'Уязвимый вид', sound: 'lion', video: 'gnO-UjL5HII', youtubeUrl: 'https://youtu.be/gnO-UjL5HII', image: 'lion.jpg', funFact: 'Львы могут спать до 20 часов в сутки.', feedingTime: '14:00', showSchedule: 'Шоу хищников: 12:00, 16:00' }, { id: 'elephant', name: 'Африканский слон', scientificName: 'Loxodonta africana', type: 'mammal', habitat: 'Саванна, лес', diet: 'Травоядное', description: 'Крупнейшее наземное животное. Обладает отличной памятью и сложной социальной структурой.', size: 'Высота: 3-4м, Вес: 4-7 тонн', lifespan: '60-70 лет', conservation: 'Находится под угрозой', sound: 'elephant', video: 'Gh_dM9gpCp0', youtubeUrl: 'https://youtu.be/Gh_dM9gpCp0', image: 'elephant.jpg', funFact: 'Хобот слона содержит около 150 000 мышц.', feedingTime: '12:00, 16:00', showSchedule: 'Демонстрация слонов: 11:00, 15:00' }, { id: 'panda', name: 'Большая панда', scientificName: 'Ailuropoda melanoleuca', type: 'mammal', habitat: 'Бамбуковые леса', diet: 'Травоядное (99% бамбук)', description: 'Символ Китая и охраны природы. Проводят большую часть дня за едой.', size: 'Длина: 1.2-1.9м, Вес: 75-135кг', lifespan: '20 лет в неволе', conservation: 'Уязвимый вид', sound: 'panda', video: '_roD2Qjx7eY', youtubeUrl: 'https://youtu.be/_roD2Qjx7eY', image: 'panda.jpg', funFact: 'Новорожденная панда весит всего 100 грамм.', feedingTime: '11:00, 15:00', showSchedule: 'Кормление панд: 11:30, 15:30' }, { id: 'tiger', name: 'Амурский тигр', scientificName: 'Panthera tigris altaica', type: 'mammal', habitat: 'Тайга', diet: 'Хищник', description: 'Самый крупный представитель семейства кошачьих. Отличный охотник и пловец.', size: 'Длина: 2.7-3.3м, Вес: 180-300кг', lifespan: '15-20 лет', conservation: 'Находящийся под угрозой', sound: 'tiger', video: 'RSzbwqp-LsY', youtubeUrl: 'https://youtu.be/RSzbwqp-LsY', image: 'tiger.jpg', funFact: 'Узор полосок у каждого тигра уникален, как отпечатки пальцев у человека.', feedingTime: '13:00', showSchedule: 'Тигриное шоу: 13:30' }, { id: 'giraffe', name: 'Жираф', scientificName: 'Giraffa camelopardalis', type: 'mammal', habitat: 'Саванна', diet: 'Травоядное', description: 'Самое высокое наземное животное. Длинная шея помогает доставать листья с деревьев.', size: 'Высота: 4.3-5.7м, Вес: 800-1200кг', lifespan: '25 лет', conservation: 'Уязвимый вид', sound: 'giraffe', video: 'A5AHExS_ch0', youtubeUrl: 'https://youtu.be/A5AHExS_ch0', image: 'giraffe.jpg', funFact: 'Шея жирафа содержит то же количество позвонков, что и шея человека - семь.', feedingTime: '10:30, 14:30', showSchedule: 'Кормление жирафов: 10:45, 14:45' }, { id: 'penguin', name: 'Императорский пингвин', scientificName: 'Aptenodytes forsteri', type: 'bird', habitat: 'Антарктика', diet: 'Рыба, криль', description: 'Самый крупный вид пингвинов. Отличные пловцы, могут нырять на глубину до 500 метров.', size: 'Высота: 1.1-1.3м, Вес: 22-45кг', lifespan: '15-20 лет', conservation: 'Близкий к угрозе', sound: 'penguin', video: 'IdEa5_JKSoM', youtubeUrl: 'https://youtu.be/IdEa5_JKSoM', image: 'penguin.jpg', funFact: 'Императорские пингвины могут выдерживать температуру до -60°C.', feedingTime: '11:00, 15:00, 17:00', showSchedule: 'Пингвинье шоу: 12:00, 16:00' }, { id: 'monkey', name: 'Горилла', scientificName: 'Gorilla gorilla', type: 'mammal', habitat: 'Тропические леса', diet: 'Травоядное', description: 'Крупнейшие приматы. Обладают высоким интеллектом и сложной социальной структурой.', size: 'Высота: 1.4-1.8м, Вес: 135-180кг', lifespan: '35-40 лет', conservation: 'Находящийся под угрозой', sound: 'monkey', video: 'TxLejciGDtg', youtubeUrl: 'https://youtu.be/TxLejciGDtg', image: 'monkey.jpg', funFact: 'Гориллы делятся 98% ДНК с человеком.', feedingTime: '10:00, 14:00, 16:00', showSchedule: 'Обезьяний остров: весь день' } ]; this.filteredAnimals = [...this.animals]; } renderAnimals() { const container = document.getElementById('animalsGrid'); if (!container) return; container.innerHTML = ''; this.filteredAnimals.forEach(animal => { const animalCard = this.createAnimalCard(animal); container.appendChild(animalCard); }); this.updateAnimalCount(); } createAnimalCard(animal) { const card = document.createElement('div'); card.className = 'animal-card hover-animation'; card.setAttribute('data-animal', animal.id); card.setAttribute('data-type', animal.type); card.innerHTML = ` <div class="animal-card-image"> <img src="assets/images/${animal.image}" alt="${animal.name}" loading="lazy"> <div class="animal-type-badge">${this.getTypeIcon(animal.type)}</div> <button class="animal-play-btn play-sound" data-sound="${animal.sound}"> <i class="fas fa-volume-up"></i> </button> </div> <div class="animal-card-content"> <h3 class="animal-name">${animal.name}</h3> <p class="animal-scientific">${animal.scientificName}</p> <p class="animal-description">${animal.description.substring(0, 100)}...</p> <div class="animal-details"> <div class="animal-detail"> <i class="fas fa-home"></i> <span>${animal.habitat}</span> </div> <div class="animal-detail"> <i class="fas fa-utensils"></i> <span>${animal.diet}</span> </div> </div> <div class="animal-card-actions"> <button class="btn btn-small btn-primary view-animal" data-id="${animal.id}"> <i class="fas fa-binoculars"></i> Подробнее </button> <button class="btn btn-small btn-outline watch-video" data-video="${animal.video}"> <i class="fas fa-video"></i> Видео </button> </div> </div> `; return card; } getTypeIcon(type) { const icons = { mammal: '<i class="fas fa-paw"></i> Млекопитающее', bird: '<i class="fas fa-dove"></i> Птица', reptile: '<i class="fas fa-dragon"></i> Рептилия', fish: '<i class="fas fa-fish"></i> Рыба', amphibian: '<i class="fas fa-frog"></i> Амфибия' }; return icons[type] || '<i class="fas fa-paw"></i> Животное'; } updateAnimalCount() { const countElement = document.getElementById('animalCount'); if (countElement) { countElement.textContent = `Найдено животных: ${this.filteredAnimals.length}`; } } setupEventListeners() { this.setupFilters(); this.setupSorting(); this.setupSearch(); this.setupModalEvents(); } setupFilters() { const filterButtons = document.querySelectorAll('.animal-filter'); filterButtons.forEach(button => { button.addEventListener('click', (e) => { const filter = e.target.getAttribute('data-filter'); this.currentFilter = filter; filterButtons.forEach(btn => btn.classList.remove('active')); e.target.classList.add('active'); this.filterAnimals(); this.renderAnimals(); }); }); } filterAnimals() { if (this.currentFilter === 'all') { this.filteredAnimals = [...this.animals]; } else { this.filteredAnimals = this.animals.filter(animal => animal.type === this.currentFilter ); } this.sortAnimals(); } setupSorting() { const sortSelect = document.getElementById('sortAnimals'); if (sortSelect) { sortSelect.addEventListener('change', (e) => { this.currentSort = e.target.value; this.sortAnimals(); this.renderAnimals(); }); } } sortAnimals() { this.filteredAnimals.sort((a, b) => { switch(this.currentSort) { case 'name': return a.name.localeCompare(b.name); case 'size': return this.parseSize(b.size) - this.parseSize(a.size); case 'lifespan': return this.parseLifespan(b.lifespan) - this.parseLifespan(a.lifespan); default: return a.name.localeCompare(b.name); } }); } parseSize(sizeString) { const match = sizeString.match(/\d+/); return match ? parseInt(match[0]) : 0; } parseLifespan(lifespanString) { const match = lifespanString.match(/\d+/); return match ? parseInt(match[0]) : 0; } setupSearch() { const searchInput = document.getElementById('animalSearch'); if (searchInput) { searchInput.addEventListener('input', (e) => { const searchTerm = e.target.value.toLowerCase(); if (searchTerm.length >= 2) { this.filteredAnimals = this.animals.filter(animal => animal.name.toLowerCase().includes(searchTerm) || animal.scientificName.toLowerCase().includes(searchTerm) || animal.description.toLowerCase().includes(searchTerm) || animal.habitat.toLowerCase().includes(searchTerm) ); } else { this.filteredAnimals = [...this.animals]; } this.filterAnimals(); this.renderAnimals(); }); } } initAnimalSounds() { document.addEventListener('click', (e) => { if (e.target.closest('.play-sound')) { const button = e.target.closest('.play-sound'); const sound = button.getAttribute('data-sound'); this.playAnimalSound(sound); } }); } playAnimalSound(soundName) { const audio = new Audio(`assets/sounds/${soundName}.mp3`); audio.volume = 0.6; audio.play().catch(e => { console.log('Ошибка воспроизведения звука:', e); }); } initAnimalVideos() { document.addEventListener('click', (e) => { if (e.target.closest('.watch-video')) { const button = e.target.closest('.watch-video'); const videoId = button.getAttribute('data-video'); this.showAnimalVideo(videoId); } if (e.target.closest('.view-animal')) { const button = e.target.closest('.view-animal'); const animalId = button.getAttribute('data-id'); this.showAnimalDetails(animalId); } }); } showAnimalVideo(videoId) { // Создаем модальное окно для YouTube const modal = document.createElement('div'); modal.className = 'modal youtube-modal show'; modal.innerHTML = ` <div class="modal-content youtube-modal-content"> <div class="modal-header"> <h3>Видео с животным</h3> <button class="modal-close"> <i class="fas fa-times"></i> </button> </div> <div class="modal-body"> <div class="youtube-video-container"> <iframe src="https://www.youtube.com/embed/${videoId}?autoplay=1" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen class="youtube-video"> </iframe> </div> </div> </div> `; document.body.appendChild(modal); // Закрытие модального окна modal.querySelector('.modal-close').addEventListener('click', () => { document.body.removeChild(modal); }); modal.addEventListener('click', (e) => { if (e.target === modal) { document.body.removeChild(modal); } }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && modal.parentNode) { document.body.removeChild(modal); } }); } async showAnimalDetails(animalId) { const animal = this.animals.find(a => a.id === animalId); if (!animal) return; const detailsModal = document.getElementById('animalDetailsModal'); if (!detailsModal) return; detailsModal.innerHTML = ` <div class="modal-content"> <div class="modal-header"> <h2>${animal.name}</h2> <button class="modal-close"> <i class="fas fa-times"></i> </button> </div> <div class="modal-body"> <div class="animal-details-grid"> <div class="animal-details-image"> <img src="assets/images/${animal.image}" alt="${animal.name}"> <div class="animal-actions"> <button class="btn btn-primary play-sound" data-sound="${animal.sound}"> <i class="fas fa-volume-up"></i> Послушать </button> <button class="btn btn-outline watch-video" data-video="${animal.video}"> <i class="fas fa-video"></i> Смотреть видео </button> </div> </div> <div class="animal-details-info"> <div class="animal-info-section"> <h3><i class="fas fa-info-circle"></i> Основная информация</h3> <p><strong>Научное название:</strong> ${animal.scientificName}</p> <p><strong>Тип:</strong> ${this.getTypeIcon(animal.type)}</p> <p><strong>Среда обитания:</strong> ${animal.habitat}</p> <p><strong>Питание:</strong> ${animal.diet}</p> <p><strong>Размер:</strong> ${animal.size}</p> <p><strong>Продолжительность жизни:</strong> ${animal.lifespan}</p> <p><strong>Статус сохранения:</strong> ${animal.conservation}</p> </div> <div class="animal-info-section"> <h3><i class="fas fa-book-open"></i> Описание</h3> <p>${animal.description}</p> </div> <div class="animal-info-section"> <h3><i class="fas fa-lightbulb"></i> Интересный факт</h3> <p>${animal.funFact}</p> </div> <div class="animal-info-section"> <h3><i class="fas fa-calendar-alt"></i> Расписание в зоопарке</h3> <p><strong>Время кормления:</strong> ${animal.feedingTime}</p> <p><strong>Шоу и активности:</strong> ${animal.showSchedule}</p> </div> </div> </div> </div> <div class="modal-footer"> <button class="btn btn-outline" onclick="window.location.href='map.html'"> <i class="fas fa-map-marker-alt"></i> Найти на карте </button> <button class="btn btn-primary" onclick="window.location.href='ticket.html'"> <i class="fas fa-ticket-alt"></i> Купить билет </button> </div> </div> `; detailsModal.classList.add('show'); detailsModal.querySelector('.modal-close').addEventListener('click', () => { detailsModal.classList.remove('show'); }); const playSoundBtn = detailsModal.querySelector('.play-sound'); if (playSoundBtn) { playSoundBtn.addEventListener('click', () => { this.playAnimalSound(animal.sound); }); } const watchVideoBtn = detailsModal.querySelector('.watch-video'); if (watchVideoBtn) { watchVideoBtn.addEventListener('click', () => { this.showAnimalVideo(animal.video); }); } } setupModalEvents() { const closeModal = (modal) => { if (modal) { modal.classList.remove('show'); const iframe = modal.querySelector('iframe'); if (iframe) { // Останавливаем видео при закрытии iframe.src = iframe.src.replace('autoplay=1', 'autoplay=0'); } } }; document.addEventListener('click', (e) => { if (e.target.classList.contains('modal-close')) { const modal = e.target.closest('.modal'); closeModal(modal); } if (e.target.classList.contains('modal')) { closeModal(e.target); } }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { const modals = document.querySelectorAll('.modal.show'); modals.forEach(modal => closeModal(modal)); } }); } initRandomAnimal() { const randomAnimalBtn = document.getElementById('randomAnimal'); if (randomAnimalBtn) { randomAnimalBtn.addEventListener('click', () => { const randomIndex = Math.floor(Math.random() * this.animals.length); const randomAnimal = this.animals[randomIndex]; this.showAnimalDetails(randomAnimal.id); }); } } initFavoriteAnimals() { const favoriteButtons = document.querySelectorAll('.favorite-animal'); favoriteButtons.forEach(button => { button.addEventListener('click', (e) => { const animalId = e.target.closest('button').getAttribute('data-id'); this.toggleFavorite(animalId); const icon = e.target.closest('button').querySelector('i'); if (icon.classList.contains('far')) { icon.classList.remove('far'); icon.classList.add('fas'); } else { icon.classList.remove('fas'); icon.classList.add('far'); } }); }); } toggleFavorite(animalId) { let favorites = JSON.parse(localStorage.getItem('favoriteAnimals') || '[]'); if (favorites.includes(animalId)) { favorites = favorites.filter(id => id !== animalId); } else { favorites.push(animalId); } localStorage.setItem('favoriteAnimals', JSON.stringify(favorites)); this.showNotification('Список избранного обновлен'); } showNotification(message) { const notification = document.createElement('div'); notification.className = 'notification'; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.classList.add('show'); }, 10); setTimeout(() => { notification.classList.remove('show'); setTimeout(() => notification.remove(), 300); }, 3000); } } document.addEventListener('DOMContentLoaded', function() { const animalsManager = new AnimalsManager(); animalsManager.initRandomAnimal(); animalsManager.initFavoriteAnimals(); const compareAnimalsBtn = document.getElementById('compareAnimals'); if (compareAnimalsBtn) { compareAnimalsBtn.addEventListener('click', () => { const selectedAnimals = document.querySelectorAll('.animal-checkbox:checked'); if (selectedAnimals.length >= 2) { const animalIds = Array.from(selectedAnimals).map(cb => cb.value); animalsManager.showComparison(animalIds); } else { alert('Выберите минимум 2 животных для сравнения'); } }); } const checkAllBtn = document.getElementById('checkAllAnimals'); if (checkAllBtn) { checkAllBtn.addEventListener('click', () => { const checkboxes = document.querySelectorAll('.animal-checkbox'); const allChecked = Array.from(checkboxes).every(cb => cb.checked); checkboxes.forEach(cb => { cb.checked = !allChecked; }); checkAllBtn.innerHTML = allChecked ? '<i class="fas fa-check-square"></i> Выбрать все' : '<i class="fas fa-square"></i> Снять все'; }); } });