I'm trying to figure out the best way to build an animated hexagon menu. Please, see the following image for better understanding:
The hexagon burger button is in the center. Once clicked, it reveals the triangular shapes surrounding the burger button. The latter transformed into a cross to reverse the process and hide everything back to the how it started.
My picture is actually missing one step in the very beginning. The first picture should only show the burger button just as the codepen demo.
So my question is the following :
Could you please me advise me on how you would build this hexagon menu animation from HTML to CSS through jQuery and others. What techniques would you use to make it happen. Keep in mind that whereas the codepen example is featuring simple icons, mine features triangular pictures which once hovered reveal a title with subtitle.
Here is an approach and demo for a
And here is an animated gif of the hexagon menu in action :
vmin
). This can be modified by changing the width/height values to percentages (aspect ratio needs to be maintained see here)<img/>
tag), titles and subtitlesskewY()
and rotate()
.tr
and .clip
, unskewed with .clip
and unrotated with .content
the hexagon shape around the burger is made with an SVG polygon (easier to make and better result than with CSS (see 1st version)translate()
and opacity
) with the transition-delay propertytranslate()
and rotate()
) and fading the center elements background to a transparent rgba colorstroke-dashoffset
property of the SVG <polygon>
element.translateZ()
) and opacity. The images are faded out at the same timeI tested this menu on IE 11, chrome, FF and opera on a windows system. and the menu works on all these browsers. Chrome and FF render the font with blur (as seen in the animated gif made with chrome) and FF tends to make jagged sides for the triangles.
IE 11 has the best quality output for the fonts and triangles but :
I used crossbrowser testing to test safari support and the hexagon menu works on that system too.
For people who got this far, here is another link to the demo : hexagon menu
And the full code with the compiled CSS :
var hexNav = document.getElementById('hexNav');
document.getElementById('menuBtn').onclick = function() {
var className = ' ' + hexNav.className + ' ';
if ( ~className.indexOf(' active ') ) {
hexNav.className = className.replace(' active ', ' ');
} else {
hexNav.className += ' active';
}
}
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
}
body {
font-family: 'Open Sans', sans-serif;
background: #E3DFD2;
}
ul {
list-style-type: none;
}
a, a:hover, a:focus, a:visited {
text-decoration: none;
}
nav {
position: relative;
width: 70vmin;
height: 70vmin;
min-width: 500px;
min-height: 500px;
margin: 0 auto;
overflow: hidden;
}
/** MENU BUTTON ******************************************/
#menuBtn {
position: absolute;
top: 45%;
left: 45%;
width: 10%;
height: 10%;
cursor: pointer;
z-index: 2;
will-change: transform;
}
#menuBtn svg {
display: block;
}
#menuBtn:hover svg polygon {
-webkit-animation: hexHover 0.7s;
animation: hexHover 0.7s;
}
#menuBtn span {
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 2px;
padding: 8px 0;
background-clip: content-box;
background-color: #585247;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
-webkit-transition: background-color 0.5s;
transition: background-color 0.5s;
}
#menuBtn span:before, #menuBtn span:after {
position: absolute;
background-color: #585247;
content: '';
width: 20px;
height: 2px;
-webkit-transition: -webkit-transform 0.5s;
transition: transform 0.5s;
}
#menuBtn span:before {
top: 0;
}
#menuBtn span:after {
bottom: 0px;
}
@-webkit-keyframes hexHover {
0% {
stroke-dasharray: 0,0,300;
}
10% {
stroke-dasharray: 0,20,300;
}
100% {
stroke-dasharray: 300,20,300;
}
}
@keyframes hexHover {
0% {
stroke-dasharray: 0,0,300;
}
10% {
stroke-dasharray: 0,20,300;
}
100% {
stroke-dasharray: 300,20,300;
}
}
/** MENU ITEMS *******************************************/
#hex {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
-webkit-transform: scale(0.1) translatez(0);
-ms-transform: scale(0.1) translatez(0);
transform: scale(0.1) translatez(0);
-webkit-transition: -webkit-transform 0.05s 0.5s;
transition: transform 0.05s 0.5s;
}
.tr {
position: absolute;
left: 50%;
bottom: 50%;
width: 34.6%;
height: 40%;
-webkit-transform-origin: 0 100%;
-ms-transform-origin: 0 100%;
transform-origin: 0 100%;
overflow: hidden;
-webkit-transform: skewY(-30deg);
-ms-transform: skewY(-30deg);
transform: skewY(-30deg);
opacity: 0;
}
.tr:nth-child(1) {
-webkit-transform: rotate(0deg) skewY(-30deg);
-ms-transform: rotate(0deg) skewY(-30deg);
transform: rotate(0deg) skewY(-30deg);
-webkit-transition: opacity 0.5s, -webkit-transform 0.5s;
transition: opacity 0.5s, transform 0.5s;
}
.tr:nth-child(1) .clip {
-webkit-transform: skewY(30deg) rotate(30deg);
-ms-transform: skewY(30deg) rotate(30deg);
transform: skewY(30deg) rotate(30deg);
}
.tr:nth-child(1) .content {
-webkit-transform: rotate(-30deg);
-ms-transform: rotate(-30deg);
transform: rotate(-30deg);
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
padding-left: 15%;
-webkit-perspective-origin: 30% 70%;
perspective-origin: 30% 70%;
}
.active .tr:nth-child(1) {
-webkit-transform: rotate(0deg) skewY(-30deg) translate(10%, -10%);
-ms-transform: rotate(0deg) skewY(-30deg) translate(10%, -10%);
transform: rotate(0deg) skewY(-30deg) translate(10%, -10%);
-webkit-transition: opacity 0.5s 0.05s, -webkit-transform 0.5s 0.05s cubic-bezier(0, 2.3, 0.8, 1);
transition: opacity 0.5s 0.05s, transform 0.5s 0.05s cubic-bezier(0, 2.3, 0.8, 1);
}
.tr:nth-child(2) {
-webkit-transform: rotate(60deg) skewY(-30deg);
-ms-transform: rotate(60deg) skewY(-30deg);
transform: rotate(60deg) skewY(-30deg);
-webkit-transition: opacity 0.5s, -webkit-transform 0.5s;
transition: opacity 0.5s, transform 0.5s;
}
.tr:nth-child(2) .clip {
-webkit-transform: skewY(30deg) rotate(30deg);
-ms-transform: skewY(30deg) rotate(30deg);
transform: skewY(30deg) rotate(30deg);
}
.tr:nth-child(2) .content {
-webkit-transform: rotate(-90deg);
-ms-transform: rotate(-90deg);
transform: rotate(-90deg);
top: -8%;
left: 6.67%;
padding-left: 30%;
-webkit-perspective-origin: 30% 50%;
perspective-origin: 30% 50%;
}
.active .tr:nth-child(2) {
-webkit-transform: rotate(60deg) skewY(-30deg) translate(10%, -10%);
-ms-transform: rotate(60deg) skewY(-30deg) translate(10%, -10%);
transform: rotate(60deg) skewY(-30deg) translate(10%, -10%);
-webkit-transition: opacity 0.5s 0.1s, -webkit-transform 0.5s 0.1s cubic-bezier(0, 2.3, 0.8, 1);
transition: opacity 0.5s 0.1s, transform 0.5s 0.1s cubic-bezier(0, 2.3, 0.8, 1);
}
.tr:nth-child(3) {
-webkit-transform: rotate(120deg) skewY(-30deg);
-ms-transform: rotate(120deg) skewY(-30deg);
transform: rotate(120deg) skewY(-30deg);
-webkit-transition: opacity 0.5s, -webkit-transform 0.5s;
transition: opacity 0.5s, transform 0.5s;
}
.tr:nth-child(3) .clip {
-webkit-transform: skewY(30deg) rotate(30deg);
-ms-transform: skewY(30deg) rotate(30deg);
transform: skewY(30deg) rotate(30deg);
}
.tr:nth-child(3) .content {
-webkit-transform: rotate(-150deg);
-ms-transform: rotate(-150deg);
transform: rotate(-150deg);
-webkit-transform-origin: 42.3% 36.5%;
-ms-transform-origin: 42.3% 36.5%;
transform-origin: 42.3% 36.5%;
padding-left: 10%;
-webkit-perspective-origin: 30% 30%;
perspective-origin: 30% 30%;
}
.active .tr:nth-child(3) {
-webkit-transform: rotate(120deg) skewY(-30deg) translate(10%, -10%);
-ms-transform: rotate(120deg) skewY(-30deg) translate(10%, -10%);
transform: rotate(120deg) skewY(-30deg) translate(10%, -10%);
-webkit-transition: opacity 0.5s 0.15s, -webkit-transform 0.5s 0.15s cubic-bezier(0, 2.3, 0.8, 1);
transition: opacity 0.5s 0.15s, transform 0.5s 0.15s cubic-bezier(0, 2.3, 0.8, 1);
}
.tr:nth-child(4) {
-webkit-transform: rotate(180deg) skewY(-30deg);
-ms-transform: rotate(180deg) skewY(-30deg);
transform: rotate(180deg) skewY(-30deg);
-webkit-transition: opacity 0.5s, -webkit-transform 0.5s;
transition: opacity 0.5s, transform 0.5s;
}
.tr:nth-child(4) .clip {
-webkit-transform: skewY(30deg) rotate(30deg);
-ms-transform: skewY(30deg) rotate(30deg);
transform: skewY(30deg) rotate(30deg);
}
.tr:nth-child(4) .content {
-webkit-transform: rotate(-210deg);
-ms-transform: rotate(-210deg);
transform: rotate(-210deg);
-webkit-transform-origin: 65.4% 38.4%;
-ms-transform-origin: 65.4% 38.4%;
transform-origin: 65.4% 38.4%;
padding-left: 30%;
-webkit-perspective-origin: 70% 30%;
perspective-origin: 70% 30%;
}
.active .tr:nth-child(4) {
-webkit-transform: rotate(180deg) skewY(-30deg) translate(10%, -10%);
-ms-transform: rotate(180deg) skewY(-30deg) translate(10%, -10%);
transform: rotate(180deg) skewY(-30deg) translate(10%, -10%);
-webkit-transition: opacity 0.5s 0.2s, -webkit-transform 0.5s 0.2s cubic-bezier(0, 2.3, 0.8, 1);
transition: opacity 0.5s 0.2s, transform 0.5s 0.2s cubic-bezier(0, 2.3, 0.8, 1);
}
.tr:nth-child(5) {
-webkit-transform: rotate(240deg) skewY(-30deg);
-ms-transform: rotate(240deg) skewY(-30deg);
transform: rotate(240deg) skewY(-30deg);
-webkit-transition: opacity 0.5s, -webkit-transform 0.5s;
transition: opacity 0.5s, transform 0.5s;
}
.tr:nth-child(5) .clip {
-webkit-transform: skewY(30deg) rotate(30deg);
-ms-transform: skewY(30deg) rotate(30deg);
transform: skewY(30deg) rotate(30deg);
}
.tr:nth-child(5) .content {
-webkit-transform: rotate(-270deg);
-ms-transform: rotate(-270deg);
transform: rotate(-270deg);
top: -8%;
left: 6.67%;
padding-left: 15%;
-webkit-perspective-origin: 70% 50%;
perspective-origin: 70% 50%;
}
.active .tr:nth-child(5) {
-webkit-transform: rotate(240deg) skewY(-30deg) translate(10%, -10%);
-ms-transform: rotate(240deg) skewY(-30deg) translate(10%, -10%);
transform: rotate(240deg) skewY(-30deg) translate(10%, -10%);
-webkit-transition: opacity 0.5s 0.25s, -webkit-transform 0.5s 0.25s cubic-bezier(0, 2.3, 0.8, 1);
transition: opacity 0.5s 0.25s, transform 0.5s 0.25s cubic-bezier(0, 2.3, 0.8, 1);
}
.tr:nth-child(6) {
-webkit-transform: rotate(300deg) skewY(-30deg);
-ms-transform: rotate(300deg) skewY(-30deg);
transform: rotate(300deg) skewY(-30deg);
-webkit-transition: opacity 0.5s, -webkit-transform 0.5s;
transition: opacity 0.5s, transform 0.5s;
}
.tr:nth-child(6) .clip {
-webkit-transform: skewY(30deg) rotate(30deg);
-ms-transform: skewY(30deg) rotate(30deg);
transform: skewY(30deg) rotate(30deg);
}
.tr:nth-child(6) .content {
-webkit-transform: rotate(-330deg);
-ms-transform: rotate(-330deg);
transform: rotate(-330deg);
-webkit-transform-origin: 106.7% 25.2%;
-ms-transform-origin: 106.7% 25.2%;
transform-origin: 106.7% 25.2%;
padding-left: 30%;
-webkit-perspective-origin: 70% 70%;
perspective-origin: 70% 70%;
}
.active .tr:nth-child(6) {
-webkit-transform: rotate(300deg) skewY(-30deg) translate(10%, -10%);
-ms-transform: rotate(300deg) skewY(-30deg) translate(10%, -10%);
transform: rotate(300deg) skewY(-30deg) translate(10%, -10%);
-webkit-transition: opacity 0.5s 0.3s, -webkit-transform 0.5s 0.3s cubic-bezier(0, 2.3, 0.8, 1);
transition: opacity 0.5s 0.3s, transform 0.5s 0.3s cubic-bezier(0, 2.3, 0.8, 1);
}
.tr:nth-child(7) {
-webkit-transform: rotate(360deg) skewY(-30deg);
-ms-transform: rotate(360deg) skewY(-30deg);
transform: rotate(360deg) skewY(-30deg);
-webkit-transition: opacity 0.5s, -webkit-transform 0.5s;
transition: opacity 0.5s, transform 0.5s;
}
.tr:nth-child(7) .clip {
-webkit-transform: skewY(30deg) rotate(30deg);
-ms-transform: skewY(30deg) rotate(30deg);
transform: skewY(30deg) rotate(30deg);
}
.tr:nth-child(7) .content {
-webkit-transform: rotate(-390deg);
-ms-transform: rotate(-390deg);
transform: rotate(-390deg);
}
.active .tr:nth-child(7) {
-webkit-transform: rotate(360deg) skewY(-30deg) translate(10%, -10%);
-ms-transform: rotate(360deg) skewY(-30deg) translate(10%, -10%);
transform: rotate(360deg) skewY(-30deg) translate(10%, -10%);
-webkit-transition: opacity 0.5s 0.35s, -webkit-transform 0.5s 0.35s cubic-bezier(0, 2.3, 0.8, 1);
transition: opacity 0.5s 0.35s, transform 0.5s 0.35s cubic-bezier(0, 2.3, 0.8, 1);
}
.clip {
position: absolute;
top: 0;
left: 0;
width: 116%;
height: 86.66%;
overflow: hidden;
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
}
.content {
position: absolute;
width: 86.6%;
height: 116%;
top: 0;
left: 0;
box-sizing: border-box;
font-size: 2vmin;
-webkit-perspective: 500px;
perspective: 500px;
background: #000;
}
.content img {
position: absolute;
top: 0;
left: -50%;
right: -50%;
margin: auto;
height: 100%;
z-index: -1;
-webkit-transition: opacity 0.3s;
transition: opacity 0.3s;
pointer-events: none;
}
.content h2, .content p {
position: absolute;
width: 60%;
line-height: 1em;
color: #fff;
opacity: 0;
-webkit-transform: translateZ(-50px);
transform: translateZ(-50px);
}
.content h2 {
bottom: 50%;
text-transform: uppercase;
font-weight: 900;
font-size: 2em;
-webkit-transition: -webkit-transform 0.3s cubic-bezier(0, 2.3, 0.8, 1), opacity 0.3s;
transition: transform 0.3s cubic-bezier(0, 2.3, 0.8, 1), opacity 0.3s;
}
.content p {
position: absolute;
top: 50%;
font-size: 1em;
-webkit-transition: -webkit-transform 0.3s 0.075s cubic-bezier(0, 2.3, 0.8, 1), opacity 0.3s 0.075s;
transition: transform 0.3s 0.075s cubic-bezier(0, 2.3, 0.8, 1), opacity 0.3s 0.075s;
}
.content:hover h2, .content:hover p {
opacity: 1;
-webkit-transform: translatez(0);
-ms-transform: translatez(0);
transform: translatez(0);
}
.content:hover img {
opacity: 0.4;
}
.active #menuBtn:hover svg polygon {
-webkit-animation: none;
animation: none;
}
.active #menuBtn span {
background-color: transparent;
}
.active #menuBtn span:before {
-webkit-transform: translatey(8px) rotate(45deg);
-ms-transform: translatey(8px) rotate(45deg);
transform: translatey(8px) rotate(45deg);
}
.active #menuBtn span:after {
-webkit-transform: translatey(-8px) rotate(-45deg);
-ms-transform: translatey(-8px) rotate(-45deg);
transform: translatey(-8px) rotate(-45deg);
}
.active #hex {
-webkit-transform: scale(0.9) translatez(0);
-ms-transform: scale(0.9) translatez(0);
transform: scale(0.9) translatez(0);
-webkit-transition: none;
transition: none;
will-change: transform;
}
.active .tr {
opacity: 1;
will-change: transform;
}
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,800' rel='stylesheet' type='text/css'>
<nav id="hexNav">
<div id="menuBtn">
<svg viewbox="0 0 100 100">
<polygon points="50 2 7 26 7 74 50 98 93 74 93 26" fill="none" stroke-width="4" stroke="#585247" stroke-dasharray="0,0,300"/>
</svg>
<span></span>
</div>
<ul id="hex">
<li class="tr"><div class="clip"><a href="#" class="content">
<img src="https://farm8.staticflickr.com/7435/13629508935_62a5ddf8ec_c.jpg" alt="" />
<h2 class="title">Title</h2><p>Catch phrase</p>
</a></div></li>
<li class="tr"><div class="clip"><a href="#" class="content">
<img src="https://farm3.staticflickr.com/2827/10384422264_d9c7299146.jpg" alt="" />
<h2 class="title">Title</h2><p>Catch phrase</p>
</a></div></li>
<li class="tr"><div class="clip"><a href="#" class="content">
<img src="https://farm7.staticflickr.com/6083/6055581292_d94c2d90e3.jpg" alt="" />
<h2 class="title">Title</h2><p>Catch phrase</p>
</a></div></li>
<li class="tr"><div class="clip"><a href="#" class="content">
<img src="https://farm7.staticflickr.com/6092/6227418584_d5883b0948.jpg" alt="" />
<h2 class="title">Title</h2><p>Catch phrase</p>
</a></div></li>
<li class="tr"><div class="clip"><a href="#" class="content">
<img src="https://farm8.staticflickr.com/7187/6895047173_d4b1a0d798.jpg" alt="" />
<h2 class="title">Title</h2><p>Catch phrase</p>
</a></div></li>
<li class="tr"><div class="clip"><a href="#" class="content">
<img src="https://farm4.staticflickr.com/3766/12953056854_b8cdf14f21.jpg" alt="" />
<h2 class="title">Title</h2><p>Catch phrase</p>
</a></div></li>
</ul>
</nav>
Here is a link to the : first version
Ok. I have made a fiddle fo you because it was fun and nice practise (and something I will keep for any future necesity) and because you may use it to achieve what you want.
First the HTML which is quite basic:
<div class="container">
<div class="shape x1"></div>
<div class="shape x2"></div>
<div class="shape x3"></div>
<div class="shape x4"></div>
<div class="shape x5"></div>
<div class="shape x6"></div>
<div class="button">X</a>
</div>
and easy to understand. Basically on a relative container You place your triangles (one each div) with position absolute.
I have used css shapes for the triangles but I DO recomend for your example, specially as you may use images inside the shapes to better use plain images with transparent background on a normal shape div. then position the elements till you get what you want.
so I have use the comun class shape to get the comon css properties:
shape {
width: 0;
height: 0;
border-left: 58px solid transparent;
border-right: 58px solid transparent;
border-bottom: 100px solid red;
position:absolute;
transform-origin: center;
}
and then the "x" classes to position the elements in place as this one:
.x1 {
transform: rotate(90deg);
left:0;
top:50px;
}
If You use triangular images, obviously you ignore the rotation.
and I've placed the button in the middle.
Then it's time for this very simple jquery:
$(document).ready(function () {
$('.button').click(function () {
$('.x1').toggleClass("trans1");
$('.x2').toggleClass("trans2");
$('.x3').toggleClass("trans3");
$('.x4').toggleClass("trans4");
$('.x5').toggleClass("trans5");
$('.x6').toggleClass("trans6");
$('.button').toggleClass("button2")
});
});
which basically adds and removes a class to every element when you click the button.
Now you just have to use the new classes to reposition the elements and create the effect you want... use the button new class to (for example) change the background image of your button (burger for cross) and just add a simple transition like:
transition: all 1s linear;
Here you have the FIDDLE. I hope this could help you in your project.
Edited: If you want your elements to "close" with transition, add the property to the original classes and not to one created by the jquery. (I just like it better as now)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With