diff --git a/main/index.html b/main/index.html new file mode 100644 index 000000000..b14999b19 --- /dev/null +++ b/main/index.html @@ -0,0 +1,25 @@ + + + + + + Кошачий Pinterest + + + +
+ +
+ +
+
+
+
...загружаем еще котиков...
+
+ + + + \ No newline at end of file diff --git a/main/script.js b/main/script.js new file mode 100644 index 000000000..4e879dc27 --- /dev/null +++ b/main/script.js @@ -0,0 +1,121 @@ +const API_KEY = 'live_NkfkwgsT5jyDaTq14WAYKqJ0EOJPTadssh4xMRuDVNc8xlJmRw4yhC1WgemOJoMs'; +const grid = document.getElementById('cats-grid'); +const loader = document.getElementById('loader'); +let currentPage = 0; +let currentView = 'all'; + +async function fetchCats(page) { + try { + const response = await fetch(`https://api.thecatapi.com/v1/images/search?limit=15&page=${page}&order=DESC`, { + headers: { 'x-api-key': API_KEY } + }); + return await response.json(); + } catch (e) { + console.error("Ошибка загрузки:", e); + return []; + } +} + +function renderCat(cat, isFavorite) { + const card = document.createElement('div'); + card.className = 'cat-card'; + + card.innerHTML = ` + cat + + `; + + const btn = card.querySelector('.fav-btn'); + btn.onclick = (e) => { + e.stopPropagation(); + toggleFavorite(cat); + btn.classList.toggle('active'); + + if (currentView === 'fav' && !btn.classList.contains('active')) { + card.remove(); + } + }; + + grid.appendChild(card); +} + +function toggleFavorite(cat) { + let favorites = JSON.parse(localStorage.getItem('favCats')) || []; + const index = favorites.findIndex(f => f.id === cat.id); + + if (index === -1) { + favorites.push(cat); + } else { + favorites.splice(index, 1); + } + localStorage.setItem('favCats', JSON.stringify(favorites)); +} + +async function init() { + const cats = await fetchCats(0); + const favorites = JSON.parse(localStorage.getItem('favCats')) || []; + + cats.forEach(cat => { + const isFav = favorites.some(f => f.id === cat.id); + renderCat(cat, isFav); + }); +} + +document.getElementById('show-fav').onclick = function() { + currentView = 'fav'; + document.getElementById('show-all').classList.remove('active'); + this.classList.add('active'); + + loader.classList.add('hidden'); + + grid.innerHTML = ''; + const favorites = JSON.parse(localStorage.getItem('favCats')) || []; + favorites.forEach(cat => renderCat(cat, true)); +}; + +document.getElementById('show-all').onclick = function() { + currentView = 'all'; + document.getElementById('show-fav').classList.remove('active'); + this.classList.add('active'); + + loader.classList.remove('hidden'); + + grid.innerHTML = ''; + init(); +}; + +init(); + +let isLoading = false; + +const observer = new IntersectionObserver((entries) => { + if (entries[0].isIntersecting && currentView === 'all' && !isLoading) { + loadMoreCats(); + } +}, { + rootMargin: '200px', + threshold: 0.1 +}); + +observer.observe(document.querySelector('#loader')); + +async function loadMoreCats() { + isLoading = true; + currentPage++; + + const newCats = await fetchCats(currentPage); + const favorites = JSON.parse(localStorage.getItem('favCats')) || []; + + if (newCats.length > 0) { + newCats.forEach(cat => { + const isFav = favorites.some(f => f.id === cat.id); + renderCat(cat, isFav); + }); + } + + isLoading = false; +} \ No newline at end of file diff --git a/main/style.css b/main/style.css new file mode 100644 index 000000000..11bec3c99 --- /dev/null +++ b/main/style.css @@ -0,0 +1,152 @@ +body { + margin: 0; + font-family: 'Roboto', sans-serif; + background-color: #fff; +} + +.header { + background-color: #2196f3; + padding: 0 50px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + position: sticky; + top: 0; + z-index: 100; +} + +.navigation { + display: flex; + gap: 20px; +} + +.nav-btn { + background: none; + border: none; + color: rgba(255, 255, 255, 0.7); + padding: 20px; + cursor: pointer; + font-size: 16px; +} + +.nav-btn.active { + color: #fff; + background-color: #1e88e5; +} + +.container { + padding: 40px; +} + +.cats-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(225px, 1fr)); + gap: 48px; +} + +.cat-card { + position: relative; + width: 225px; + height: 225px; + transition: transform 0.2s; + overflow: hidden; +} + +.cat-card:hover { + transform: scale(1.1); + box-shadow: 0 10px 20px rgba(0,0,0,0.2); +} + +.cat-image { + width: 100%; + height: 100%; + object-fit: cover; +} + +.fav-btn { + position: absolute; + bottom: 15px; + right: 15px; + background: none; + border: none; + cursor: pointer; + width: 40px; + height: 40px; + background-image: url('heart-empty.svg'); + background-size: contain; + opacity: 0; + transition: opacity 0.3s; +} + +.cat-card:hover .fav-btn { + opacity: 1; +} + +.fav-btn.active { + background-image: url('heart-filled.svg'); + opacity: 1; +} + +.cat-card { + position: relative; + width: 225px; + height: 225px; + cursor: pointer; + transition: all 0.3s ease; +} + +.cat-card:hover { + transform: scale(1.1); + box-shadow: 0 10px 20px rgba(0,0,0,0.2); +} + +.fav-btn { + position: absolute; + bottom: 15px; + right: 15px; + background: none; + border: none; + padding: 0; + cursor: pointer; + opacity: 0; + transition: opacity 0.3s ease, transform 0.2s ease; +} + +.cat-card:hover .fav-btn { + opacity: 1; +} + +.heart-icon { + width: 40px; + height: 40px; + fill: none; + stroke: #FF4207; + stroke-width: 2px; + transition: fill 0.2s, stroke 0.2s; +} + +.fav-btn:hover .heart-icon { + fill: #FF4207; + transform: scale(1.1); +} + +.fav-btn.active .heart-icon { + fill: #F00; + stroke: #F00; + opacity: 1; +} + +.fav-btn.active { + opacity: 1; +} + +.loader { + text-align: center; + padding: 20px; + width: 100%; + font-size: 16px; + margin-top: 20px; + display: block; +} + +.hidden { + display: none !important; +} \ No newline at end of file