Optimisation CSS with PGCD algo
A Pen by Long Lazuli on CodePen.
/ 8 items : | |
%menu.circular.classic{:'data-title' => 'Go !'} | |
%label{:for => 'Doc'} | |
%a{:href => '#Doc'} Doc | |
%label{:for => 'Toto'} | |
%a{:href => '#Toto'} Toto | |
%label{:for => 'Poum'} | |
%a{:href => '#Poum'} Poum | |
%label{:for => 'Home'} | |
%a{:href => '#Home'} Home | |
%label{:for => 'Doc'} | |
%a{:href => '#Doc'} Doc | |
%label{:for => 'Toto'} | |
%a{:href => '#Toto'} Toto | |
%label{:for => 'Poum'} | |
%a{:href => '#Poum'} Poum | |
/ 7 items : | |
%menu.circular.new | |
%label{:for => 'i'} | |
%a{:href => '#i'} I | |
%label{:for => 'ii'} | |
%a{:href => '#ii'} II | |
%label{:for => 'iii'} | |
%a{:href => '#iii'} III | |
%label{:for => 'iv'} | |
%a{:href => '#iv'} IV | |
%label{:for => 'v'} | |
%a{:href => '#v'} V | |
%label{:for => 'vi'} | |
%a{:href => '#vi'} VI | |
%label{:for => 'vii'} | |
%a{:href => '#vii'} VII | |
/ 5 items : | |
%menu.circular.new2 | |
%label{:for => 'Home'} | |
%a{:href => '#Home'} Home | |
%label{:for => 'Doc'} | |
%a{:href => '#Doc'} Doc | |
%label{:for => 'Toto'} | |
%a{:href => '#Toto'} Toto | |
%label{:for => 'Poum'} | |
%a{:href => '#Poum'} Poum | |
%label{:for => 'Pim'} | |
%a{:href => '#Pim'} Pim | |
/ 10 items : | |
%menu.circular | |
%label{:for => 'Home'} | |
%a{:href => '#Home'} 1 Home | |
%label{:for => 'Doc'} | |
%a{:href => '#Doc'} 2 Doc | |
%label{:for => 'Toto'} | |
%a{:href => '#Toto'} 3 Tototo | |
%label{:for => 'Poum'} | |
%a{:href => '#Poum'} 4 Poum | |
%label{:for => 'Pim'} | |
%a{:href => '#Pim'} 5 Pim | |
%label{:for => 'Home'} | |
%a{:href => '#Home'} Home di Pim poum 6 | |
%label{:for => 'Doc'} | |
%a{:href => '#Doc'} Document 7 | |
%label{:for => 'Toto'} | |
%a{:href => '#Toto'} Toto 8 | |
%label{:for => 'Poum'} | |
%a{:href => '#Poum'} Poum Poum Pouuuum 9 | |
%label{:for => 'Pim'} | |
%a{:href => '#Pim'} Pim 10 |
@import "compass"; | |
// Maximum number of Item in menu. Will stop to generate Code at this limit. | |
//$max-items: 12; | |
// If you want the item text to be shown horizontally, | |
// Be aware that if you put a too large number; some menu will not be visible. | |
//$max-items-horizontal: 8; | |
// If you want only a portion of circle, you can change this : | |
//$circle-portion: 360; | |
// An arbitrary rotation shift for all element : | |
//$rotation-shift: 90; | |
// those 2 values are the limit for the Menu text to be inverted; to enhance the reading. | |
// However, this is not applied in menu with horizontal text. | |
//$invert-text-min-angle: 90; | |
//$invert-text-max-angle: 270; | |
// You can, if you want reverse the rotation direction. | |
// It iis by default, it is clockwise. | |
//$rotation-reverse: true; | |
// Just a background to test this code. This should not be in a production generated CSS. | |
body { | |
background-image: url( http://fc09.deviantart.net/fs27/f/2008/070/d/a/Wood_tile_wallpaper_by_neko_xexe.png ); | |
background-size: cover; | |
} | |
// ********************************************************************** // | |
// here are the default values. | |
// * DO NOT CHANGE * // | |
$max-items: 12 !default; | |
$max-items-horizontal: 8 !default; | |
$circle-portion: 360 !default; | |
$rotation-shift: 0 !default; | |
$invert-text-min-angle: 90 !default; | |
$invert-text-max-angle: 270 !default; | |
$rotation-reverse: false !default; | |
/* ********************************************************** */ | |
/* Menu */ | |
menu.circular { | |
height: 4em; | |
margin: 7em 8em; | |
position: relative; | |
display: inline-block; | |
text-decoration: none; | |
transition-duration: 200ms; | |
transition-property: box-shadow; | |
width: 4em; | |
white-space: nowrap; | |
&, *, &:before, *:before, &:after, *:after { | |
// for debug : | |
// border: 1px solid blue; | |
// outline: 1px solid black; | |
border-radius: 50%; | |
box-sizing: border-box; | |
line-height: 4em; | |
padding: 0; | |
text-align: center; | |
transform-origin: 50% 50%; | |
overflow: visible; | |
} | |
*, &:before, *:before, &:after, *:after { | |
display: block; | |
color: inherit; | |
font-family: inherit; | |
font-weight: inherit; | |
height: 100%; | |
left: 50%; | |
margin: 0; | |
position: absolute; | |
text-decoration: inherit; | |
text-shadow: inherit; | |
top: 50%; | |
transform: translate( -50%, -50%); | |
transform-origin: 50% 50%; | |
white-space: inherit; | |
width: 100%; | |
} | |
&:before { | |
content: ""; | |
box-sizing: content-box; | |
transition-delay: 300ms, 300ms, 300ms, 0, 300ms, 300ms; | |
transition-duration: 200ms; | |
transition-property: border, color, text-shadow, box-shadow, width, height; | |
transition-timing-function: linear; | |
} | |
&:after { content: "Menu"; } | |
&[data-title]:after { content: attr( data-title )!important;} | |
&:hover { | |
&:before { | |
transition-delay: 0; | |
transition-duration: 100ms; | |
} | |
} | |
} | |
/* ********************************************************** */ | |
/* Menu Items */ | |
menu.circular { | |
& > * { | |
opacity: 0.0; | |
transform-origin: -100% 0; | |
transition-delay: 200ms, 200ms, 100ms, 0; | |
transition-duration: 200ms, 200ms, 200ms, 200ms; | |
transition-property: margin, left, opacity, transform; | |
transition-timing-function: linear; | |
} | |
&:hover > * { | |
left: 150%; | |
opacity: 1; | |
transition-delay: 0, 0, 100ms, 200ms; | |
} | |
} | |
/* ********************************************************** */ | |
/* Menu Items Links */ | |
menu.circular { | |
& > * > * { | |
transform-origin: 0% 0%; | |
transition-delay: 0; | |
transition-duration: 200ms, 200ms, 50ms, 50ms, 50ms; | |
transition-property: transform, color, box-shadow, heigth, width; | |
transition-timing-function: linear; | |
} | |
&:hover > * > *{ | |
text-align: left; | |
&:hover { | |
text-decoration: underline; | |
transition-delay: 0,500ms,0, 0, 0; | |
} | |
} | |
} | |
/* ********************************************************** */ | |
/* Differents Style : | |
*/ | |
/* CLASSIC (first one) */ | |
menu.circular.classic { | |
background-color: rgba(0,0,0,0); | |
box-shadow: 0.125em 0.1em .5em rgba(0,0,0,0.5), | |
-0.1em -0.06em 0 rgba(255,255,255,0.5); | |
color: rgba(0,0,0,0.5); | |
font-family: sans-serif; | |
font-weight: bold; | |
text-shadow: -0.125em -0.1em .5em rgba(0,0,0,0.25), | |
0.1em 0.06em 0 rgba(255,255,255,0.5); | |
&:before { | |
border: rgba(0,0,0,0.0) 0.5em solid; | |
box-shadow: inset 0.125em 0.1em .5em 0 rgba(0,0,0,0.5), | |
inset -0.1em -0.06em 0 0 rgba(255,255,255,0.5); | |
height: 5.5em; | |
width: 6.5em; | |
} | |
&:after { | |
content: "menu"; | |
text-transform: uppercase; | |
} | |
&:hover { | |
box-shadow: none; | |
&:before { | |
border-width: 6.5em; | |
color: transparent; | |
text-shadow: none; | |
} | |
} | |
& > * > * { | |
border: 0.5em transparent solid; | |
box-shadow: inset 0.125em 0.1em .5em 0 rgba(0,0,0,0.5), | |
inset -0.1em -0.06em 0 0 rgba(255,255,255,0.5); | |
color: transparent; | |
height: 150%; | |
text-shadow: none; | |
width: 150%; | |
} | |
&:hover > * > *{ | |
color: inherit; | |
text-shadow: inherit; | |
margin-left: 75%; | |
text-align: center; | |
&:hover { | |
box-shadow: 0.125em 0.1em .5em 0 rgba(0,0,0,0.5), | |
-0.1em -0.06em 0 0 rgba(255,255,255,0.5); | |
text-decoration: none; | |
&:active { | |
box-shadow: 0.125em 0.1em .5em 0 rgba(0,0,0,0.5), | |
-0.1em -0.06em 0 0 rgba(255,255,255,0.5), | |
inset 0.125em 0.1em .5em 0 rgba(0,0,0,0.5), | |
inset -0.1em -0.06em 0 0 rgba(255,255,255,0.5); | |
} | |
} | |
} | |
} | |
/* new */ | |
menu.circular.new { | |
box-shadow: 0.125em 0.1em .5em rgba(0,0,0,0.5), | |
-0.1em -0.06em 0 rgba(255,255,255,0.5); | |
font-weight: bold; | |
text-shadow: -0.125em -0.1em .5em rgba(0,0,0,0.25), | |
0.1em 0.06em 0 rgba(255,255,255,0.5); | |
&:before { | |
box-shadow: inset 0.125em 0.2em .4em 0 rgba(0,0,0,0.25), | |
inset -0.1em -0.06em 0 0 rgba(255,255,255,0.5), | |
1em 0.6em 2.5em 0 rgba(255,255,255,0.1); | |
height: 140%; | |
width: 165%; | |
} | |
&:after { | |
content: "new"; | |
text-transform: uppercase; | |
} | |
&:hover { | |
box-shadow: inset 0.125em 0.1em .25em rgba(0,0,0,0.25), | |
inset -0.1em -0.06em 0 rgba(255,255,255,0.5), | |
-1em -0.6em 2.5em 0 rgba(255,255,255,0.1); | |
&:before { | |
height: 450%; | |
width: 450%; | |
} | |
&:after { | |
content: ""; | |
} | |
} | |
& > * > * { | |
box-shadow: 0.125em 0.1em .5em 0 rgba(0,0,0,0.5), | |
-0.1em -0.06em 0 0 rgba(255,255,255,0.5); | |
height: 100%; | |
min-width: 100%; | |
} | |
&:hover > * > * { | |
box-shadow: 0.125em 0.1em .5em 0 rgba(0,0,0,0.5), | |
-0.1em -0.06em 0 0 rgba(255,255,255,0.5); | |
margin-left: 35%; | |
text-align: center; | |
text-decoration: none; | |
&:hover { | |
box-shadow: 0.125em 0.1em .5em 0 rgba(0,0,0,0.5), | |
-0.1em -0.06em 0 0 rgba(255,255,255,0.5), | |
inset 1em 0.6em 2.5em 0 rgba(255,255,255,0.1); | |
&:active { | |
box-shadow: 0.06em 0.05em .25em 0 rgba(0,0,0,0.25), | |
-0.1em -0.06em 0 0 rgba(255,255,255,0.5), | |
inset 1em 0.6em 2.5em 0 rgba(255,255,255,0.1); | |
} | |
} | |
} | |
} | |
/** | |
* This is the Tricky part. | |
* Calculating the rotation by the number of Item in the menu. | |
* | |
**/ | |
@function child-nth-of($index, $total) { | |
@if $index == 1 { | |
@return unquote(" & > *:first-child:nth-last-child( #{ $total} )"); | |
} @else { | |
@return unquote(" & > *:first-child:nth-last-child( #{ $total} ) ~ *:nth-child( #{ $index } )"); | |
} | |
} | |
@function pgcd($a, $b) { | |
$r: $a % $b; | |
@while $r != 0 { | |
$a: $b; | |
$b: $r; | |
$r: $a % $b; | |
} | |
@return $b; | |
} | |
@if $rotation-shift != 0 { | |
$rotationValue: $rotation-shift; | |
@if $rotation-reverse == true { | |
$rotationValue: $rotationValue * -1; | |
} | |
/* First-Items Rotation : */ | |
menu.circular:hover > *:first-child { | |
transform: rotate(#{$rotationValue}deg) translate(-50%, -50%); | |
} | |
@if $max-items-horizontal > 0 { | |
$sFirstItems: child-nth-of( 1, 1 ); | |
@for $f from 2 through $max-items-horizontal { | |
$sFirstItems: $sFirstItems, child-nth-of( 1, $f ); | |
} | |
menu.circular:hover { | |
#{$sFirstItems} { | |
& > * { | |
transform: rotate( #{$rotationValue * -1}deg ) translate( -50%, -50% ) ; | |
} | |
} | |
} | |
} | |
} | |
@for $itemCount from 1 through $max-items { | |
$item-portion: $circle-portion / $itemCount; | |
@for $itemIndex from 1 through $itemCount - 1 { | |
@if ( ( $itemCount % $itemIndex != 0 ) or $itemIndex == 1 ) { | |
@if pgcd($itemCount, $itemIndex) == 1 { | |
$rotationValue: ( $item-portion * $itemIndex ) + $rotation-shift; | |
@while $rotationValue > 360 { $rotationValue: $rotationValue - 360; } | |
@while $rotationValue < -360 { $rotationValue: $rotationValue + 360; } | |
@if $rotation-reverse == true { | |
$rotationValue: $rotationValue * -1; | |
} | |
$sItem: child-nth-of( $itemIndex + 1, $itemCount ); | |
$sTextHorizontal: null; | |
$cTextHorizontal: 0; | |
$sTextClassic: null; | |
$cTextClassic: 0; | |
$l: 1; | |
@while $l < $max-items / $itemCount { | |
$index: $itemIndex * $l + 1; | |
$total: $itemCount * $l; | |
@if $l > 1{ | |
$sItem: $sItem, child-nth-of( $index, $total ); | |
} | |
@if ( $itemCount * $l ) <= $max-items-horizontal { | |
@if $cTextHorizontal == 0{ | |
$sTextHorizontal: child-nth-of( $index, $total ); | |
} @else { | |
$sTextHorizontal: $sTextHorizontal, child-nth-of( $index, $total ); | |
} | |
$cTextHorizontal: $cTextHorizontal + 1; | |
} @else{ | |
@if $cTextClassic == 0{ | |
$sTextClassic: child-nth-of( $index, $total ); | |
} @else { | |
$sTextClassic: $sTextClassic, child-nth-of( $index, $total ); | |
} | |
$cTextClassic: $cTextClassic + 1; | |
} | |
$l: $l + 1; | |
} | |
menu.circular:hover { | |
#{$sItem} { | |
transform: rotate(#{$rotationValue}deg) translate(-50%, -50%); | |
} | |
@if $cTextHorizontal > 0 { | |
#{$sTextHorizontal} { | |
//text-align: center; | |
& > * { | |
transform: rotate( #{$rotationValue * -1}deg ) translate( -50%, -50% ) ; | |
text-align: center; | |
} | |
} | |
} | |
@if $cTextClassic > 0 { | |
#{$sTextClassic} { | |
@if $rotationValue >= $invert-text-min-angle | |
and $rotationValue < $invert-text-max-angle { | |
& > * { | |
transform: | |
rotate( 180deg ) | |
translate( -50%, -50%); | |
text-align: right; | |
direction: rtl; | |
right: 0; | |
color: blue; | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} |