Подход Vue.js к масонству
Мэнсплейнинг-каменная кладка (и как моя бабушка научила меня этому рецепту)
Если вы создаете фотогалерею в Vue, вы, возможно, пробовали макет «Masonry», в котором фотографии укладываются в столбцы с равными промежутками между ними, и когда количество столбцов изменяется в соответствии с размером окна, изображения вверху оставайтесь наверху. Я попытался использовать MagicGrid, специальный пакет Vue для каменной кладки, но обнаружил, что при первой загрузке он накладывает изображения друг на друга (возможно, моя вина в том, что он загружает сканы 4000x6000). Вместо этого я понял, что есть довольно простой способ (почти) добиться каменной кладки — с минимальным явным javascript.
Фактический код
<div class="gallery-column" v-for="ci in Math.ceil(((windowWidth*0.9)-320) / 400)" :key="ci"> <img :src="photo.url" v-on:click="showLargeImage(photo.url)" v-for="(photo, index) in photos.filter((p,pi) => ((pi+1-ci)%Math.ceil(((windowWidth*0.9)-320) / 400))===0)" class="gallery-image" :key="index"/> </div>
Не обращая внимания на мои грубые расчеты и ужасные соглашения об именах… вот оно. Просто как. Вот разбивка: используя v-for, вы создаете соответствующее количество столбцов в зависимости от ширины окна и ширины изображений. Для меня изображения были шириной 400 пикселей, поэтому я взял ширину окна и выполнил math.ceil деление на 400 (наряду с некоторыми другими вычислениями, чтобы соответствовать внешним границам и внешнему виду, который я хотел), чтобы получить количество столбцов, которые должны показывать. В каждом столбце я хотел сгенерировать соответствующие изображения: в столбце 1 было бы первое изображение, в столбце 2 было бы второе и т. д., и самое главное, если бы столбцов было 4, то изображение 5 отображалось бы как второе изображение в столбец 1. Похоже, что V-if не работает с v-for, поэтому вместо этого я просто отфильтровал список фотографий для каждого v-if на основе столбца, чтобы в столбце 1 в макете из 4 столбцов отображались изображения 1, 5,9,13 в таком порядке, а в макете из 3 столбцов будут отображаться изображения 1,4,7,10.
Математика (🤮)
(p,pi) => (pi+1-ci)%Math.ceil(((windowWidth*0.9)-320) / 400) === 0
p=фото, pi=индекс фото и ci=индекс столбца. Чтобы убедиться, что мы находимся в правильном столбце, вы берете индекс фотографии и вычитаете индекс столбца. Если это число делится поровну на количество столбцов, то вы находитесь в правильном столбце. Объясню более подробно: если фото находится в столбце c, то оно будет иметь индекс c, затем c+cn, затем c+2cn и т. д., где cn — количество столбцов, потому что нам придется размещать фото в каждом втором столбец перед возвратом в столбец c, следовательно, cn. Таким образом, вы можете получить индекс фотографий, которые должны быть в столбце c, сказав, что индекс фотографии p за вычетом индекса столбца будет без остатка делиться на cn. Итак, мы получаем, что p-c%cn = 0. Почему +1? Когда vue выполняет v-for для числа (v-for=”i in 10), он возвращает i как 1,2,3… поэтому мы учитываем тот факт, что он начинает отсчет с 1, а не с 0.
JavaScript (🤮🤮)
Если вы здесь, потому что пропустили математический раздел, молодцы, я ужасно объясняю математику. В любом случае, последнее, что нам нужно сделать, это получить значение windowWidth, которое является единственным javascript, который нам действительно нужен. Для этого добавьте следующее в раздел Экспорт по умолчанию (любезно предоставлено Samayo on StackOverflow)
data() { return { windowWidth: window.innerWidth, } }, methods: { onResize() { this.windowWidth = window.innerWidth }, }, beforeDestroy() { window.removeEventListener('resize', this.onResize); }, mounted() { this.$nextTick(() => { window.addEventListener('resize', this.onResize); }) }
Бада-бинг, бада-бум, мы закончили. Лично моя бесконечная любовь Бруно Марса к flexbox — где я мог поймать за него гранату, но она, будучи моделью веб-макета css 3, не делала того же — заставила меня сделать вертикальный столбец гибким, как ну, в котором все, что вам действительно нужно сделать, это убедиться, что вы используете justify-content: flex-start, чтобы все было наверху. Еще одна вещь, которую вы можете сделать, это установить ширину ваших столбцов на 100%, чтобы они изменялись по мере изменения размера окна, прежде чем оно достигнет порога для добавления другого столбца.
ВНИМАНИЕ:
Так что есть одно предостережение: на самом деле это не кирпичная кладка. Я лгал все это время. Если в столбце 1 есть набор изображений высотой 100 пикселей, а в столбце 2 — набор изображений высотой 1000 пикселей, у вас будут столбцы с перекосами, которые заканчиваются далеко друг от друга. Эта упрощенная версия не знает, какая колонна самая короткая, какая кладка будет учитываться в настоящей кладке. В будущем я собираюсь поработать над исправлением этого и посмотреть, не смогу ли я найти выход из слишком большого количества JavaScript и position: absolute; либо предварительно загрузив изображение heights и выполняя некоторые быстрые математические действия, или просто проверяя высоту каждого столбца во время каждого цикла, а затем помещая изображения, сгенерированные v-for, в следующее место столбцов, сгенерированных v-for. Как это будет работать, я понятия не имею, но моя цель — заставить @LinusBorg попросить меня удалить это за то, что это вызвало приток людей, использующих v-for способами, которые не одобрит Бог.