Układ murarski oparty tylko na CSS

143

Muszę zaimplementować dość spływy układ murarski. Jednak z wielu powodów nie chcę do tego używać JavaScript.

Siatka złożona z wielu kolumn prostokątów o różnej wysokości.

Parametry:

  • Wszystkie elementy mają tę samą szerokość
  • Elementy mają wysokość, której nie można obliczyć po stronie serwera (obraz plus różne ilości tekstu)
  • Jeśli zajdzie taka potrzeba, mogę żyć ze stałą liczbą kolumn

jest rozwiązanie trywialne do tego, że działa w nowoczesnych przeglądarek, w column-countnieruchomości.

Problem z tym rozwiązaniem polega na tym, że elementy są uporządkowane w kolumnach:

Zaczynając od górnego lewego pola, są ponumerowane od 1 do 4 prosto w dół, najwyższe pole w następnej kolumnie to 5 i tak dalej.

Chociaż potrzebuję elementów do zamówienia w rzędach, przynajmniej w przybliżeniu:

Zaczynając od górnego lewego pola, są ponumerowane od 1 do 6 prosto w poprzek, ale ponieważ pole 5 jest najkrótsze, pole pod nim ma 7, ponieważ wygląda na to, że znajduje się o rząd wyżej niż następne pole po lewej stronie.

Podejścia, które wypróbowałem, nie działają:

Teraz mógł zmienić renderowania po stronie serwera i uporządkować elementy podzielenie liczby pozycji przez liczbę kolumn, ale to skomplikowane, podatne na błędy (na podstawie sposobu przeglądarek decydują się podzielić z listy element do kolumn), więc chciałbym aby tego uniknąć, jeśli to możliwe.

Czy jest jakaś nowomodna magia flexbox, która to umożliwia?

Pekka
źródło
7
Nie mogę wymyślić sposobu, który nie zależy od predefiniowanych wysokości. Jeśli zastanowisz się ponownie nad JS, zajrzyj na stackoverflow.com/questions/13518653/ ... gdzie wdrażam takie rozwiązanie, które jest dość proste.
Gabriele Petrioli
1
@Gaby wdraża teraz Twoje rozwiązanie, dziękuję!
Pekka
4
Zdaję sobie sprawę, że powiedziałeś tylko o CSS. Chcę tylko wspomnieć, że Masonry nie wymaga już jQuery - zminimalizowana biblioteka ma mniej niż 8kb - i może zostać zainicjowana samym html. Tylko w celach informacyjnych jsfiddle.net/wp7kuk1t
I haz
1
Jeśli możesz określić wysokość elementów z wyprzedzeniem, znając wysokość linii, rozmiar czcionki (musiałbyś podać określoną czcionkę i wykonać sprytne obliczenia), wysokość obrazu, margines wertykalny i wypełnienie, możesz Zrób to. W przeciwnym razie nie możesz tego zrobić używając tylko CSS. Możesz również użyć czegoś takiego jak PhantomJS, aby wstępnie wyrenderować każdy element i uzyskać wysokość tego elementu, ale dodano by znaczące obciążenie / opóźnienie.
Timothy Zorn,

Odpowiedzi:

169

Flexbox

Dynamiczny układ muru nie jest możliwy w przypadku flexbox, przynajmniej nie w czysty i wydajny sposób.

Flexbox to jednowymiarowy system układu. Oznacza to, że może wyrównywać elementy wzdłuż linii poziomych LUB pionowych. Element flex jest ograniczony do swojego wiersza lub kolumny.

Prawdziwy system siatki jest dwuwymiarowy, co oznacza, że ​​może wyrównywać elementy wzdłuż linii poziomych ORAZ pionowych. Elementy treści mogą jednocześnie obejmować wiersze i kolumny, czego nie mogą robić elementy flex.

Dlatego flexbox ma ograniczoną zdolność do budowania kratek. Jest to również powód, dla którego W3C opracowało inną technologię CSS3, Grid Layout .


row wrap

W kontenerze elastycznym z flex-flow: row wrapelementami elastycznymi należy zawijać do nowych rzędów .

Oznacza to, że element flex nie może być zawijany pod innym elementem w tym samym wierszu .

Zauważ powyżej, jak div # 3 zawija się poniżej div # 1 , tworząc nowy wiersz. Nie może zawijać się pod div # 2 .

W rezultacie, gdy przedmioty nie są najwyższe w rzędzie, pozostaje biała przestrzeń, tworząc brzydkie luki.


column wrap

Jeśli przełączysz się na flex-flow: column wrap, bardziej osiągalny jest układ przypominający siatkę. Jednak kontener w kierunku kolumn ma od razu cztery potencjalne problemy:

  1. Elementy Flex przepływają pionowo, a nie poziomo (tak jak potrzebujesz w tym przypadku).
  2. Kontener rozwija się w poziomie, a nie w pionie (jak układ Pinterest).
  3. Wymaga, aby pojemnik miał stałą wysokość, aby przedmioty wiedziały, gdzie owinąć.
  4. W chwili pisania tego tekstu ma on wady we wszystkich głównych przeglądarkach, w których kontener nie rozszerza się, aby pomieścić dodatkowe kolumny .

W rezultacie kontener w kierunku kolumn nie jest opcją w tym przypadku, a także w wielu innych przypadkach.


Siatka CSS z wymiarami pozycji undefined

Układ siatki byłby idealnym rozwiązaniem problemu, gdyby można było z góry określić różne wysokości elementów treści . Wszystkie inne wymagania mieszczą się w granicach możliwości Gridu.

Szerokość i wysokość elementów siatki musi być znana, aby zamknąć luki między otaczającymi elementami.

Tak więc Grid, który jest najlepszym CSS, jaki ma do zaoferowania do tworzenia poziomego układu murów, nie spełnia w tym przypadku problemów.

W rzeczywistości, dopóki nie pojawi się technologia CSS z możliwością automatycznego zamykania luk, CSS ogólnie nie ma rozwiązania. Coś takiego prawdopodobnie wymagałoby ponownego załadowania dokumentu, więc nie jestem pewien, czy byłoby to użyteczne lub wydajne.

Będziesz potrzebował scenariusza.

Rozwiązania JavaScript mają tendencję do stosowania pozycjonowania bezwzględnego, które usuwa elementy treści z przepływu dokumentu w celu ponownego uporządkowania ich bez przerw. Oto dwa przykłady:


Siatka CSS ze zdefiniowanymi wymiarami pozycji

W przypadku układów, w których znana jest szerokość i wysokość elementów treści, oto poziomy układ masonry w czystym CSS:

grid-container {
  display: grid;                                                /* 1 */
  grid-auto-rows: 50px;                                         /* 2 */
  grid-gap: 10px;                                               /* 3 */
  grid-template-columns: repeat(auto-fill, minmax(30%, 1fr));   /* 4 */
}

[short] {
  grid-row: span 1;                                             /* 5 */
  background-color: green;
}

[tall] {
  grid-row: span 2;
  background-color: crimson;
}

[taller] {
  grid-row: span 3;
  background-color: blue;
}

[tallest] {
  grid-row: span 4;
  background-color: gray;
}

grid-item {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.3em;
  font-weight: bold;
  color: white;
}
<grid-container>
  <grid-item short>01</grid-item>
  <grid-item short>02</grid-item>
  <grid-item tall>03</grid-item>
  <grid-item tall>04</grid-item>
  <grid-item short>05</grid-item>
  <grid-item taller>06</grid-item>
  <grid-item short>07</grid-item>
  <grid-item tallest>08</grid-item>
  <grid-item tall>09</grid-item>
  <grid-item short>10</grid-item>
  <grid-item tallest>etc.</grid-item>
  <grid-item tall></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item tallest></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item tallest></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
</grid-container>

jsFiddle demo


Jak to działa

  1. Utwórz kontener siatki na poziomie bloku. ( inline-gridbyłaby inna opcja)
  2. grid-auto-rowsWłaściwość ustala wysokość automatycznie generowanych wierszy. W tej siatce każdy wiersz ma 50 pikseli wysokości.
  3. grid-gapNieruchomość jest skrótem grid-column-gapi grid-row-gap. Ta reguła ustawia odstęp 10 pikseli między elementami siatki. (Nie dotyczy obszaru między przedmiotami a pojemnikiem).
  4. grid-template-columnsWłaściwość określa szerokość wyraźnie zdefiniowanych kolumnach.

    repeatOznaczenie określa wzór powtarzających kolumn lub rzędów ().

    auto-fillFunkcja opowiada siatki wyrównać taką liczbę kolumn (lub wierszy), jak to możliwe bez przepełnienia pojemnika. (Może to spowodować podobne zachowanie do układu flex flex-wrap: wrap).

    minmax()Funkcji wyznacza zakres maksymalnej i minimalnej wielkości dla każdej kolumny (lub wiersza). W powyższym kodzie szerokość każdej kolumny będzie wynosić minimum 30% kontenera i maksimum wolnego miejsca.

    frUrządzenie stanowi część wolnej przestrzeni w zbiorniku siatki. Jest to porównywalne z flex-growwłaściwością flexboxa .

  5. Za pomocą grid-rowi spanmówimy elementom siatki, ile wierszy powinny obejmować.


Wsparcie przeglądarek dla siatki CSS

  • Chrome - pełne wsparcie od 8 marca 2017 r. (Wersja 57)
  • Firefox - pełne wsparcie od 6 marca 2017 r. (Wersja 52)
  • Safari - pełne wsparcie od 26 marca 2017 r. (Wersja 10.1)
  • Edge - pełne wsparcie od 16 października 2017 r. (Wersja 16)
  • IE11 - brak wsparcia dla aktualnej specyfikacji; obsługuje przestarzałą wersję

Oto pełny obraz: http://caniuse.com/#search=grid


Fajna funkcja nakładki siatki w przeglądarce Firefox

W narzędziach programistycznych Firefox dla deweloperów podczas sprawdzania kontenera siatki w deklaracji CSS znajduje się mała ikona siatki. Po kliknięciu wyświetla zarys twojej siatki na stronie.

Więcej szczegółów tutaj: https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts

Michael Benjamin
źródło
6
Fantastyczna odpowiedź! Czy dzięki rozwiązaniu „Siatka CSS ze zdefiniowanymi wymiarami elementów” można wyrazić wysokość komórki jako procent szerokości komórki? Byłoby to przydatne do wyświetlania obrazów, w których znany jest współczynnik proporcji. Chcemy zawsze zachować proporcje.
Oliver Joseph Ash
Dzięki. Jeśli chodzi o problem ze współczynnikiem proporcji obrazów, metodę „procentu szerokości komórki” (sztuczka z wypełnieniem procentowym?), Nie jest ona niezawodna w Grid lub Flexbox. Zobacz tutaj i tutaj . @OliverJosephAsh
Michael Benjamin
Tak, mam na myśli sztuczkę z wypełnieniem procentowym. Czy można zastosować którekolwiek z obejść w połączeniu z rozwiązaniem dotyczącym układu muru? Ze względów kontekstowych zamierzamy zaimplementować układ masonry tylko dla CSS dla obrazów na unsplash.com .
Oliver Joseph Ash,
1
Niestety używanie JS do tego nie wchodzi w grę. Chcemy włączyć renderowanie po stronie serwera ze względu na wydajność i SEO. Oznacza to, że układ musi zostać wyrenderowany przed pobraniem, przeanalizowaniem i wykonaniem kodu JavaScript po stronie klienta. Biorąc pod uwagę, że wydaje się to niemożliwe, myślę, że gdzieś po drodze będziemy musieli znaleźć kompromis! Dzięki za pomoc :-)
Oliver Joseph Ash
2
Aktualizacja specyfikacji: W Flexbox, procentowe marginesy i dopełnienia są teraz ustawione tak, aby ponownie rozwiązać rozmiar wbudowanego kontenera. drafts.csswg.org/css-flexbox/#item-margins @OliverJosephAsh
Michael Benjamin
15

Jest to niedawno odkryta technika wykorzystująca flexbox: https://tobiasahlin.com/blog/masonry-with-css/ .

Artykuł ma dla mnie sens, ale nie próbowałem go używać, więc nie wiem, czy są jakieś zastrzeżenia, poza wymienionymi w odpowiedzi Michaela.

Oto próbka z artykułu wykorzystująca orderwłaściwość w połączeniu z :nth-child.

Fragment stosu

.container {
  display: flex;
  flex-flow: column wrap;
  align-content: space-between;
  /* Your container needs a fixed height, and it 
   * needs to be taller than your tallest column. */
  height: 960px;
  
  /* Optional */
  background-color: #f7f7f7;
  border-radius: 3px;
  padding: 20px;
  width: 60%;
  margin: 40px auto;
  counter-reset: items;
}

.item {
  width: 24%;
  /* Optional */
  position: relative;
  margin-bottom: 2%;
  border-radius: 3px;
  background-color: #a1cbfa;
  border: 1px solid #4290e2;
  box-shadow: 0 2px 2px rgba(0,90,250,0.05),
    0 4px 4px rgba(0,90,250,0.05),
    0 8px 8px rgba(0,90,250,0.05),
    0 16px 16px rgba(0,90,250,0.05);
  color: #fff;
  padding: 15px;
  box-sizing: border-box;
}

 /* Just to print out numbers */
div.item::before {
  counter-increment: items;
  content: counter(items);
}

/* Re-order items into 3 rows */
.item:nth-of-type(4n+1) { order: 1; }
.item:nth-of-type(4n+2) { order: 2; }
.item:nth-of-type(4n+3) { order: 3; }
.item:nth-of-type(4n)   { order: 4; }

/* Force new columns */
.break {
  flex-basis: 100%;
  width: 0;
  border: 1px solid #ddd;
  margin: 0;
  content: "";
  padding: 0;
}

body { font-family: sans-serif; }
h3 { text-align: center; }
<div class="container">
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 190px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 120px"></div>
  <div class="item" style="height: 160px"></div>
  <div class="item" style="height: 180px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 150px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 190px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 120px"></div>
  <div class="item" style="height: 160px"></div>
  <div class="item" style="height: 180px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 150px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 170px"></div>
  
  <span class="item break"></span>
  <span class="item break"></span>
  <span class="item break"></span>
</div>

Oliver Joseph Ash
źródło
Po pierwsze, odpowiedzi tylko linków są złe, ponieważ kiedy takie łącze umiera, to samo dotyczy odpowiedzi. Po drugie, jeśli uważniej przeczytasz udzieloną odpowiedź, przekonasz się, że to, co zostało wspomniane w Twoim linku, zostało uwzględnione. Mówiąc najprościej, istnieje zbyt wiele ograniczeń związanych z używaniem samego Flexboxa, chyba że to, co zostało wspomniane powyżej, nie stanowi problemu.
Ason
3
Bardzo dokładnie przeczytałem zaakceptowaną odpowiedź - zobaczysz nawet kilka komentarzy, które dodałem na dole. Podejście flexbox, z którym się połączyłem, jest wyjątkowe i nie jest objęte tą odpowiedzią. Nie chcę powtarzać artykułu, ponieważ jest bardzo skomplikowany, a artykuł świetnie to opisuje.
Oliver Joseph Ash
Jedyną rzeczą, którą link robi inaczej, jest wykorzystanie orderwłaściwości, dzięki czemu wygląda ona tak, jakby elementy przepływały od lewej do prawej. Jednak jego główna sztuczka polega na tym flex-flow: column wrap, o czym wspomina powyższa odpowiedź. Ponadto nie może przepływać elementów tak, jak robi to prawdziwy mur, np. Jeśli trzeci i czwarty element zmieściłyby się w trzeciej kolumnie, to połączone nie. Ale znowu, jak powiedziałem, wszystko zależy od wymagań, aw tym przypadku (to pytanie) nie zadziała tak, jak się prosi.
Ason
Co więcej, nie chodzi o to, że „nie chcesz powtarzać artykułu” , odpowiedź powinna zawierać zasadniczą część kodu, więc będzie nadal obowiązywać, gdy powiązany zasób umrze.
Ason
3
Zaktualizowałem go, dodałem próbkę z artykułu + głos za.
Ason