Пустой HTML5 и bootstrap шаблон для начала любого проекта
Представляем Вам пустые bootstrap и html5 шаблоны для старта любого проекта. В исходниках Вы увидите необходимые компоненты для работы с bootstrap шаблоном.
Когда я начал изучать эту тему, мне не нужна была трехмерная карусель, но я больше интересовался техническими деталями ее реализации. Разумеется, основные используемые методы, конечно же, относятся к модулю CSS Transforms Module Level 1 , но по мере того, как будет применена группа других технологий разработки переднего плана, затрагивая различные темы в CSS, Sass и клиентском JavaScript .
Чтобы проиллюстрировать настройку 3D-преобразований CSS, я покажу вам версию компонента только для CSS. Затем я покажу вам, как улучшить его с помощью JavaScript, разработав простой скрипт компонента.
Для разметки изображения внутри компонента обернуты внутри <figure> элемента, который обеспечивает базовый скелет:
<figure style="transform-origin: 50% 50% -600.159px; transform: rotateY(0rad);">
<img src="https://source.unsplash.com/nvzvOPQW0gc/800x533" alt="" style="padding: 0px;">
<img src="https://source.unsplash.com/EbuaKnSm8Zw/800x533" alt="" style="padding: 0px; transform-origin: 50% 50% -600.159px; transform: rotateY(0.785398rad);">
<img src="https://source.unsplash.com/kG38b7CFzTY/800x533" alt="" style="padding: 0px; transform-origin: 50% 50% -600.159px; transform: rotateY(1.5708rad);">
<img src="https://source.unsplash.com/mCg0ZgD7BgU/800x533" alt="" style="padding: 0px; transform-origin: 50% 50% -600.159px; transform: rotateY(2.35619rad);">
<img src="https://source.unsplash.com/VkwRmha1_tI/800x533" alt="" style="padding: 0px; transform-origin: 50% 50% -600.159px; transform: rotateY(3.14159rad);">
<img src="https://source.unsplash.com/1FWICvPQdkY/800x533" alt="" style="padding: 0px; transform-origin: 50% 50% -600.159px; transform: rotateY(3.92699rad);">
<img src="https://source.unsplash.com/bjhrzvzZeq4/800x533" alt="" style="padding: 0px; transform-origin: 50% 50% -600.159px; transform: rotateY(4.71239rad);">
<img src="https://source.unsplash.com/7mUXaBBrhoA/800x533" alt="" style="padding: 0px; transform-origin: 50% 50% -600.159px; transform: rotateY(5.49779rad);">
</figure>Это будет нашим фундаментом.
Прежде чем смотреть в CSS, давайте рассмотрим план, который будет разработан в следующих разделах.
Эти <img> элементы должны быть расположены по кругу , очерченном карусели. Этот круг может быть аппроксимирован его ограниченным регулярным многоугольником и изображениями, расположенными по бокам:

Таким образом, количество сторон такого многоугольника совпадает с количеством изображений в карусели:
С тремя изображениями многоугольник является равносторонним треугольником;
С четырьмя изображениями это квадрат;
С пятью пятиугольниками; и так далее:

Полигон не может быть определен, и следующая процедура не может применяться как есть. Во всяком случае, случай с одним изображением довольно бесполезен; Два изображения несколько более вероятны, и их можно было разместить на двух диаметрально противоположных точках на круге. Для простоты эти особые случаи не обрабатываются и предполагается как минимум три изображения. Однако относительные модификации кода не были бы трудными.
Этот воображаемый опорный многоугольник будет располагаться в трехмерном пространстве, перпендикулярном плоскости окна просмотра, и его центр будет оттеснен назад в экран на расстоянии, равном его апотему, расстоянии стороны многоугольника от его центра, как показано на рис. Этот сверху вниз вид карусели:

Таким образом, сторона, которая в настоящее время обращена к зрителю, будет находиться на плоскости экрана при z = 0, а переднее изображение, не подверженное перспективой ракурса, будет иметь свой обычный 2D размер. d Письмо на картинке представляет значение для CSS perspective свойства.
В этом разделе я покажу вам ключевые правила CSS, которые я буду проходить шаг за шагом.
В следующих фрагментах кода некоторые переменные Sass используются, чтобы сделать компонент более настраиваемым. Я буду использовать, $n чтобы обозначить количество изображений в карусели и $item-width указать ширину изображения.
<figure> Элемент является содержащим окном для первого изображения и опорного элемента , вокруг которого другие изображения будут расположены и трансформированны.
Предположим, на данный момент, что у карусели было только одно изображение для демонстрации, я могу начать с калибровки и выравнивания:
.carousel {
display: flex;
flex-direction: column;
align-items: center;
> * {
flex: 0 0 auto;
}
.figure {
width: $item-width;
transform-style: preserve-3d;
img {
width: 100%;
&:not(:first-of-type) {
display: none /* Just for now */
}
}
}
}<figure> Элемент имеет заданную ширину карусельного элемента , и он имеет такую же высоту изображений (они могут иметь различные размеры , но они должны иметь одинаковое соотношение сторон). Таким образом, высота контейнера карусели адаптируется к высоте изображения. Кроме того, <figure> горизонтально центрируется в карусельном контейнере.
Первое изображение не нуждается в дополнительных преобразованиях, потому что оно уже находится в своем целевом положении, то есть на передней стороне карусели.
Карусель можно поворачивать в трехмерном пространстве, применяя преобразование вращения к <figure> элементу. Это вращение должно быть вокруг центра многоугольника, поэтому я изменю исходное происхождение преобразования <figure>:
.carousel figure {
transform-origin: 50% 50% (-$apothem);
}Это значение отрицается, потому что в CSS положительное направление оси z находится вне экрана, к зрителю. Скобки необходимы, чтобы избежать синтаксических ошибок Sass. Вычисление многоугольника apothem будет объяснено позже.
Переведя систему отсчета <figure> элемента, вся карусель может поворачиваться с вращением на (новой) оси y:
.carousel figure {
transform: rotateY(/* some amount here */rad);
}Я вернусь к деталям этого поворота позже.
Перейдем к преобразованиям для других изображений. При абсолютном позиционировании изображения складываются внутри <figure>:
.carousel figure img:not(:first-of-type) {
position: absolute;
left: 0;
top: 0;
}Эти z-index значения игнорируются , потому что это только предварительный шаг для следующих преобразований. Фактически, теперь каждое изображение можно поворачивать по оси y карусели на угол поворота, который зависит от стороны многоугольника, на которой назначено изображение. Во-первых, как это делается с <figure> элементом, изменяется исходное происхождение изображений по умолчанию, перемещая его в центр многоугольника:
.img:not(:first-of-type) {
transform-origin: 50% 50% (-$apothem);
}Затем изображения могут поворачиваться на их новой оси y на величину, заданную ($i — 1) * $theta радианами, где $i индекс (начиная с одного) изображения и $theta = 2 * $PI / $n, $PI представляя математическую константу pi . Следовательно, второе изображение будет повернуто $theta третьим 2 * $theta, и так далее, до последнего изображения, которое будет повернуто ($n — 1) * $theta.

Это относительное расположение изображений будет сохранено во время поворотов карусели (т.е. Вращения вокруг измененной оси y <figure>) благодаря иерархической природе вложенных преобразований CSS.
Это количество вращения каждого изображения может быть присвоено с помощью @for директивы управления Sass :
.carousel figure img {
@for $i from 2 through $n {
&:nth-child(#{$i}) {
transform: rotateY(#{($i - 1) * $theta}rad);
}
}
}Это использует for...through конструкцию, а не for...to потому, что вместо for...to последнего значения, присвоенного переменной индекса $i, будет n-1 вместо n.
Обратите внимание на два примера #{} синтаксиса интерполяции Sass . В первом случае он используется для индекса :nth-child() селектора;
Во втором случае он используется для установки значения свойства вращения.
Вычисление апофемы полигона зависит от количества сторон и ширины стороны, то есть, на $n и $item-width переменных.
Формула: $image-width / (2 * tan($PI/$n))
Где tan() — касательная тригонометрическая функция .
Эта формула может быть получена с некоторой геометрией и тригонометрией . В источнике пера эта формула не реализована так, как написано, потому что тангенциальная функция недоступна в Sass, поэтому вместо этого используется твердое значение. Формула будет полностью реализована в демонстрации JavaScript вместо этого.
На этом этапе изображения карусели «зашиты» бок о бок, образуя требуемую полигональную форму. Но здесь они плотно упакованы, а часто в трехмерных каруселях между ними есть пространство. Это расстояние повышает восприятие 3D-пространства, потому что оно позволяет вам видеть обратные изображения на задней части карусели.
Можно дополнительно добавить этот промежуток между изображениями, введя другую конфигурационную переменную $item-separation и используя ее в качестве горизонтальной прокладки для каждого <img> элемента. Точнее, взяв половину этого значения для левого и правого заполнения:
.carousel figure img {
padding: 0 $item-separation / 2;
}Изображения сделаны полупрозрачными с opacity свойством, чтобы лучше проиллюстрировать структуру карусели, а гибкая макета на корневом элементе карусели использовалась для вертикального центрирования ее в окне просмотра.
Чтобы облегчить тестирование поворота карусели, я собираюсь добавить элемент управления пользовательского интерфейса, чтобы перемещаться между изображениями. См. Демо-версию CodePen для HTML, CSS и JavaScript, реализующих этот элемент управления; Здесь я опишу только код, относящийся к вращению.
Мы используем currImage целочисленную переменную, указывающую, какое изображение находится впереди карусели. Когда пользователь взаимодействует с предыдущими / последующими кнопками, эта переменная увеличивается или уменьшается на единицу.
После обновления currImage, поворот карусели выполняется с помощью: figure.style.transform = `rotateY(${currImage * -theta}rad)`;
(Здесь и в следующих фрагментах литералы шаблонов ES6 используются для интерполяции выражений в строках, не стесняйтесь использовать традиционный оператор конкатенации «+», если вы предпочитаете)
Где theta то же самое, что и раньше:numImages = figure.childElementCount;theta = 2 * Math.PI / numImages;
Вращение происходит — theta из-за того, что для перехода к следующему пункту требуется вращение против часовой стрелки, и такие значения поворота отрицательны в преобразованиях CSS.
Обратите внимание, что это currImageзначение не ограничено диапазоном [0, numImages — 1], но вместо этого оно может расти бесконечно, как в положительном, так и в отрицательном направлении. На самом деле, если изображение на передней панели является последним (так currImage== n-1), и пользователь нажимает следующую кнопку, если мы переустановим currImageна 0, чтобы перейти к первому изображению карусели, произойдет переход Угол поворота от (n-1)*theta до 0, и это повернет карусель в противоположном направлении по всем предыдущим изображениям. Аналогичная проблема может возникнуть при нажатии кнопки prev, когда первое изображение является первым.
Чтобы быть разборчивым, я должен даже проверить потенциальные переполнения currentImage, потому что Number тип данных не может принимать сколь угодно большие значения. Эти проверки не реализованы в демо-коде.
Увидев базовый CSS, лежащий в основе карусели, теперь JavaScript можно использовать для улучшения компонента несколькими способами, такими как:
Сначала я удаляю из таблицы стилей переменные и правила, связанные с происхождением преобразования и поворотами, потому что это будет сделано с использованием JavaScript:
$item-width: 40%; // Now we can use percentages
$item-separation: 0px; // This now is set with Js
$viewer-distance: 500px;
.carousel {
padding: 20px;
perspective: $viewer-distance;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
> * {
flex: 0 0 auto;
}
figure {
margin: 0;
width: $item-width;
transform-style: preserve-3d;
transition: transform 0.5s;
img {
width: 100%;
box-sizing: border-box;
padding: 0 $item-separation / 2;
&:not(:first-of-type) {
position: absolute;
left: 0;
top: 0;
}
}
}
}Далее в скрипте есть carousel() функция, которая выполняет инициализацию экземпляра:
function carousel(root) {
// coming soon...
}root Аргумент относится к элементу DOM , который содержит карусель.
Обычно эта функция будет конструктором, чтобы генерировать один объект для каждой карусели на странице, но здесь я не пишу карусельную библиотеку, поэтому достаточно простой функции.
Чтобы создать несколько компонентов на одной странице, код ожидает, что все изображения будут загружены, зарегистрировав слушателя на объекте окна для loadсобытия, а затем вызовет carousel() для каждого элемента с carouselклассом:
window.addEventListener('load', () => {
var carousels = document.querySelectorAll('.carousel');
for (var i = 0; i < carousels.length; i++) {
carousel(carousels[i]);
}
});carousel() Выполняет три основные задачи:
Перед рассмотрением кода настройки преобразования я рассмотрю некоторые ключевые переменные и как они инициализируются на основе конфигурации экземпляра:
var
figure = root.querySelector('figure'),
images = figure.children,
n = images.length,
gap = root.dataset.gap || 0,
bfc = 'bfc' in root.dataset
;Количество изображений (n) инициализируется в зависимости от количества дочерних элементов <figure> элемента. Разделение между слайдами (gap), инициализируется из data-gap атрибута HTML5 , если установлено. Флаг видимости обратной стороны ( bfc) читается с использованием API набора данных HTML5. Это будет использоваться позже, чтобы определить, должны ли изображения на обратной стороне карусели быть видимыми или нет.
Код, который устанавливает свойства, связанные с преобразованиями CSS, инкапсулируется в setupCarousel(). Эта вложенная функция принимает два аргумента. Первое — количество элементов в карусели, то есть nвведенная выше переменная. Второй параметр s- длина стороны многоугольника карусели. Как я упоминал ранее, это равно ширине изображений, поэтому можно прочитать текущую ширину одного из них getComputedStyle():
setupCarousel(n, parseFloat(getComputedStyle(images[0]).width));Таким образом, ширина изображения может быть задана с помощью значений процентов.
Чтобы сохранить отзыв карусели, я регистрирую слушателя для resize события окна, который setupCarousel() снова вызывает изменения (в конечном итоге) измененных размеров изображений:
window.addEventListener('resize', () => {
setupCarousel(n, parseFloat(getComputedStyle(images[0]).width));
});Для простоты я не разбираюсь в слушателе изменения размера.
Первое, что setupCarousel() нужно сделать, — вычислить apothem многоугольника, используя переданные параметры и ранее обсуждавшуюся формулу:apothem = s / (2 * Math.tan(Math.PI / n));
Затем это значение используется для изменения начала преобразования фигурного элемента для получения новой оси вращения карусели:figure.style.transformOrigin = `50% 50% ${-apothem}px`;
Затем применяются стили для изображений:
for (var i = 0; i < n; i++) {
images[i].style.padding = `${gap}px`;
}
for (i = 1; i < n; i++) {
images[i].style.transformOrigin = `50% 50% ${- apothem}px`;
images[i].style.transform = `rotateY(${i * theta}rad)`;
}
if (bfc) {
for (i = 0; i < n; i++) {
images[i].style.backfaceVisibility = 'hidden';
}
}Первый цикл присваивает пробел для пространства между элементами карусели. Второй цикл устанавливает 3D-преобразования. Последний цикл обрабатывает обратные грани, если соответствующий флаг был указан в конфигурации карусели.
Наконец, rotateCarousel() вызывается, чтобы перенести текущее изображение на передний план. Это небольшая вспомогательная функция, которая, учитывая индекс отображаемого изображения, поворачивает фигурный элемент по оси Y для перемещения целевого изображения вперед. Он также используется навигационным кодом для перехода туда и обратно:
function rotateCarousel(imageIndex) {
figure.style.transform = `rotateY(${imageIndex * -theta}rad)`;
}Вот окончательный результат, демонстрация, в которой создаются несколько каруселей, каждая из которых имеет другую конфигурацию:
Напиши комментарий и поделись своим мнением, все ли у тебя получилось, какие сложности возникли или просто напиши спасибо за статью
Не хотите разбираться с кодом самостоятельно? Я могу установить, настроить и адаптировать решение под ваш сайт или разработать аналогичную функциональность под ваши требования.
Обратиться за помощью
Ещё бы добавить свайп
Отличная идея
Ели нашел кнопку «исходники»
очень жаль, что сие не работает в ie
ie уже уходит в небытие, пользуйтесь современными браузерами