javascriptjquerycssanimation

CSS3 circular animation


I did a CSS3 circular animated menu with the menu items starting from the left end and rotates to the right to their corresponding locations.
I have put each of the menu items inside a container and made each container to rotate to different angles to bring out the animation.
What my problem is since each menu item is inside a container, there would be as many as containers as there are menu items which makes it impossible to get the :hover action for the anchor tag inside each menu item since the container is in the top. Can someone suggest a fix for this?

You can see it here:

http://jsfiddle.net/blueeyes/bWWHm/4/

You can see that I am able hover the links aa and fff because aa doesn't have a container and the container of fff is on top of all.
The other links are under the container so not accessible.


Solution

  • DEMO

    2024 update

    CSS got better in the meanwhile and we can now simplify things by using a grid layout and custom properties.

    We assume we have 6 menu items and they spread across two thirds of the circle. Normally, I'd generate the HTML structure out of an array/ object, but here it's just 6 items and can be written manually too. The container they're in gets the number of items set as a custom property --n and each item gets an index set as a custom property --i.

    In the CSS, we compute the base angle in between two consecutive items (in between the radii going to the midpoints of two consecutive items). That's those two thirds of the circle divided by the number of items minus 1.

    ba = 240ยฐ/(n - 1)
    

    This base angle lets us compute the radius of the circle the items are distributed on (R) out of the size (diameter d) of the items or the other way around. We can also leave some space in between items that's a fraction f of their diameter. If we din't want any space in between them, then this fraction is 0.

    (.5*(1 + f)*d)/R = sin(.5*ba)
    

    So we can extract R depending on d:

    R = .5*(1 + f)*d/sin(.5*ba)
    

    or the other way around:

    d = 2*R*sin(.5*ba)/(1 + f)
    

    The items are stacked one on top of each other using CSS grid, rotated by an angle that's their index i multiplied with the base angle ba, translated outwards by the radius R and then rotated back by the inverse of their rotation angle so they're upright. No more setting transform for each one individually.

    html, body, nav, a {
      display: grid;
      place-content: center
    }
    
    nav, a {
      aspect-ratio: 1;
      border-radius: 50%
    }
    
    nav {
      --sa: 240deg;
      --ba: calc(var(--sa)/(var(--n) - 1));
      --r: .5*(1 + 0.25)*3em/sin(.5*var(--ba));
      width: calc(2*var(--r) + 3em);
      font-size: clamp(.75em, 5vmin, 2em);
      /* debug styles */
      outline: dashed 2px;
      outline-offset: -1.5em;
      box-shadow: 0 0 0 2px hotpink
    }
    
    a {
      --ca: var(--i)*var(--ba) - .5*var(--sa) + 90deg;
      grid-area: 1/ 1;
      width: 3em;
      transform: rotate(calc(var(--ca))) 
        translate(calc(-1*var(--r))) 
        rotate(calc(-1*(var(--ca))));
      text-decoration: none
    }
    <nav class="wrap" style="--n: 6">
      <a href='#' class="item" style="--i: 0; background: #ffadad">โค๏ธ</a>
      <a href='#' class="item" style="--i: 1; background: #ffd6a5">๐Ÿงก</a>
      <a href='#' class="item" style="--i: 2; background: #fdffb6">๐Ÿ’›</a>
      <a href='#' class="item" style="--i: 3; background: #caffbf">๐Ÿ’š</a>
      <a href='#' class="item" style="--i: 4; background: #9bf6ff">๐Ÿ’™</a>
      <a href='#' class="item" style="--i: 5; background: #bdb2ff">๐Ÿ’œ</a>
    </nav>

    2013 solution

    You're overcomplicating things, you don't need a container. You just position all menu items absolutely at the center of the circle, then you rotate each one by the desired angle, translate it outwards by the radius of the circle and then rotate it again by the opposite angle to make the text horizontal again. In this way, the center of each menu item is going to be on the circle.

    HTML:

    <ul class='circ-menu'>
      <li><a href='#'>aa</a></li>
      <li><a href='#'>bb</a></li>
      <!-- and so on -->
    </ul>
    

    CSS:

    .circ-menu {
      position: relative;
      padding: 0;
      width: 10em; height: 10em;
      list-style: none;
    }
    .circ-menu li {
      position: absolute;
      top: 50%; left: 50%;
      margin: -1.5em;
      width: 3em; height: 3em;
      background: rgba(255, 0, 0, .3);
    }
    .circ-menu li:first-child {
      transform: rotate(-112.5deg) translateY(-5em) rotate(112.5deg);
    }
    .circ-menu li:nth-child(2) {
      transform: rotate(-67.5deg) translateY(-5em) rotate(67.5deg);
    }
    /* and so on */