Подход 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 способами, которые не одобрит Бог.