Кроме 4-х фотографий каждый слайд имеет фоновое изображение, заголовок и описание. И все это анимируется движком anime.js
Пример:
HTML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
<div class="slider"> <div class="nav"> <div class="prev"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg> </div> <div class="next"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg> </div> </div> <div class="item is-active" style="background-image: url(bg-photo.jpg)"> <div class="content"> <div class="item-title">Заголовок</div> <div class="item-text">Описание</div> </div> <div class="imgs"> <div class="grid"> <div class="img img-1"> <img src="photo-1.jpg"/> </div> <div class="img img-2"> <img src="photo-2.jpg"/> </div> <div class="img img-3"> <img src="photo-3.jpg"/> </div> <div class="img img-4"> <img src="photo-4.jpg"/> </div> </div> </div> </div> <!-- Еще слайды --> <div class="item" style="background-image: url(bg-photo.jpg)"> <div class="content"> <div class="item-title">Заголовок</div> <div class="item-text">Описание</div> </div> <div class="imgs"> <div class="grid"> <div class="img img-1"> <img src="photo-1.jpg"/> </div> <div class="img img-2"> <img src="photo-2.jpg"/> </div> <div class="img img-3"> <img src="photo-3.jpg"/> </div> <div class="img img-4"> <img src="photo-4.jpg"/> </div> </div> </div> </div> </div> |
Обратите внимание, первый слайд имеет класс is-active
CSS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
.slider * { box-sizing: border-box; } .slider { height: 600px; display: flex; align-items: center; justify-content: center; position: relative; overflow: hidden; margin: 20px 0; box-shadow: 0 4px 12px rgba(0,0,0,0.2), 0 16px 20px rgba(0,0,0,0.2); } .slider .item { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background-size: cover; opacity: 1; transition: opacity 0.3s; } .slider .item:not(.is-active) { opacity: 0; pointer-events: none; } .slider .item:after { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); } .slider .item .imgs { position: relative; width: 60%; padding-top: 60%; } .slider .item .imgs .grid { position: absolute; z-index: 2; top: 0; left: 0; width: 100%; height: 100%; display: grid; grid-template-columns: repeat(12, 1fr); grid-template-rows: repeat(12, 1fr); grid-column-gap: 32px; grid-row-gap: 32px; transform: rotate(-20deg); } .slider .item .img { width: 100%; height: 100%; position: relative; will-change: transform; box-shadow: -2px 4px 12px rgba(0,0,0,0.2), -8px 16px 20px rgba(0,0,0,0.2); background: #000; } .slider .item .img img { position: absolute; top: 0; width: 100%; height: 100%; object-fit: cover; position: relative; opacity: 0.7; transition: opacity 1s; } .slider .item .img:hover img { opacity: 1; } .slider .item .img-1 { grid-area: 1/1/7/5; } .slider .item .img-2 { grid-area: 2/5/7/13; } .slider .item .img-3 { grid-area: 7/1/12/9; } .slider .item .img-4 { grid-area: 7/9/13/13; } .slider .item .content { pointer-events: none; position: absolute; z-index: 3; top: 0; left: 0; width: 100%; padding: 0 120px; height: 100%; display: flex; flex-flow: column; align-items: center; justify-content: center; line-height: 1.5; font-weight: 600; font-family: Verdana, sans-serif; text-align: center; text-shadow: 0 0 12px rgba(0,0,0,0.5), 0 2px 6px rgba(0,0,0,0.7); color: #BFE2FF; font-size: 44px; text-transform: uppercase; } .slider .item .content .item-text { color: #FFF; font-size: 20px; margin-top: 20px; } .slider .item .content .item-title .letter { display: inline-block; } .slider .nav .next, .slider .nav .prev { height: 80px; width: 80px; position: absolute; top: calc(50% - 40px); cursor: pointer; z-index: 4; transition: transform 0.3s; } .slider .nav .next { right: 20px; } .slider .nav .next:hover { transform: translateX(10px); } .slider .nav .prev { left: 20px; } .slider .nav .prev:hover { transform: translateX(-10px); } .slider .nav .next svg, .slider .nav .prev svg { stroke: #BFE2FF; transition: stroke 0.3s; } .slider .nav .next:hover svg, .slider .nav .prev:hover svg { stroke: #FFF; } @media (max-width: 767px) { .slider { height: 400px; } .slider .nav .next, .slider .nav .prev { height: 40px; width: 40px; top: calc(50% - 20px); } .slider .nav .next { right: 10px; } .slider .nav .prev { left: 10px; } .slider .item .content { font-size: 20px; padding: 0 60px; } .slider .item .content .item-text { font-size: 13px; } } @media (max-width: 460px) { .slider .item .imgs { width: 100%; padding-top: 100%; } } |
JS:
Подключаем anime.min.js и скрипт ниже.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
function init() { const slider = document.querySelector(".slider"); const nextBtn = slider.querySelector(".slider .nav .next"); const prevBtn = slider.querySelector(".slider .nav .prev"); const items = slider.querySelectorAll(".slider .item"); let current = 0; items.forEach((item) => { const textWrapper1 = item.querySelector(".item-title"); textWrapper1.innerHTML = textWrapper1.textContent.replace( /\S/g, "<span class='letter'>$&</span>" ); const textWrapper2 = item.querySelector(".item-text"); textWrapper2.innerHTML = textWrapper2.textContent.replace( /\S/g, "<span class='letter'>$&</span>" ); }); function anim(current, next, callback) { const currentImgs = current.querySelectorAll(".img"); const currentText = current.querySelectorAll(".content .letter"); const nextImgs = next.querySelectorAll(".img"); const nextText = next.querySelectorAll(".content .letter"); const duration = 400; const offset = "-=" + 300; const imgOffset = duration*.8; const tl = anime.timeline({ easing: "easeInOutQuint", duration: duration, complete: callback }); // Add children tl.add({ targets: currentText, translateY: [0, '-150px'], opacity: [1, 0], easing: "easeInQuint", duration: 600, delay: 0 }) .add({ targets: currentImgs[0], translateY: -600, rotate: [0, '-15deg'], opacity: [1, 0], easing: "easeInCubic" }, offset ) .add({ targets: currentImgs[1], translateY: -600, rotate: [0, '15deg'], opacity: [1, 0], easing: "easeInCubic" }, "-=" + imgOffset ) .add({ targets: currentImgs[2], translateY: -600, rotate: [0, '-15deg'], opacity: [1, 0], easing: "easeInCubic" }, "-=" + imgOffset ) .add({ targets: currentImgs[3], translateY: -600, rotate: [0, '15deg'], opacity: [1, 0], easing: "easeInCubic" }, "-=" + imgOffset ) .add({ targets: current, opacity: 0, duration: 10, easing: "easeInCubic" }) .add({ targets: next, opacity: 1, duration: 10 }, offset ) .add({ targets: nextImgs[0], translateY: [600, 0], rotate: ['15deg', 0], opacity: [0, 1], easing: "easeOutCubic" }, offset ) .add({ targets: nextImgs[1], translateY: [600, 0], rotate: ['-15deg', 0], opacity: [0, 1], easing: "easeOutCubic" }, "-=" + imgOffset ) .add({ targets: nextImgs[2], translateY: [600, 0], rotate: ['15deg', 0], opacity: [0, 1], easing: "easeOutCubic" }, "-=" + imgOffset ) .add({ targets: nextImgs[3], translateY: [600, 0], rotate: ['-15deg', 0], opacity: [0, 1], easing: "easeOutCubic" }, "-=" + imgOffset ) .add({ targets: nextText, translateY: ['150px', 0], opacity: [0, 1], easing: "easeOutCubic", duration: 600, delay: (el, i) => 10 * (i + 1) }, offset ); } let isPlaying = false; function updateSlider(newIndex) { const currentItem = items[current]; const newItem = items[newIndex]; function callback() { currentItem.classList.remove("is-active"); newItem.classList.add("is-active"); current = newIndex; isPlaying = false; } anim(currentItem, newItem, callback); } function next() { if (isPlaying) return; isPlaying = true; const newIndex = current === items.length - 1 ? 0 : current + 1; updateSlider(newIndex); } function prev() { if (isPlaying) return; isPlaying = true; const newIndex = current === 0 ? items.length - 1 : current - 1; updateSlider(newIndex); } nextBtn.onclick = next; prevBtn.onclick = prev; } document.addEventListener("DOMContentLoaded", init); |
За основу взято решение, найденное на codepen.io у пользователя Haja Randriakoto
Библиотека anime.js
Фотографии pexels.com
Смотрите также:
Овальное разделение текста в секции от фотографии
Смена слайдов в карусели на Bootstrap 3 движением мышки или пальца
Слайдер на jQuery с фиксированной текстовой информацией и листающимися изображениями
Добавить комментарий:
Ваш комментарий отправлен!
жалко что не работает
Не работает прокрутка. Подскажите что может быть. подключал js через внешний файл и через тэг. не подгружает js
Нужно ссылку на результат или ошибки, которые в консоле.
Из-за чего может не работать прокрутка? Все файлы скопировала правильно.
function init в js забыли поставить };
Весь JS код эта функция, а в последней строке вызывается.
Подскажите пожалуйста как сделать кнопку.Кнопки неактивные почему то получаются
Точно также, как и в предыдущем комментарии, нужно добавить pointer-events: auto;
Как можно добавить ссылку
Добавить к классу item-title стиль pointer-events: auto; и соотв. поставить саму ссылку на заголовок.
А как сделать чтобы автоматически перелистывалось через 5 секунд? Спасибо
Можно ниже
Добавить: