Раскладка изображений мозайкой
Задача разложить изображения в ряд мозайкой как это делают на https://www.freepik.com
Особенности:
- Изображения всегда заполняют всю ширину экрана
- Изображения выстраиваются в линии, при этом высота каждой линии отличается
- Высота, ширина изображений выбирается не случайным образом, а максимально оптимально
- Flex, а не абсолютное позиционирование
- Изначально известен путь до изображения и его размеры (ширина, высота)
- Исходные изображения или квадратные или прямоугольные, ширина всех всегда одинаковая (как на фрипике - 626px), а высота может отличаться.
Библиотеки вроде masonry (https://masonry.desandro.com), isotope (https://isotope.metafizzy.co) слишком монструозные, используют абсолютное позиционирование и не решают задачу всё равно.
Не понимаю, что взять за основу расчётов, ведь неизвестных несколько: неизвестно сколько изображений нужно добавить в ряд чтобы полностью заполнить ряд, а чтобы это понять нужно определить оптимальную высоту ряда. При этом само собой, на разных размерах экранов, разная оптимальная высота и количество изображений в ряду. Очевидно, что отталкиваться нужно от ширины/высоты оригинальных изображений и ширины/высоты экрана, но как именно для меня пока не понятно.
Вот пример с добавленными изображениями и выведенными размерами в атрибуты для удобства. Нужно задать оптимально ширину и высоту каждого элемента .image
.container {
padding: 0 10px;
}
.row {
margin: 0 -5px;
display: flex;
flex-wrap: wrap;
align-items: flex-start;
}
.image {
margin: 0 5px 10px 5px;
}
.image img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="container">
<div class="row">
<div class="image" data-w="626" data-h="312">
<img src="https://image.freepik.com/free-vector/enjoy-summer-3d-realistic-background-with-clouds-daisies-grass-leaves-product-podium_87521-3206.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="521">
<img src="https://image.freepik.com/free-vector/linear-flat-wedding-monograms_52683-64319.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="442">
<img src="https://image.freepik.com/free-vector/flat-car-poster-with-photo-horizontal_52683-64510.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="417">
<img src="https://image.freepik.com/free-vector/gradient-grainy-gradient-shapes_23-2148975080.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="286">
<img src="https://image.freepik.com/free-photo/city-tornado-doomsday-scene-illustration_456031-22.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="417">
<img src="https://image.freepik.com/free-vector/gradient-grainy-gradient-texture_23-2148974472.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="417">
<img src="https://image.freepik.com/free-vector/hand-drawn-blackboard-coffee-collection_79603-1654.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="468">
<img src="https://image.freepik.com/free-photo/person-putting-medical-mask-earth_23-2148984685.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="626">
<img src="https://image.freepik.com/free-psd/3d-space-rocket-with-smoke_23-2148938939.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="417">
<img src="https://image.freepik.com/free-vector/gradient-grainy-gradient-shape-set_23-2148971570.jpg" alt="">
</div>
</div>
</div>
Ответы (2 шт):
Здесь пример как можно сделать по два элемента в ряд, например (подгонка под малое окно просмотра). А так должен быть алгоритм, который рассматривает несколько изображений в ряд в зависимости от ширины контейнера, учитывая пределы возможного уменьшения изображений. Если нет - на одно изображение в ряд меньше.
const MARGINS = 60; // Margins, paddings, полоса прокрутки - на глаз
let image_arr = [...document.querySelectorAll('.image')]; // массив всех контейнеров изображений (боксов)
let obj_arr = image_arr.map(image => { // копия массива, но с более удобными для работы свойствами
return {w: image.getAttribute('data-w'), h: image.getAttribute('data-h')};
});
let width = document.querySelector('.container').getBoundingClientRect().width - MARGINS; // Ширина общего контейнера для расчетов
let counter = 0;
changeSize = () => {
let images = obj_arr.slice(counter, counter+2); // Берем по два бокса
// Здесь берем меньшую по высоте картинку и изменяем вторую до этой же высоты, а ширину по коэффициенту для сохранения соотношения
let lowest = images.indexOf(images.sort((a,b) => a - b)[0]);
let k1 = images[lowest].h/images[1-lowest].h;
images[1-lowest].h *= k1;
images[1-lowest].w *= k1;
// Здесь складываем полученную ширину двух картинок и подгоняем под ширину контейнера
let imagesW = images.reduce((acc,cur) => acc+= +cur.w, 0);
let k2 = width/imagesW;
images[lowest].w *= k2;
images[lowest].h *= k2;
images[1-lowest].w *= k2;
images[1-lowest].h *= k2;
counter += 2;
}
// Перебираем массив попарно
for (let i = 0; i < image_arr.length - 1; i+=2){
changeSize();
}
// Назначаем размеры боксам соответственно рабочему массиву объектов.
image_arr.forEach((image,i) => {
image.style.width = obj_arr[i].w + 'px';
image.style.height = obj_arr[i].h + 'px';
})
.container {
padding: 0 10px;
}
.row {
margin: 0 -5px;
display: flex;
flex-wrap: wrap;
align-items: flex-start;
}
.image {
margin: 0 5px 10px 5px;
}
.image img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="container">
<div class="row">
<div class="image" data-w="626" data-h="312">
<img src="https://image.freepik.com/free-vector/enjoy-summer-3d-realistic-background-with-clouds-daisies-grass-leaves-product-podium_87521-3206.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="521">
<img src="https://image.freepik.com/free-vector/linear-flat-wedding-monograms_52683-64319.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="442">
<img src="https://image.freepik.com/free-vector/flat-car-poster-with-photo-horizontal_52683-64510.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="417">
<img src="https://image.freepik.com/free-vector/gradient-grainy-gradient-shapes_23-2148975080.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="286">
<img src="https://image.freepik.com/free-photo/city-tornado-doomsday-scene-illustration_456031-22.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="417">
<img src="https://image.freepik.com/free-vector/gradient-grainy-gradient-texture_23-2148974472.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="417">
<img src="https://image.freepik.com/free-vector/hand-drawn-blackboard-coffee-collection_79603-1654.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="468">
<img src="https://image.freepik.com/free-photo/person-putting-medical-mask-earth_23-2148984685.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="626">
<img src="https://image.freepik.com/free-psd/3d-space-rocket-with-smoke_23-2148938939.jpg" alt="">
</div>
<div class="image" data-w="626" data-h="417">
<img src="https://image.freepik.com/free-vector/gradient-grainy-gradient-shape-set_23-2148971570.jpg" alt="">
</div>
</div>
</div>
На freepik.com используется opensource-библиотека JavaScript-flexImages от разработчиков Pixabay. Она легковесная - весит всего 2кб.
Там же можно ознакомится с алгоритмом работы - меньше сотни строк кода (: