How to put items in a Circle in CSS LESS

Through the depths of the internet I have search and found a method to put items on a circle using a CSS preprocessor. However it was in SCSS and I work in LESS, by which I mean the preprocessor not just less things. So I have converted it over to the dark side of LESS and broken down how it works below, I hope this helps.

Let’s start off with the SCSS version of items on a circle by Hugo Giraudel from his site.

/// Mixin to put items on a circle
/// [1] - Allows children to be absolutely positioned
/// [2] - Allows the mixin to be used on a list
/// [3] - In case box-sizing: border-box has been enabled
/// [4] - Allows any type of direct children to be targeted
///
/// @param {Integer} $nb-items - Number or items
/// @param {Length} $circle-size - Container size
/// @param {Length} $item-size - Item size
/// @param {String | false} $class-for-IE - Base class name for old IE
@mixin distribute-on-circle(
$nb-items,
$circle-size,
$item-size,
$class-for-IE: false
) {

$half-item: ($item-size / 2);
$half-parent: ($circle-size / 2);

position: relative; /* 1 */
width: $circle-size;
height: $circle-size;
padding: 0;
border-radius: 50%;
list-style: none; /* 2 */
box-sizing: content-box; /* 3 */

> * { /* 4 */

display: block;
position: absolute;
top: 50%;
left: 50%;
width: $item-size;
height: $item-size;
margin: -$half-item;

}

$angle: (360 / $nb-items);
$rot: 0;

@for $i from 1 through $nb-items {

@if not $class-for-IE {
> :nth-of-type(#{$i}) {
transform: rotate($rot * 1deg) translate($half-parent) rotate($rot * -1deg);
}
} @else {
> .#{$class-for-IE}#{$i} {
// If CSS transforms are not supported
$mt: sin($rot * pi() / 180) * $half-parent - $half-item;
$ml: cos($rot * pi() / 180) * $half-parent - $half-item;
margin: $mt 0 0 $ml;
}

}

$rot: ($rot + $angle);
}

}

So now you have seen the SCSS, let’s go to the LESS CSS.

First lets build the method you will be calling in the CSS, which like most of this we will just be copying the SCSS. Instead of putting an action call before the method name like in SCSS (@mixin), we call it like a class in standard CSS. In LESS we also identify variables with ‘@’ instead of the ‘$’. This means the above method of calling a method turns into the below.

.on-circle(@item-count, @circle-size, @item-size) {

}

There are then some manadatory styles that we need for the container to work. At this point though you could also add in some of your own custom style for the container as well. When we call the method we will be putting it in the containers class. We also use the variable ‘circle-size’ for the width and height. For this I used the Unit function for LESS, so it could be swapped out to be in percentage, em, rem or anything else. Rembering to use the ‘@’ to call the variable and not the ‘$’.

.on-circle(@item-count, @circle-size, @item-size) {

position: relative;
width: unit(@circle-size, px);
height: unit(@circle-size, px);
padding: 0;
margin:auto;
border-radius: 50%;

}

Within the method we now call upon the children, but to make sure we only affect the children of the container and nothing further down we got the direct child symbol added in (‘>’). Also because you could be using div’s, span’s or li elements as the children, it using the any type of element selector (‘*’).

A method to centre items up, which we have used here, is to set the width and height 50%, then move the item back half of its size. We get the children’s item size sent in the method to use for this, plus we got the Unit function used in case your making this responsive.

> * {

display: block;
position: absolute;
top: 50%;
left: 50%;
width: unit(@item-size, px);
height: unit(@item-size, px);
margin: unit(-(@item-size / 2), px);

}

Next is the loop for each child and it gets a bit different here for the conversion so ill break it down a bit more. The loop in SCSS looks more like a general loop you would see in JavaScript or some other languages, but the way to do in LESS is to call it like a method with an exception on the side. You can see from the example below I have called the method ‘cl’ for ‘children loop’, passed in two variables and then put a when clause on the side. This will then run while ‘i’ is greater than zero.

.cl(@i,@r) when (@i > 0) {

}

However this will be an infinite loop at the moment as ‘i’ never changes and if we just change ‘i’ in the loop then the ‘when’ clause won’t know about it. What we can do is call the loop method again inside, but of course minus 1.

Our ‘i’ variable is going to be our item count as we want to loop through each of the items. The second variable is our rotation for the item we are at, so for this reason we need to calculate out how much rotation we need for each item. This will be 360 degree’s divided between how many items we have, which will give us the 1% rotation value for each item on the circle.

You can then see we call the method at the bottom after we create the method, or the LESS won’t know what you are calling. Then in each iteration we iterate the variables by removing 1 from the item count and adding another degree percentage to the previous degree.

@angle: (360 / @item-count);

.cl(@i,@r) when (@i > 0) {

@rot: @r + @angle;
.cl(@i - 1,@rot);

}

.cl(@item-count,0);

We can now convert in the style for each of the children items, which is added in the loop. The simple conversion differences are the variables in the nth-of-type and the degrees. Instead of the ‘$’ symbol we use the ‘@’.  Then compared to the SCSS version, I did the calculation within the translate instead of using a variable. I halfed the circle and again to make it flexible to change I used the Unit function.

&:nth-of-type(@{i}) {

transform: rotate(@r * 1deg)  translate(unit(@circle-size/2,px)) rotate(@r * -1deg);

}

All Together Now

At the end of the road with all the pieces we have the final product. You can then use the following as the base to build your items in a circle. I have done a small demo on my Code Pen here.

/// Mixin to place items on a circle
/// @author Hugo Giraudel
/// @author Ana Tudor
/// @author convert SASS to LESS Christopher Pateman

.on-circle(@item-count, @circle-size, @item-size) {

position: relative;
width: unit(@circle-size, px);
height: unit(@circle-size, px);
padding: 0;
margin:auto;
border-radius: 50%;

> * {

display: block;
position: absolute;
top: 50%;
left: 50%;
width: unit(@item-size, px);
height: unit(@item-size, px);
margin: unit(-(@item-size / 2), px);

@angle: (360 / @item-count);

.cl(@i,@r) when (@i > 0) {

&:nth-of-type(@{i}) {

transform: rotate(@r * 1deg)  translate(unit(@circle-size/2,px)) rotate(@r * -1deg);

}

@rot: @r + @angle;
.cl(@i - 1,@rot);

}

.cl(@item-count,0);

}

}

 

Advertisements

Leave a message please

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s