Sunday 11 August 2013

BUILDING A CIRCULAR NAVIGATION WITH CSS TRANSFORMS - Part2

cn-demo2-step1
As you see the navigation is already starting to look different; we’re more than halfway through.
We still have one important thing to do. At the current state of this style, the clickable area of the anchors is still bigger than what we want. What we do want, is that only the colored part of the navigation shown in the image be clickable/hoverable. The image below shows the extra clickable area of the anchors.
cn-demo2-cover
When you hover over the red area shown in the image, which is the lower part of the anchor tags, the hover state of the anchors is fired, which is the normal thing to happen, but because we want it to seem like the anchors are only the purple area, we need to prevent mouse events from firing on the lower part of the anchors. For this we’re going to use a “cover” which we’ll place over at the center of the navigation container, which will be a circular shape covering the lower parts of the anchors, and therefore blocking the mouse events on these parts. We’ll be using a pseudo-element for this to avoid adding additional empty tags to our markup.
So, applying these three steps to our CSS, and changing the overall styles (colors and size) of the navigation and navigation items to look like the preview image above, we end up with the following CSS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
.csstransforms .cn-wrapper {
  position: absolute;
  top: 100%;
  left: 50%;
  z-index: 10;
  margin-top: -13em;
  margin-left: -13.5em;
  width: 27em;
  height: 27em;
  border-radius: 50%;
  background: transparent;
  opacity: 0;
  transition: all .3s ease 0.3s;
  transform: scale(0.1);
  pointer-events: none;
  overflow: hidden;
}
 
/*cover to prevent extra space of anchors from being clickable*/
.csstransforms .cn-wrapper:after{
  color: transparent;
  content:".";
  display:block;
  font-size:2em;
  width:6.2em;
  height:6.2em;
  position: absolute;
  left: 50%;
  margin-left: -3.1em;
  top:50%;
  margin-top: -3.1em;
  border-radius: 50%;
  z-index:10;
}
 
.csstransforms .cn-wrapper li {
  position: absolute;
  top: 50%;
  left: 50%;
  overflow: hidden;
  margin-top: -1.3em;
  margin-left: -10em;
  width: 10em;
  height: 10em;
  font-size: 1.5em;
  transition: all .3s ease;
  transform: rotate(76deg) skew(60deg);
  transform-origin: 100% 100%;
  pointer-events: none;
}
 
.csstransforms .cn-wrapper li a {
  position: absolute;
  right: -7.25em;
  bottom: -7.25em;
  display: block;
  width: 14.5em;
  height: 14.5em;
  border-radius: 50%;
  background: #429a67;
  background: radial-gradient(transparent 35%, #429a67 35%);
  color: #fff;
  text-align: center;
  text-decoration: none;
  font-size: 1.2em;
  line-height: 2;
  transition: all .3s ease;
  transform: skew(-60deg) rotate(-75deg) scale(1);
  pointer-events: auto;
}
 
.csstransforms .cn-wrapper li a span {
  position: relative;
  top: 1.8em;
  display: block;
  font-size: .5em;
  font-weight: 700;
  text-transform: uppercase;
}
 
.csstransforms .cn-wrapper li a:hover,
.csstransforms .cn-wrapper li a:active,
.csstransforms .cn-wrapper li a:focus {
    background: radial-gradient(transparent 35%, #449e6a 35%);
}
We want the navigation items in the second demo to spread out in a fan-like effect when the navigation is opened.
To achieve this effect, we have positioning the items in the same place and with the same rotation/skew ofrotate(76deg) skew(60deg).
Using transition delays we allow the items to spread out after we scale the wrapper up. When the navigation gets closed, we’ll wait for the items to move back in, before we scale the wrapper down again.
When the open button is clicked we’re going to spread the items out by rotating each of them to their final position on the circle.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
.csstransforms .opened-nav {
  border-radius: 50%;
  opacity: 1;
  transition: all .3s ease;
  transform: scale(1);
  pointer-events: auto;
}
 
.csstransforms .opened-nav li {
  transition: all .3s ease .3s;
}
 
.csstransforms .opened-nav li:first-child {
  transform: rotate(-20deg) skew(60deg);
}
 
.csstransforms .opened-nav li:nth-child(2) {
  transform: rotate(12deg) skew(60deg);
}
 
.csstransforms .opened-nav  li:nth-child(3) {
  transform: rotate(44deg) skew(60deg);
}
 
.csstransforms .opened-nav li:nth-child(4) {
  transform: rotate(76deg) skew(60deg);
}
 
.csstransforms .opened-nav li:nth-child(5) {
  transform: rotate(108deg) skew(60deg);
}
 
.csstransforms .opened-nav li:nth-child(6) {
  transform: rotate(140deg) skew(60deg);
}
 
.csstransforms .opened-nav li:nth-child(7) {
  transform: rotate(172deg) skew(60deg);
}
Of course, we’ll also provide a basic fallback for non-supporting browsers.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
.no-csstransforms .cn-wrapper{
  margin:10em auto;
  overflow:hidden;
  text-align:center;
  padding:1em;
}
.no-csstransforms .cn-wrapper ul{
  display:inline-block;
}
.no-csstransforms li{
  font-size:1em;
  width:5em;
  height:5em;
  float:left;
  line-height:5em;
  text-align:center;
  background-color: #fff;
}
.no-csstransforms li a{
  display:block;
  height:100%;
  width:100%;
  text-decoration: none;
  color: inherit;
}
.no-csstransforms .cn-wrapper li a:hover,
.no-csstransforms .cn-wrapper li a:active,
.no-csstransforms .cn-wrapper li a:focus{
  background-color: #f8f8f8;
}
.no-csstransforms .cn-wrapper li.active a {
  background-color: #6F325C;
  color: #fff;
}
.no-csstransforms .cn-button{
  display:none;
}
And the navigation is also going to be responsive, so we’ll shrink it on smaller screens. Here are the responsive styles for both circular and simple styles.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@media only screen and (max-width: 620px) {
  .no-csstransforms li{
    width:4em;
    height:4em;
    line-height:4em;
  }
}
@media only screen and (max-width: 500px) {
  .no-ccstransforms .cn-wrapper{
    padding:.5em;
  }
  .no-csstransforms .cn-wrapper li{
    font-size:.9em;
    width:4em;
    height:4em;
    line-height:4em;
  }
}
@media only screen and (max-width: 480px) {
  .csstransforms .cn-wrapper{
    font-size: .68em;
  }
  .cn-button{
    font-size:1em;
  }
}
@media only screen and (max-width:420px){
  .no-csstransforms .cn-wrapper li{
    width:100%;
    height:3em;
    line-height:3em;
  }
}
And that’s pretty much all the CSS you need to create these styles!

THE JAVASCRIPT

We won’t be using any JavaScript framework for these demos. I will be using David De Sandro’s Classie.js to add and remove classes. And finally for browsers that don’t support the addEventListener and removeEventListener we’ll use Jonathan Neal’s EventListener polyfill.
We’ll add an event handler to the button in each of the two demos. Clicking the button and/or focusing it will trigger opening/closing of the navigation.
Also, for the first demo, clicking anywhere outside the navigation when it’s open will also close it.
Let’s start with the JavaScript for the first demo.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
(function(){
 
  var button = document.getElementById('cn-button'),
    wrapper = document.getElementById('cn-wrapper'),
    overlay = document.getElementById('cn-overlay');
 
  //open and close menu when the button is clicked
  var open = false;
  button.addEventListener('click', handler, false);
  button.addEventListener('focus', handler, false);
  wrapper.addEventListener('click', cnhandle, false);
 
  function cnhandle(e){
    e.stopPropagation();
  }
 
  function handler(e){
    if (!e) var e = window.event;
    e.stopPropagation();//so that it doesn't trigger click event on document
 
      if(!open){
        openNav();
      }
    else{
        closeNav();
      }
  }
  function openNav(){
    open = true;
      button.innerHTML = "-";
      classie.add(overlay, 'on-overlay');
      classie.add(wrapper, 'opened-nav');
  }
  function closeNav(){
    open = false;
    button.innerHTML = "+";
    classie.remove(overlay, 'on-overlay');
    classie.remove(wrapper, 'opened-nav');
  }
  document.addEventListener('click', closeNav);
 
})();
The JavaScript for the second demo is similar to the previous one, except that we customize it for this case:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
(function(){
 
  var button = document.getElementById('cn-button'),
    wrapper = document.getElementById('cn-wrapper');
 
    //open and close menu when the button is clicked
  var open = false;
  button.addEventListener('click', handler, false);
  button.addEventListener('focus', handler, false);
 
  function handler(){
    if(!open){
      this.innerHTML = "Close";
      classie.add(wrapper, 'opened-nav');
    }
    else{
      this.innerHTML = "Menu";
    classie.remove(wrapper, 'opened-nav');
    }
    open = !open;
  }
  function closeWrapper(){
    classie.remove(wrapper, 'opened-nav');
  }
 
})();
And that’s pretty much it! I hope you liked this tutorial and found it useful!