Wygeneruj powtarzający się wzór sześciokątny za pomocą CSS3

79

Muszę więc stworzyć powtarzający się sześciokątny wzór, używając CSS. Jeśli potrzebne są obrazy, mogę tam wejść, ale wolałbym po prostu użyć CSS, jeśli to możliwe.

Oto pomysł na to, co próbuję stworzyć:

wprowadź opis obrazu tutaj

Po prostu potrzebuję sposobu na utworzenie sześciokątnych kształtów, a następnie nałożenie na nie tekstu / obrazów. Nie mam jeszcze dużo kodu, ponieważ nie bardzo wiem, od czego zacząć. Problem w tym, że mógłbym po prostu użyć <div>s w kształcie sześciokąta, jak pokazano na ( http://css-tricks.com/examples/ShapesOfCSS/ ), ale wtedy nie będą się łączyć. Mógłbym użyć powtarzającego się wzoru sześciokąta, ale wtedy nie byłbym w stanie określić dokładnej lokalizacji potrzebnego tekstu lub obrazów w określonych kształtach. Z góry dziękuję za pomoc.

element119
źródło
2
Są maski CSS, ale wsparcie jest straszne: webkit.org/blog/181/css-masks
mddw
Aby uzyskać ten sam wzór sześciokąta, używając <img>tagu zamiast obrazu tła, możesz sprawdzić Siatkę sześciokątów za pomocą tagu <img> .
web-tiki
Stworzyłem prostą wersję rozwiązania ScottS, aby szybko uzyskać różne rozmiary. Sprawdź tutaj codepen.io/mort3za/pen/wBabaB
Morteza Ziyae

Odpowiedzi:

130

(Chociaż odpowiedź Any nadeszła kilka miesięcy po mojej, prawdopodobnie wykorzystując moją jako podstawę do „myślenia”, fakt, że była w stanie wymyślić metodę wykorzystującą singiel, divjest wart promowania, więc sprawdź też jej odpowiedź - ale Zwróć uwagę, że zawartość na heksie jest bardziej ograniczona.)

To było naprawdę niesamowite pytanie. Dziękuję, że o to zapytałeś. Wspaniałe jest to, że:

To skrzypce udowadnia, że ​​możesz to zrobić!

Oryginalne skrzypce używane (zmodyfikowane w późniejszej edycji do linku skrzypcowego powyżej) - wykorzystywały obrazy imgur.com, które nie wydawały się bardzo niezawodne w ładowaniu, więc nowe skrzypce używają photobucket.com ( daj mi znać, jeśli są trwałe problemy z ładowaniem obrazu ). Mam zachował pierwotną więź, ponieważ poniżej kod który idzie z wyjaśnieniem (istnieje kilka różnic background-sizelub positiondo nowego skrzypce).

Pomysł przyszedł mi do głowy niemal natychmiast po przeczytaniu twojego pytania, ale jego wdrożenie zajęło trochę czasu. Początkowo próbowałem uzyskać pojedynczy „hex” z jednym divi tylko pseudoelementami, ale najlepiej, jak mogłem powiedzieć, nie było sposobu, aby po prostu obrócić background-image(czego potrzebowałem), więc musiałem dodać kilka dodatkowych divelementów, aby uzyskać właściwą / lewe boki hexa, abym mógł wtedy użyć pseudoelementów jako środka background-imageobrotu.

Testowałem w IE9, FF i Chrome. Teoretycznie każda przeglądarka obsługująca CSS3, w transformktórej powinna działać.

Pierwsza główna aktualizacja (dodano wyjaśnienie)

Mam teraz trochę czasu, aby opublikować wyjaśnienie kodu, więc oto:

Po pierwsze, sześciokąty są definiowane przez relacje 30/60 stopni i trygonometrię, więc będą to kluczowe kąty. Po drugie, zaczynamy od „wiersza”, w którym ma się znajdować siatka szesnastkowa. Kod HTML jest zdefiniowany jako (dodatkowe divelementy pomagają zbudować hex):

<div class="hexrow">
    <div>
        <span>First Hex Text</span>
        <div></div>
        <div></div>
    </div>
    <div>
        <span>Second Hex Text</span>
        <div></div>
        <div></div>
    </div>
    <div>
        <span>Third Hex Text</span>
        <div></div>
        <div></div>
    </div>
</div>

Będziemy używać inline-blocksześciokąta display, ale nie chcemy, aby przypadkowo zawinął do następnej linii i zrujnował siatkę, więc white-space: nowraprozwiązujemy ten problem. Wartość marginw tym wierszu będzie zależeć od tego, ile chcesz odstępu między heksami, i może być potrzebne trochę eksperymentów, aby uzyskać to, czego chcesz.

.hexrow {
    white-space: nowrap;
    /*right/left margin set at (( width of child div x sin(30) ) / 2) 
    makes a fairly tight fit; 
    a 3px bottom seems to match*/
    margin: 0 25px 3px;
}

Używając bezpośrednich elementów potomnych, .hexrowktóre są tylko divelementami, tworzymy podstawę dla kształtu heksadecymalnego. widthPojedzie poziomy z góry hex The heightpochodzi od tej liczby, ponieważ wszystkie boki są równej długości na sześciokąta foremnego. Ponownie, margines będzie zależał od odstępów, ale w tym miejscu wystąpi „nakładanie się” poszczególnych sześciokątów, aby uzyskać wygląd siatki. To background-imagejest zdefiniowane raz, właśnie tutaj. Przesunięcie z lewej strony ma na celu uwzględnienie co najmniej dodanej szerokości lewej strony heksa. Zakładając, że chcesz wyśrodkować tekst, text-alignuchwyt obsługuje położenie poziome (oczywiście), ale to, line-heightco pasuje heightdo, pozwoli na wyśrodkowanie w pionie.

.hexrow > div {
    width: 100px;
    height: 173.2px; /* ( width x cos(30) ) x 2 */
    /* For margin:
    right/left = ( width x sin(30) ) makes no overlap
    right/left = (( width x sin(30) ) / 2) leaves a narrow separation
    */
    margin: 0 25px;
    position: relative;
    background-image: url(http://i.imgur.com/w5tV4.jpg);
    background-position: -50px 0; /* -left position -1 x width x sin(30) */
    background-repeat: no-repeat;
    color: #ffffff;
    text-align: center;
    line-height: 173.2px; /*equals height*/
    display: inline-block;
}

Każde szesnastkowe liczby nieparzyste przesuniemy w dół w stosunku do „rzędu”, a każda parzysta zmiana w górę. Obliczenie przesunięcia (szerokość x cos (30) / 2) jest również takie samo jak (wysokość / 4).

.hexrow > div:nth-child(odd) {
    top: 43.3px; /* ( width x cos(30) / 2 ) */
}

.hexrow > div:nth-child(even) {
    top: -44.8px; /* -1 x( ( width x cos(30) / 2) + (hexrow bottom margin / 2)) */
}

Używamy 2 divelementów potomnych do stworzenia „skrzydeł” heksa. Mają taki sam rozmiar jak główny sześciokątny prostokąt, a następnie są obracane i wypychane „poniżej” głównego heksa. Background-imagejest dziedziczona, więc obraz jest taki sam (oczywiście), ponieważ obraz w "skrzydłach" będzie "wyrównany" do tego w głównym prostokącie. Pseudoelementy są używane do generowania obrazów, ponieważ należy je „ponownie obrócić” z powrotem do poziomu (ponieważ obróciliśmy divich rodziców, aby utworzyć „skrzydła”).

Pierwsza :beforez nich przetłumaczy swoje tło na szerokość ujemnej wartości równej głównej części szesnastki plus oryginalne przesunięcie tła głównego szesnastki. :beforeZ drugiego zmienia punkt początkowy translacji i przesuwa główną szerokość na osi x, a w połowie wysokości na osi y.

.hexrow > div > div:first-of-type {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    z-index: -1;
    overflow: hidden;
    background-image: inherit;

    -ms-transform:rotate(60deg); /* IE 9 */
    -moz-transform:rotate(60deg); /* Firefox */
    -webkit-transform:rotate(60deg); /* Safari and Chrome */
    -o-transform:rotate(60deg); /* Opera */
    transform:rotate(60deg);
}

.hexrow > div > div:first-of-type:before {
    content: '';
    position: absolute;
    width: 200px; /* width of main + margin sizing */
    height: 100%;
    background-image: inherit;
    background-position: top left;
    background-repeat: no-repeat;
    bottom: 0;
    left: 0;
    z-index: 1;

    -ms-transform:rotate(-60deg) translate(-150px, 0); /* IE 9 */
    -moz-transform:rotate(-60deg) translate(-150px, 0); /* Firefox */
    -webkit-transform:rotate(-60deg) translate(-150px, 0); /* Safari and Chrome */
    -o-transform:rotate(-60deg) translate(-150px, 0); /* Opera */
    transform:rotate(-60deg) translate(-150px, 0);

    -ms-transform-origin: 0 0; /* IE 9 */
    -webkit-transform-origin: 0 0; /* Safari and Chrome */
    -moz-transform-origin: 0 0; /* Firefox */
    -o-transform-origin: 0 0; /* Opera */
    transform-origin: 0 0;
}

.hexrow > div > div:last-of-type {
    content: '';
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    z-index: -2;
    overflow: hidden;
    background-image: inherit;

    -ms-transform:rotate(-60deg); /* IE 9 */
    -moz-transform:rotate(-60deg); /* Firefox */
    -webkit-transform:rotate(-60deg); /* Safari and Chrome */
    -o-transform:rotate(-60deg); /* Opera */
    transform:rotate(-60deg);
}

.hexrow > div > div:last-of-type:before {
    content: '';
    position: absolute;
    width: 200px; /* starting width + margin sizing */
    height: 100%;
    background-image: inherit;
    background-position: top left;
    background-repeat: no-repeat;
    bottom: 0;
    left: 0;
    z-index: 1;

    /*translate properties are initial width (100px) and half height (173.2 / 2 = 86.6) */
    -ms-transform:rotate(60deg) translate(100px, 86.6px); /* IE 9 */
    -moz-transform:rotate(60deg) translate(100px, 86.6px); /* Firefox */
    -webkit-transform:rotate(60deg) translate(100px, 86.6px); /* Safari and Chrome */
    -o-transform:rotate(60deg) translate(100px, 86.6px); /* Opera */
    transform:rotate(60deg) translate(100px, 86.6px);

    -ms-transform-origin: 100% 0; /* IE 9 */
    -webkit-transform-origin: 100% 0; /* Safari and Chrome */
    -moz-transform-origin: 100% 0; /* Firefox */
    -o-transform-origin: 100% 0; /* Opera */
    transform-origin: 100% 0;
}

To spanmieści twój tekst. line-heightJest resetowany, aby linie tekstu normalne, ale vertical-align: middleprace Ponieważ line-heightbył większy od rodzica. white-spaceJest resetowany co pozwala zawijanie ponownie. Marginesy lewy / prawy można ustawić na wartość ujemną, aby umożliwić umieszczenie tekstu w „skrzydłach” heksa.

.hexrow > div > span {
    display: inline-block;
    margin: 0 -30px;
    line-height: 1.1;
    vertical-align: middle;
    white-space: normal;
}

Możesz wybrać poszczególne wiersze i komórki w tych wierszach, aby zmienić obrazy, spanustawienia tekstu lub krycie, lub pomieścić większy obraz (aby przesunąć go w wybrane miejsce) itd. Oto, co zrobisz w drugim wierszu.

.hexrow:nth-child(2) > div:nth-child(1) {
    background-image: url(http://i.imgur.com/7Un8Y.jpg);
}

.hexrow:nth-child(2) > div:nth-child(1) > span {
    /*change some other settings*/
    margin: 0 -20px;
    color: black;
    font-size: .8em;
    font-weight: bold;
}

.hexrow:nth-child(2) > div:nth-child(2) {
    background-image: url(http://i.imgur.com/jeSPg.jpg);
}

.hexrow:nth-child(2) > div:nth-child(3) {
    background-image: url(http://i.imgur.com/Jwmxm.jpg);
    /*you can shift a large background image, but it can get complicated
    best to keep the image as the total width (200px) and height (174px)
    that the hex would be.
    */
    background-position: -150px -120px;
    opacity: .3;
    color: black;
}

.hexrow:nth-child(2) > div:nth-child(3) > div:before {
    /*you can shift a large background image, but it can get complicated
    best to keep the image as the total width (200px) and height (174px)
    that the hex would be.
    */
    background-position: -100px -120px; /* the left shift is always less in the pseudo elements by the amount of the base shift */
}

.hexrow:nth-child(2) > div:nth-child(4) {
    background-image: url(http://i.imgur.com/90EkV.jpg);
    background-position: -350px -120px;
}

.hexrow:nth-child(2) > div:nth-child(4) > div:before {
    background-position: -300px -120px;
}
ScottS
źródło
1
Dziękuję za udzielenie tak dobrze wyjaśnionej, dokładnej i genialnej odpowiedzi, to jest niesamowite!
element119
@ScottS Miałem jednak dziwny problem z wdrażaniem go. Używam zestawu narzędzi Bootstrap Twittera i zawsze wygląda to tak . Ograniczyłem problem do div.container: układ staje się pomieszany, gdy sześciokąt znajduje się w tym elemencie div kontenera. Dziwną rzeczą jest jednak przyjrzenie się porównaniu między każdym scenariuszem: link i link . Przeprowadziłem kilka testów, ale nie wiem, co psuje sześciokąty!
element119
@ Archio - Najpierw pokazujesz kod pierwszego poziomu podrzędnego rzędu szesnastkowego, ale problem jest prawdopodobnie związany z elementami potomnymi drugiego poziomu, które generują "skrzydła" hexu, ponieważ to jest brakująca część obrazu ( więc nie mogę debugować). Problem mógł się tam znaleźć ( overflow: hiddenzrobiłby to, co pokazujesz), ale tak nie jest. Spójrz na zagnieżdżenie drugiego poziomu divi jego :beforekod, ponieważ prawdopodobnie albo divnie obraca się w transformacji, albo background-imagenie dziedziczy zarówno tego, jakdiv i jego :beforepseudoelementu.
ScottS,
Właśnie sprawdziłem - oba wydają się dziedziczyć kolor tła ( link ). One też się obracają, widzę, że są obracane w przeglądarce Chrome Web Inspector. Problem polega na tym, że po prostu się nie wyświetlają, mimo że tak display:block.
element119
Ach, chyba to znalazłem! Wygląda na to, że problem był z-index. Poprawiam to teraz, aby upewnić się, że wszystko działa poprawnie.
element119
53

W rzeczywistości można to zrobić za pomocą tylko jednego elementu na sześciokąt i pseudoelementów obrazu tła i tekstu.

próbny

Podstawowa struktura HTML :

<div class='row'>
    <div class='hexagon'></div>
</div>
<div class='row'>
    <div class='hexagon content ribbon' data-content='This is a test!!! 
    9/10'></div><!--
    --><div class='hexagon content longtext' data-content='Some longer text here.
       Bla bla bla bla bla bla bla bla bla bla blaaaah...'></div>
</div>

Możesz mieć więcej rzędów, wystarczy mieć nsześciokąty na nieparzystych rzędach i n+/-1sześciokąty na parzystych rzędach.

Odpowiedni CSS :

* { box-sizing: border-box; margin: 0; padding: 0; }
.row { margin: -18.5% 0; text-align: center; }
.row:first-child { margin-top: 7%; }
.hexagon {
    position: relative;
    display: inline-block;
    overflow: hidden;
    margin: 0 8.5%;
    padding: 16%;
    transform: rotate(30deg) skewY(30deg) scaleX(.866); /* .866 = sqrt(3)/2 */
}
.hexagon:before, .content:after {
    display: block;
    position: absolute;
    /* 86.6% = (sqrt(3)/2)*100% = .866*100% */
    top: 6.7%; right: 0; bottom: 6.7%; left: 0; /* 6.7% = (100% -86.6%)/2 */
    transform: scaleX(1.155) /* 1.155 = 2/sqrt(3) */ 
               skewY(-30deg) rotate(-30deg);
    background-color: rgba(30,144,255,.56);
    background-size: cover;
    content: '';
}
.content:after { content: attr(data-content); }
/* add background images to :before pseudo-elements */
.row:nth-child(n) .hexagon:nth-child(m):before {
    background-image: 
        url(background-image-mxn.jpg); 
}
Ana
źródło
5
To naprawdę zasługuje na więcej głosów! Tak zwięzłe i proste, a demo naprawdę prezentuje wszechstronność. Jedyną wadą może być to, że treść jest przechowywana w atrybucie, a nie w samym elemencie.
Thomas W
Możesz umieścić zawartość w elemencie podrzędnym :) Chciałem tylko zminimalizować liczbę potrzebnych tutaj elementów HTML. Ale zamiast pseudo można łatwo użyć elementu potomnego z rzeczywistą zawartością.
Ana
2
Masz rację, w zasadzie tylko wymaga zastąpienia .content:afterz .hexagon > *. Pomysłowy.
Thomas W
jakieś przemyślenia na temat tego, jak można elegancko zmienić marginesy między sześciokątami?
maxheld
dziwnie, jeśli rozszerzy się na całą wiązkę sześciokątów, staje się to glitchowe na safari, jak na to pytanie
maxheld
2

Przedstawię proste demo, jak stworzyć sześciokątny kształt.

.hex {
  width: 40px;
  height: 70px;
  margin: 20px;
  overflow: hidden;
}

.hex:before {
  content: "";
  transform: rotate(45deg);
  background: #f00;
  width: 50px;
  height: 50px;
  display: inline-block;
  margin: 10px -5px 10px -5px;
}
<div class="hex">
</div>

Starx
źródło
1

Możesz stworzyć w pełni responsywną sześciokątną siatkę, używając tylko CSS. Pomysł polega na utworzeniu kształtu rodzica jako maski za pomocą przepełnienia CSS2.1: ukrytego, który jest kompatybilny z prawie wszystkimi przeglądarkami, nawet z Internet Explorer 6.

Jest to zaskakująco prosta technika, której można użyć do stworzenia responsywnej siatki o różnych kształtach, a rozwiązanie problemu wymaga jedynie nieszablonowego myślenia.

Mam tutaj obszerny przewodnik krok po kroku, jak wykonać tę technikę: https://www.codesmite.com/article/how-to-create-pure-css-hexagonal-grids

To najlepszy sposób, jaki do tej pory znalazłem, nie wymaga javascript i jest zarówno płynny, jak i responsywny.

Technikę wykorzystałem również w darmowym szablonie HTML, który zawiera obrazy wewnątrz sześciokątów, które możesz zaprezentować i pobrać tutaj: https://www.codesmite.com/freebie/hexa-free-responsive-portfolio-template

Popiół
źródło
0

jeśli jesteś w stanie zaimplementować sztuczkę z kształtami div, po prostu daj każdemu elementowi div a position:relative(musisz najpierw ustawić je wszystkie jeden po drugim, ustawiając topi left)

RozzA
źródło
Czy możesz podać przykład w JSFiddle? Sztuczka z kształtami div tak naprawdę nie działa, jeśli muszę wypełnić sześciokąt obrazami i tekstem.
element119
1
Tak, będziesz mógł tworzyć tylko kształty jednokolorowe: jsfiddle.net/Exceeder/cVqtW Nie będzie możliwe użycie tego elementu div jako obszaru przycinania dla obrazów tła lub do kształtowania tekstu.
Alex Pakka
-2

AFAIK, nie da się tego zrobić w czystym CSS. Najlepszym rozwiązaniem byłoby użycie przycinania maski dla tła, jak wyjaśniono tutaj: http://www.cssbakery.com/2009/06/background-image.html (zadziała to tylko wtedy, gdy tło strony jest jednolite lub można je dopasować i dopasuj maskę do tła strony.

Następnie możesz użyć csstextwrap, aby dopasować się do tekstu: http://www.csstextwrap.com/examples.php

Na SO było kilka podobnych pytań. Np. Jest jakiś sposób, aby tekst w div wypełniał kształt trójkąta?

Alex Pakka
źródło
Alex, chciałem tylko, żebyś wiedział, że jest sposób na zrobienie tego w czystym CSS3 (zobacz moją odpowiedź).
ScottS,