search check home clock-o tag tags chevron-left chevron-right chevron-up chevron-down twitter facebook github rss comment comments terminal code

[CSS] Efektowne okrągłe menu rozwijane zbudowane za pomocą CSS

[CSS] Efektowne okrągłe menu rozwijane zbudowane za pomocą CSS

W oczekiwaniu na rozstrzygnięcia dzisiejszego wieczoru (polska drużyna, Legia Warszawa, gra mecz rewanżowy w Lidze Mistrzów) postanowiłem Ci przedstawić jeszcze jeden sposób na utworzenie rozwijanego menu na stronie za pomocą CSS. Co ciekawe, będzie ono symulowało efekt kliknięcia bez JS.

Okrągłe menu - kod HTML

1
2
3
4
5
6
7
8
<a class="button" href="#">&#9733;</a>
<ul class="menu">
  <li class="item"><a href="#">&#10022;</a></li>
  <li class="item"><a href="#">&#10047;</a></li>
  <li class="item"><a href="#">&#10037;</a></li>
  <li class="item"><a href="#">&#10026;</a></li>
  <li class="item"><a href="#">&#9728;</a></li>
</ul>

Utworzono dwa osobne elementy: przycisk i listę elementów. Są one od siebie niezależne i są one nałożone na siebie. Jako tekstu w menu użyto encji HTMLowych, które są zapisem technicznym symboli, takich jak różnego rodzaju gwiazdy lub kwiatki.

Dobrodziejstwa CSS3 - animacje i transformacje

Jedną z największych zalet najnowszej specyfikacji CSS są animacje i transformacje. Oczywiście, istnieje również mnóstwo innych zalet, między innymi nowy box-model czy też media queries.

W przypadku tego menu będzimy wykorzystywać animacje i transformacje, a także pobawimy się cieniami i wielokrotnymi tłami.

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
.button, /* #1 */
.menu {
  position: absolute;
  top: 50%;
  left: 50%;
  font: 1.5em/1.13 Verdana, sans-serif;
  transition: .5s;
}
.button,
.menu a {
  display: block;
  opacity: .56;
  background: #c9c9c9;
  color: #7a8092;
  text-align: center;
  text-decoration: none;
  text-shadow: 0 -1px #666;
}
.button:hover,
.button:focus,
.menu a:hover,
.menu a:focus {
  opacity: 1;
}
.button:focus,
.menu a:focus {
  outline: none;
}
.button {
  z-index: 2;
  margin: -.625em;
  width: 1.25em;
  height: 1.25em;
  border-radius: 50%;
  box-shadow: 0 0 3px 1px #fff;
}
.menu { /* #2 */
  z-index: 1;
  margin: -5em;
  width: 10em;
  height: 10em;
  transform: scale(.001);
  list-style: none;
  opacity: 0;
}
.menu:before,
.menu:after { /* #3 */
  position: absolute;
  top: 34.3%;
  width: .5em;
  height: 14%;
  opacity: .56;
  background: #c9c9c9;
  content: '';
}
.menu:before {
  left: 5.4%;
  border-radius: .25em 0 0 .25em;
  box-shadow:
    -1px 0 1px #666,
    inset 1px 0 1px #fff,
    inset -1px 0 1px #555,
    inset 0 1px 1px #fff,
    inset 0 -1px 1px #fff;
  transform: rotate(-75deg);
}
.menu:after {
  right: 5.4%;
  border-radius: 0 .25em .25em 0;
  box-shadow:
    1px 0 1px #666,
    inset -1px 0 1px #fff,
    inset 1px 0 1px #555,
    inset 0 1px 1px #fff,
    inset 0 -1px 1px #fff;
  transform: rotate(75deg);
}
.button:focus + .menu { /* #4 */
  transform: scale(1);
  opacity: 1;
}
.item { /* #5 */
  overflow: hidden;
  position: absolute;
  width: 50%;
  height: 50%;
  transform-origin: 100% 100%;
}
.item:first-child  { transform: rotate(-45deg) skewY(60deg); } /* #6 */
.item:nth-child(2) { transform: rotate(-15deg) skewY(60deg); }
.item:nth-child(3) { transform: rotate(15deg) skewY(60deg); }
.item:nth-child(4) { transform: rotate(45deg) skewY(60deg); }
.item:last-child   { transform: rotate(75deg) skewY(60deg); }
.item:after { /* #7 */
  position: absolute;
  top: 32%;
  left: 32%;
  width: 136%;
  height: 136%;
  border-radius: 50%;
  transform: skewY(-60deg);
  content: '';
}
.item a { /* #8 */
  width: 200%;
  height: 200%;
  border-radius: 50%;
  box-shadow:
    0 0 3px #666,
    inset 0 0 4px #fff;
  transform: skewY(-60deg) rotate(-15deg);
  background:
      linear-gradient(75deg, transparent 50%, #555 50%, transparent 54%) no-repeat 36.5% 0,
      linear-gradient(-75deg, transparent 50%, #555 50%, transparent 54%) no-repeat 63.5% 0,
      radial-gradient(rgba(127,127,127,0) 49%, rgba(255,255,255,.7) 51%, #c9c9c9 52%);
  background-size: 15% 15%, 15% 15%, cover;
  line-height: 1.4;
}
.item:nth-child(3) a:after { /* #9 */
  position: absolute;
  top: 13%;
  left: 50%;
  margin: -.25em;
  width: .5em;
  height: .5em;
  box-shadow: 2px 2px 2px #fff;
  transform: rotate(45deg);
  background: linear-gradient(-45deg, #c9c9c9 50%, transparent 50%);
  content: '';
}

W przerwie meczu (jest remis 0-0 z Molde), kontynuuję dalsze opisywanie sposobu uzyskania efektu rozwijanego menu po okręgu.

W związku z tym, że ciekawych rozwiązań zastosowanych przy budowie tego menu jest dość sporo, to postanowiłem, że opiszę je kolejno w punktach.

Zastosowane rozwiązania:

  1. Definiujemy te same style początkowe dla przycisku menu i samego menu, a także czas trwania efektu przejścia między wartościami własności;

  2. W tym miejscu definiujemy sposób zachowania się menu. Własność transform o wartości scale(.001) pozwala na utworzenie efektu rozwijania się menu bezpośrednio spod przycisku aktywującego;

  3. Pseudoelementy :before i :after służą do określania stylu zakończeń menu;

  4. Taka kombinacja selektorów pozwala na symulowanie kliknięcia w element i rozwinięcie menu;

  5. Określamy wymiary pojedynczego elementu w menu oraz jego stopień transformacji na osi OXY;

  6. W tym miejscu, definiujemy położenie każdego elementu z osobna, czyli określamy jego pozycję na okręgu oraz jego spłaszczenie, powstanie romboid. W tym przypadku, zastosowano wzór matematyczny:
    kąt obrotu = (A/2)° + (k - (n+1)/2)*A°, gdzie:

    • A to kąt pierwszego elementu naszej listy (stanowi naszą bazę),
    • k jest numerem porządkowym elementu, np: dla pierwszego elementu to 1, a dla trzeciego to 3;
    • n to liczba elementów w menu.

    Powyższy wzór ma zastosowanie dla nieparzystej liczby elementów. Dla parzystej liczby wzór się nieco zmienia:
    kąt obrotu = (A/2)° + (k - n/2)*A°

    Po obliczeniu kąta obrotu elementów, obliczamy ich przekrzywienie wg wzoru (dla wszystkich ta sama wartość):
    kąt przekrzywienia = 90° - kąt obrotu

  7. W tym miejscu określamy przestrzeń między elementem listy a przyciskiem głównym menu, aby efekt najazdu (hover) nie pojawiał się;

  8. Definiujemy wygląd środka elementu listy w którym znajduje się link. Dzięki wykonaniu transformacji link będzie widoczny poprawnie. Zerujemy przekrzywienie oraz poprawiamy jego położenie.
    Ponadto, tworzymy efektownie wyglądające tło elementu, dzięki temu uzyskujemy ulepszony efekt przestrzeni między elementami.

  9. Poprawiamy wygląd linka wewnątrz środkowego elementu listy.

Podsumowanie

Powyższy przykład został przygotowany dla przeglądarki Mozilla Firefox, ale bez problemu można tak zmodyfikować kod, aby działał on również w przeglądarkach z silnikiem Webkit. Zapewne pod IE9 i nowszych też zadziała po podaniu odpowiednich prefiksów.

Dlaczego to menu jest ciekawe i użyteczne? Dlatego, że mamy tu doczynienia z nietypową animacją listy elementów, w których kształty elementów są zależne od kąta nachylenia. Ponadto, dzięki skupieniu elementów w jednym miejscu pod jednym prczyciskiem, możemy utworzyć ciekawe interefejsy użytkownika, które będą cieszyły wzrok animacjami i nie będą rozpraszały uwagi od głównej zawartości strony.

BTW. Wpis ten dokończyłem już po meczu Legii. Na szczęcie mamy przynajmniej jedną polską drużynę w pucharach do grudnia. Podoba Ci się taka forma wpisu, która zawiera wtrącenia niekoniecznie powiązane w projektowaniem stron?

Demo z tego artykułu jest dostępne tutaj (obecnie działa tylko w Firefoksie): menu punktowe w CSS

źródło

  • Comandeer

    IMO dość dziwny pomysł, żeby przypiąć to do :focus. wg mnie focusowanie klikniętego linku jest dość dziwnym działaniem.
    pod chrome to raczej najszybciej byłoby uzyskać to przy :checked – wtedy nałożenie na stronę przezroczystego label dla tego pola?… itp itd. bo on nie focusuje (chyba, że próba z :active?)
    a wtrącenia raczej niepotrzebne 😉

  • Pod Chromem mi nie działa. W Firefoxie wygląda całkiem nieźle.

  • tak, nie działa pod Chromem z tego względu, że kod był przygotowany pod Firefoxa. By zadziałał w Chromie należy zdublować właściwości CSS dodając odpowiednie prefixy.

  • Super! Fajnie działa pod Operą.