79 votes

Générer un motif hexagonal répétitif avec CSS3

J'ai donc besoin de créer un motif hexagonal répétitif, à l'aide de CSS. Si des images sont nécessaires, je peux le faire, mais je préférerais utiliser les CSS si possible.

Voici une idée de ce que j'essaie de créer :

enter image description here

En gros, j'ai juste besoin d'un moyen de créer des formes hexagonales, puis d'y superposer du texte ou des images. Je n'ai pas encore beaucoup de code, car je ne sais pas vraiment par où commencer. Le problème est que je pourrais simplement utiliser <div> en forme d'hexagone comme indiqué dans ( http://css-tricks.com/examples/ShapesOfCSS/ ), mais alors ils ne seraient pas connectés. Je pourrais utiliser un motif hexagonal répétitif, mais je ne serais pas en mesure de spécifier l'emplacement exact du texte ou des images dont j'ai besoin dans des formes spécifiques. Merci d'avance pour toute aide.

2 votes

Il existe des masques CSS, mais le support est terrible : webkit.org/blog/181/css-masks

0 votes

Pour un même motif hexagonal utilisant le <img> au lieu d'une image d'arrière-plan, vous pouvez cocher la case Grille d'hexagones avec la balise <img>. .

0 votes

J'ai fait une version sass de la solution de ScottS pour avoir des tailles différentes rapidement. Regardez-la ici codepen.io/mort3za/pen/wBabaB

130voto

ScottS Points 37738

(Bien que la réponse d'Ana soit arrivée plusieurs mois après la mienne, utilisant probablement la mienne comme base de réflexion, le fait qu'elle ait été capable de trouver une méthode en utilisant une seule div vaut la peine d'être promu, donc Regardez aussi sa réponse -mais notez que le contenu de l'hexagone est plus limité).

C'était une question vraiment étonnante. Merci de l'avoir posée. Ce qui est génial, c'est le fait que :

Ce violon prouve que vous pouvez le faire !

Violon original utilisé (modifié dans l'édition ultérieure du lien du violon ci-dessus) - il utilisait des images imgur.com, qui ne semblaient pas être très fiables dans le chargement, donc le nouveau violon utilise photobucket.com ( faites-moi savoir s'il y a des problèmes persistants de chargement d'images ). J'ai gardé le lien original parce que le code d'explication ci-dessous va avec (il y a quelques différences en background-size o position au nouveau violon).

L'idée m'est venue presque instantanément après avoir lu votre question, mais il a fallu du temps pour la mettre en œuvre. J'ai d'abord essayé d'obtenir un seul "hex" avec un seul div et des pseudo-éléments, mais il n'y a pas de moyen de faire pivoter l'image de l'utilisateur. background-image (dont j'avais besoin), j'ai donc dû ajouter un peu de div pour obtenir les côtés droit/gauche de l'hexagone, de sorte que je puisse ensuite utiliser les pseudo-éléments comme un moyen de background-image rotation.

J'ai testé dans IE9, FF, et Chrome. Théoriquement, tout navigateur supportant CSS3 transform dans lequel il devrait fonctionner.

Première mise à jour principale (explication ajoutée)

J'ai un peu de temps maintenant pour poster une explication de code, alors c'est parti :

Tout d'abord, les hexagones sont définis par des relations de 30/60 degrés et la trigonométrie, ce sont donc les principaux angles impliqués. Deuxièmement, nous commençons par une "rangée" dans laquelle la grille d'hexagones doit résider. Le HTML est défini comme (l'extra div les éléments aident à construire l'hexagone) :

<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>

Nous allons utiliser inline-block pour l'hexagone display mais nous ne voulons pas qu'ils passent accidentellement à la ligne suivante et ruinent la grille, donc white-space: nowrap résout ce problème. Le site margin sur cette rangée va dépendre de l'espace que vous voulez entre les hexagones, et une certaine expérimentation peut être nécessaire pour obtenir ce que vous voulez.

.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;
}

En utilisant les enfants immédiats de la .hexrow qui sont juste div éléments, nous formons la base de la forme hexagonale. Le site width conduira l'horizontale du sommet de l'hexagone, le height est dérivé de ce nombre puisque tous les côtés sont de même longueur sur un hexagone régulier. Encore une fois, la marge va dépendre de l'espacement, mais c'est ici que le "chevauchement" des hexagones individuels va se produire pour que la grille ait l'air de se produire. Le site background-image est défini une fois, ici même. Le décalage vers la gauche permet de prendre en compte au moins la largeur supplémentaire du côté gauche de l'hexagone. En supposant que vous souhaitiez centrer le texte, l'élément text-align gère l'horizontal (bien sûr) mais le line-height qui correspond à la height va permettre un centrage vertical.

.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;
}

Chaque étrange hexagone que nous allons déplacer vers le bas par rapport à la "rangée" et à chaque même shift up. Le calcul du décalage (largeur x cos(30) / 2) est également identique à celui de la hauteur (hauteur / 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)) */
}

Nous utilisons 2 enfants div pour créer les "ailes" de l'hexagone. Ils ont la même taille que le rectangle de l'hexagone principal, puis ils sont tournés et poussés "sous" l'hexagone principal. Background-image est hérité pour que l'image soit la même (bien sûr), car l'image dans les "ailes" va être "alignée" sur celle du rectangle principal. Les pseudo-éléments sont utilisés pour générer les images, parce qu'ils doivent être ramenés à l'horizontale (puisque nous avons fait pivoter le rectangle parent). div d'entre eux pour créer les "ailes").

Le site :before du premier translatera son arrière-plan de la largeur de la quantité négative égale à la partie principale de l'hexagone plus le décalage d'arrière-plan original de l'hexagone principal. Le site :before de la seconde changera le point d'origine de la translation et décalera la largeur principale sur l'axe des x, et la moitié de la hauteur sur l'axe des 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;
}

Ce site span abrite votre texte. Le site line-height est réinitialisée pour rendre les lignes de texte normales, mais l'option vertical-align: middle fonctionne depuis le line-height était plus grande sur le parent. Le site white-space est réinitialisé pour qu'il autorise à nouveau l'emballage. La marge gauche/droite peut être définie comme négative pour permettre au texte d'entrer dans les "ailes" de l'hexagone.

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

Vous pouvez cibler individuellement des lignes et des cellules de ces lignes pour modifier les images, ou bien span les paramètres du texte, ou l'opacité, ou l'adaptation d'une image plus grande (pour la déplacer à l'endroit souhaité), etc. C'est ce que font les éléments suivants pour la deuxième ligne.

.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;
}

1 votes

Merci d'avoir donné une réponse aussi bien expliquée, complète et géniale, c'est génial !

0 votes

@ScottS J'ai eu le plus étrange des problèmes pour l'implémenter. J'utilise la boîte à outils Bootstrap de Twitter et, d'une manière ou d'une autre, il apparaît toujours comme suit ce . J'ai réduit le problème à div.container la mise en page se dérègle lorsque l'hexagone se trouve à l'intérieur de ce conteneur div. Ce qui est bizarre, c'est de regarder la comparaison entre chaque scénario : lien et lien . J'ai fait quelques tests mais je n'arrive pas à trouver ce qui perturbe les hexagones !

0 votes

@Archio--D'abord, vous montrez le code pour l'enfant de premier niveau de la ligne hexagonale, mais le problème se situe probablement au niveau des enfants de deuxième niveau qui génèrent les "ailes" de l'hexagone puisque c'est la partie de l'image qui manque (je ne peux donc pas déboguer). Le problème podría ont été là (un overflow: hidden ferait ce que vous montrez), mais cela ne semble pas être le cas. Regardez l'imbrication de deuxième niveau div et son :before car il est probable que soit le div ne tourne pas dans la transformation, ou le background-image n'hérite pas de la les deux que div et son :before pseudo-élément.

53voto

Ana Points 19473

Il est en fait possible de le faire avec un seul élément par hexagone et des pseudo-éléments pour l'image de fond et le texte.

Démonstration

Base HTML structure :

<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>

Vous pouvez avoir beaucoup plus de lignes, vous devez juste avoir n hexagones sur les rangs impairs et n+/-1 hexagones sur des rangs pairs.

Pertinent 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); 
}

5 votes

Cela mérite vraiment plus de votes positifs ! Si laconique et simple, et la démo montre vraiment la polyvalence. Le seul inconvénient pourrait être que le contenu est stocké dans un attribut plutôt que dans l'élément lui-même.

0 votes

Vous pouvez mettre le contenu dans un élément enfant :) Je voulais seulement minimiser le nombre d'éléments HTML nécessaires ici. Mais un élément enfant avec un contenu réel peut être utilisé facilement à la place du pseudo élément.

2 votes

Vous avez raison, il suffit en fait de remplacer .content:after avec .hexagon > * . Ingénieux.

2voto

Starx Points 38727

Je vais vous faire une démonstration simple de la façon de créer une forme hexagonale.

.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>

1voto

Interdruper Points 75

Voici une autre approche utilisant COMPASS/SCSS, qui permet de définir facilement la taille et la disposition des hexagones :

http://codepen.io/interdruper/pen/GrBEk

0voto

RozzA Points 176

Si vous êtes en mesure de mettre en œuvre l'astuce des formes de div, il suffit de donner à chaque div une position:relative (vous devrez d'abord les positionner tous un par un, en définissant également les paramètres suivants top et left )

0 votes

Pouvez-vous donner un exemple dans un JSFiddle ? L'astuce des formes div ne fonctionne pas vraiment, si je dois remplir l'hexagone avec des images et du texte.

1 votes

Oui, vous ne pourrez créer que des formes unicolores : jsfiddle.net/Exceeder/cVqtW Il ne sera pas possible d'utiliser ces divisions comme zones d'écrêtage pour les images d'arrière-plan ou pour façonner le texte.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X