Position icons into circle
2020 solution
Here's a more modern solution I use these days.
I start off by generating the HTML starting from an array of images. Whether the HTML is generated using PHP, JS, some HTML preprocessor, whatever... this matters less as the basic idea behind is the same.
Here's the Pug code that would do this:
//- start with an array of images, described by url and alt text
- let imgs = [
- {
- src: 'image_url.jpg',
- alt: 'image alt text'
- } /* and so on, add more images here */
- ];
- let n_imgs = imgs.length;
- let has_mid = 1; /* 0 if there's no item in the middle, 1 otherwise */
- let m = n_imgs - has_mid; /* how many are ON the circle */
- let tan = Math.tan(Math.PI/m); /* tangent of half the base angle */
.container(style=`--m: ${m}; --tan: ${+tan.toFixed(2)}`)
- for(let i = 0; i < n_imgs; i++)
a(href='#' style=i - has_mid >= 0 ? `--i: ${i}` : null)
img(src=imgs[i].src alt=imgs[i].alt)
The generated HTML looks as follows (and yes, you can write the HTML manually too, but it's going to be a pain to make changes afterwards):
<div class="container" style="--m: 8; --tan: 0.41">
<a href='#'>
<img src="image_mid.jpg" alt="alt text"/>
</a>
<a style="--i: 1">
<img src="first_img_on_circle.jpg" alt="alt text"/>
</a>
<!-- the rest of those placed on the circle -->
</div>
In the CSS, we decide on a size for the images, let's say 8em
. The --m
items are positioned on a circle and it's if they're in the middle of the edges of a polygon of --m
edges, all of which are tangent to the circle.
If you have a hard time picturing that, you can play with this interactive demo which constructs the incircle and circumcircle for various polygons whose number of edges you pick by dragging the slider.
This tells us that the size of the container must be twice the radius of the circle plus twice half the size of the images.
We don't yet know the radius, but we can compute it if we know the number of edges (and therefore the tangent of half the base angle, precomputed and set as a custom property --tan
) and the polygon edge. We probably want the polygon edge to be a least the size of the images, but how much we leave on the sides is arbitrary. Let's say we have half the image size on each side, so the polygon edge is twice the image size. This gives us the following CSS:
.container {
--d: 6.5em; /* image size */
--rel: 1; /* how much extra space we want between images, 1 = one image size */
--r: calc(.5*(1 + var(--rel))*var(--d)/var(--tan)); /* circle radius */
--s: calc(2*var(--r) + var(--d)); /* container size */
position: relative;
width: var(--s); height: var(--s);
background: silver /* to show images perfectly fit in container */
}
.container a {
position: absolute;
top: 50%; left: 50%;
margin: calc(-.5*var(--d));
width: var(--d); height: var(--d);
--az: calc(var(--i)*1turn/var(--m));
transform:
rotate(var(--az))
translate(var(--r))
rotate(calc(-1*var(--az)))
}
img { max-width: 100% }
See the old solution for an explanation of how the transform chain works.
This way, adding or removing an image from the array of images automatically arranges the new number of images on a circle such that they're equally spaced out and also adjusts the size of the container. You can test this in this demo.
OLD solution (preserved for historical reasons)
Yes, it is very much possible and very simple using just CSS. You just need to have clear in mind the angles at which you want the links with the images (I've added a piece of code at the end just for showing the angles whenever you hover one of them).
You first need a wrapper. I set its diameter to be 24em
(width: 24em; height: 24em;
does that), you can set it to whatever you want. You give it position: relative;
.
You then position your links with the images in the center of that wrapper, both horizontally and vertically. You do that by setting position: absolute;
and then top: 50%; left: 50%;
and margin: -2em;
(where 2em
is half the width of the link with the image, which I've set to be 4em
- again, you can change it to whatever you wish, but don't forget to change the margin in that case).
You then decide on the angles at which you want to have your links with the images and you add a class deg{desired_angle}
(for example deg0
or deg45
or whatever). Then for each such class you apply chained CSS transforms, like this:
.deg{desired_angle} {
transform: rotate({desired_angle}) translate(12em) rotate(-{desired_angle});
}
where you replace {desired_angle}
with 0
, 45
, and so on...
The first rotate transform rotates the object and its axes, the translate transform translates the object along the rotated X axis and the second rotate transform brings back the object into position.
The advantage of this method is that it is flexible. You can add new images at different angles without altering the current structure.
CODE SNIPPET
.circle-container {
position: relative;
width: 24em;
height: 24em;
padding: 2.8em;
/*2.8em = 2em*1.4 (2em = half the width of a link with img, 1.4 = sqrt(2))*/
border: dashed 1px;
border-radius: 50%;
margin: 1.75em auto 0;
}
.circle-container a {
display: block;
position: absolute;
top: 50%; left: 50%;
width: 4em; height: 4em;
margin: -2em;
}
.circle-container img { display: block; width: 100%; }
.deg0 { transform: translate(12em); } /* 12em = half the width of the wrapper */
.deg45 { transform: rotate(45deg) translate(12em) rotate(-45deg); }
.deg135 { transform: rotate(135deg) translate(12em) rotate(-135deg); }
.deg180 { transform: translate(-12em); }
.deg225 { transform: rotate(225deg) translate(12em) rotate(-225deg); }
.deg315 { transform: rotate(315deg) translate(12em) rotate(-315deg); }
<div class='circle-container'>
<a href='#' class='center'><img src='image.jpg'></a>
<a href='#' class='deg0'><img src='image.jpg'></a>
<a href='#' class='deg45'><img src='image.jpg'></a>
<a href='#' class='deg135'><img src='image.jpg'></a>
<a href='#' class='deg180'><img src='image.jpg'></a>
<a href='#' class='deg225'><img src='image.jpg'></a>
<a href='#' class='deg315'><img src='image.jpg'></a>
</div>
How to place icons at the edge of circle
Consider the use of transformation. You place all the item into the center then rototate/translate them to place them on the edges. I am using CSS variables to make the code eaiser but it's not mandatory
.circle {
width: 300px;
height: 300px;
background: rgba(0, 0, 0, 0.2);
display: flex;
border-radius: 50%;
}
.menu {
list-style-type: none;
padding: 0;
display:grid; /* use grid */
margin:auto; /* center the menu with all the items */
}
.menu li {
--d:120px; /* distance from the center */
grid-area:1/1; /* all of them at the same position (they overlap) */
/* we rotate then translate to the edge then rotate again using the opposite rotation */
transform:rotate(var(--r)) translateX(var(--d)) rotate(calc(-1*var(--r)));
}
/* adjust the angle for each icon */
.menu li:nth-child(1) { --r: 0deg}
.menu li:nth-child(2) { --r: 40deg}
.menu li:nth-child(3) { --r:-40deg}
.menu li a {
display: block;
}
.menu li a img {
width: 50px;
}
<div class="circle">
<ul class="menu">
<li>
<a href="#instagram">
<img src="https://img.icons8.com/cotton/344/instagram-new.png" />
</a>
</li>
<li>
<a href="#chrome">
<img src="https://img.icons8.com/cotton/344/chrome.png" />
</a>
</li>
<li>
<a href="#youtube">
<img src="https://img.icons8.com/cotton/344/youtube.png" />
</a>
</li>
</ul>
</div>
Make Font Awesome icons in a circle?
i.fa {
display: inline-block;
border-radius: 60px;
box-shadow: 0 0 2px #888;
padding: 0.5em 0.6em;
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<i class="fa fa-wrench"></i>
Position elements around a half-circle (CSS)
The method from your link works perfectly.
jsFiddle
CSS
.circle-big {
position: relative;
height:180px;
width:180px;
padding: 21px;
border-radius: 50% 50%;
margin: 100px;
}
.circle-big:before {
position: absolute;
height: 90px;
width: 180px;
border-radius: 90px 90px 0 0 ;
background: green;
content: "";
}
.circle {
border-radius: 50%;
width: 30px;
height: 30px;
background-color: red;
display: block;
position: absolute;
overflow: hidden;
top: 50%;
left: 50%;
margin: -15px;
}
.one { transform: rotate(-30deg) translate(130px); }
.two { transform: rotate(-50deg) translate(130px); }
.three { transform: rotate(-70deg) translate(130px); }
.four { transform: rotate(-90deg) translate(130px); }
.five { transform: rotate(-110deg) translate(130px); }
.six { transform: rotate(-130deg) translate(130px); }
.seven { transform: rotate(-150deg) translate(130px); }
Hope it's what you expected.
If you want to increase the distance between the big circle and the small ones just increase the translation.
Absolute positioning an element on the circumference of a circle
Define right
and bottom
as % not px
Do the same for height and width if you want size to adapt. See my snippet
.wrapper{ position: relative; display: inline-block; margin: 30px;}
.image{ border-radius: 50%;}
.icon{ height: 15%; width: 15%; background: #008080; position: absolute; border-radius: 50%; right: 10%; bottom: 5%;}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"/>
<div class='wrapper'> <img src='https://via.placeholder.com/350x350' class='image img-fluid'/> <span class='icon'></span></div>
How to align the font icons and text in a circle?
you can use flexbox for this. Read the CSS added to the end of css part
.css-menu { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; position: relative; margin: 10px auto 0; width: 100%; max-width: 568px; height: 0;}
.css-menu > div { position: relative; margin-bottom: 250px; padding-bottom: 100%; max-width: 100%; height: 0;}
.css-menu * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box;}
.css-menu input { position: absolute; display: none;}
.css-menu .segment { position: absolute; z-index: 100; left: 50%; top: 50%; margin-left: -48%; margin-top: -48%; width: 96%; height: 96%; border-radius: 50%; overflow: hidden; transform: scale(0); transition: 0.5s cubic-bezier(.58, 2.4, 0.5, 0.5); transition: 0.5s ease;}
.css-menu .curve-upper { position: absolute; left: 1%; top: 1%; width: 98%; height: 98%; background: #fff; border-radius: 50%; transform: scale(0); transition: 0s;}
.css-menu .curve-lower { position: absolute; left: 0; top: 0; width: 100%; height: 100%; border-radius: 50%; overflow: hidden; transform: rotate(-130deg) scale(0); transition: 0s;}
.css-menu .curve { display: block; position: absolute; left: 50%; top: 50%; width: 100%; height: 100%; background: #000; transform-origin: left top; transform: rotate(0deg) skewY(45deg);}
.css-menu .segment label { display: block; position: absolute; left: 50%; top: 50%; width: 50%; height: 50%; background: #567a98; border: 1px solid #fff; cursor: pointer; transform-origin: left top; transition: 0.25s cubic-bezier(0, 0, 1, 1); transition: 0.25s ease;}
.css-menu .segment label span { display: block; position: absolute; left: 0; top: 0; right: 35%; bottom: 25%; margin: auto; width: 35px; height: 100px; font-size: 3rem; font-weight: normal; color: #fff;}
.fab:after{ content: attr(data-label); font-family: 'Nexa Light', sans-serif; font-size: 14px; font-weight: 700; margin-top: 10px; color: #fff; display: block; text-align: center; position: relative; left: 50%; transform: translateX(-50%);}
.css-menu .segment label:nth-of-type(1) { transform: rotate(-135deg) skewY(45deg);}
.css-menu .segment label:nth-of-type(2) { transform: rotate(-90deg) skewY(45deg);}
.css-menu .segment label:nth-of-type(3) { transform: rotate(-45deg) skewY(45deg);}
.css-menu .segment label:nth-of-type(4) { transform: rotate(-0deg) skewY(45deg);}
.css-menu .segment label:nth-of-type(5) { transform: rotate(45deg) skewY(45deg);}
.css-menu .segment label:nth-of-type(6) { transform: rotate(90deg) skewY(45deg);}
.css-menu .segment label:nth-of-type(7) { transform: rotate(135deg) skewY(45deg);}
.css-menu .segment label:nth-of-type(8) { transform: rotate(180deg) skewY(45deg);}
.css-menu .segment label:nth-of-type(1) span { transform: skewY(-45deg) rotate(135deg);}
.css-menu .segment label:nth-of-type(2) span { transform: skewY(-45deg) rotate(90deg);}
.css-menu .segment label:nth-of-type(3) span { transform: skewY(-45deg) rotate(45deg);}
.css-menu .segment label:nth-of-type(4) span { transform: skewY(-45deg) rotate(0deg);}
.css-menu .segment label:nth-of-type(5) span { transform: skewY(-45deg) rotate(-45deg);}
.css-menu .segment label:nth-of-type(6) span { transform: skewY(-45deg) rotate(-90deg);}
.css-menu .segment label:nth-of-type(7) span { transform: skewY(-45deg) rotate(-135deg);}
.css-menu .segment label:nth-of-type(8) span { transform: skewY(-45deg) rotate(-180deg);}
.css-menu .center { display: block; position: absolute; z-index: 200; left: 50%; top: 50%; margin-left: -17.5%; margin-top: -17.5%; width: 35%; height: 35%; border-radius: 50%; background: #000; overflow: hidden; box-shadow: 0 0 0 2px #fff;}
.css-menu .holder .segment { transition: 0.5s cubic-bezier(.58, 3, 0.5, 0.5); transition: 0.5s ease; transform: scale(1);}
.css-menu .holder .center { transition: 0.5s cubic-bezier(.58, 3, 0.5, 0.5); transition: 0.5s ease; transform: scale(1.2);}
.css-menu .holder .curve-upper { transition: 0.5s cubic-bezier(.58, 3, 0.5, 0.5); transition: 1s ease; transform: scale(1);}
.css-menu .holder .curve-lower { transition: 0.5s cubic-bezier(.58, 3, 0.5, 0.5); transition: 1s ease; transform: rotate(-135deg) scale(1);}
.css-menu #c1:checked ~ .holder .segment label:nth-of-type(1),.css-menu #c2:checked ~ .holder .segment label:nth-of-type(2),.css-menu #c3:checked ~ .holder .segment label:nth-of-type(3),.css-menu #c4:checked ~ .holder .segment label:nth-of-type(4),.css-menu #c5:checked ~ .holder .segment label:nth-of-type(5),.css-menu #c6:checked ~ .holder .segment label:nth-of-type(6),.css-menu #c7:checked ~ .holder .segment label:nth-of-type(7),.css-menu #c8:checked ~ .holder .segment label:nth-of-type(8) { background: #45acea;}
.css-menu #c1:checked ~ #toggle:checked ~ .holder .curve-lower { transform: rotate(-135deg)}
.css-menu #c2:checked ~ #toggle:checked ~ .holder .curve-lower { transform: rotate(-90deg)}
.css-menu #c3:checked ~ #toggle:checked ~ .holder .curve-lower { transform: rotate(-45deg)}
.css-menu #c4:checked ~ #toggle:checked ~ .holder .curve-lower { transform: rotate(0deg)}
.css-menu #c5:checked ~ #toggle:checked ~ .holder .curve-lower { transform: rotate(45deg)}
.css-menu #c6:checked ~ #toggle:checked ~ .holder .curve-lower { transform: rotate(90deg)}
.css-menu #c7:checked ~ #toggle:checked ~ .holder .curve-lower { transform: rotate(135deg)}
.css-menu #c8:checked ~ #toggle:checked ~ .holder .curve-lower { transform: rotate(180deg)}
.css-eye { display: block; width: 100%; height: 100%; background-color: white; border:0.25em solid black; border-radius: 50%; position: relative; box-shadow: 0.03em 0.14em rgba(0,0,0,0.1); animation: blink forwards infinite 10s ease-in-out; z-index:205;}
.css-pupil { position: absolute; margin-left: 25%; margin-top: 25%; width: 50%; height: 50%; font-size: 200px; border-radius: 50%; background-color: black; border: 0.1em solid #00aeef; box-shadow: 0 0 0 0.02em black; box-sizing: border-box; will-change: transform; transition: transform 200ms 300ms ease-in-out;}
@keyframes blink { 0%, 2%, 60%, 62%, 100% { transform: scaleX(1) scaleY(1); } 1%, 61% { transform: scaleX(1.3) scaleY(0.1); } }
.css-menu .segment label > span { display: flex; flex-direction: column; align-items: center;}
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous"><div class="screen"> <div class="css-menu"> <div> <input type="radio" id="c1" name="segment" checked> <input type="radio" id="c2" name="segment"> <input type="radio" id="c3" name="segment"> <input type="radio" id="c4" name="segment"> <input type="radio" id="c5" name="segment"> <input type="radio" id="c6" name="segment"> <input type="radio" id="c7" name="segment"> <input type="radio" id="c8" name="segment"> <input type="checkbox" id="toggle" checked="checked"> <div class="holder"> <div class="segment"> <label for="c1"><span class="fab fa-bitcoin" data-label="Test 1"></span></label> <label for="c2"><span class="fab fa-bluetooth" data-label="Test 2"></span></label> <label for="c3"><span class="fab fa-angellist" data-label="Test 3"></span></label> <label for="c4"><span class="fab fa-apple-pay" data-label="Test 4"></span></label> <label for="c5"><span class="fab fa-bimobject" data-label="Test 5"></span></label> <label for="c6"><span class="fab fa-app-store" data-label="Test 6"></span></label> <label for="c7"><span class="fab fa-bandcamp" data-label="Test 7"></span></label> <label for="c8"><span class="fab fa-affiliatetheme" data-label="Test 8"></span></label> </div> <div class="curve-lower"> <div class="curve"></div> </div> <div class="curve-upper"></div> <div class="center"> <div class="css-eye"> <div class="css-pupil"/> </div> </div> </div> </div> </div> </div> </div>
Positioning divs in a circle using JavaScript
First of all, the equation for a co-ordinate on a circle is simply:
(x, y) = (r * cos(θ), r * sin(θ))
where, r
is the radius of a circle and θ
is the angle in radians.
The reason why your code is creating an eccentric ellipse is because when you assign the .top
and .left
CSS values, you are not considering that it will actually take the top-left corner as its reference. I've fixed your code and now it creates a perfect circle.
Changes made to your code:
Added an array
theta
that holds all the angles.var theta = [0, Math.PI / 6, Math.PI / 4, Math.PI / 3, Math.PI / 2, 2 * (Math.PI / 3), 3 * (Math.PI / 4), 5 * (Math.PI / 6), Math.PI, 7 * (Math.PI / 6), 5 * (Math.PI / 4), 4 * (Math.PI / 3), 3 * (Math.PI / 2), 5 * (Math.PI / 3), 7 * (Math.PI / 4), 11 * (Math.PI / 6)];
The image below shows all the angles I've used.
Added an array
colors
that holds different colors.var colors = ['red', 'green', 'purple', 'black', 'orange', 'yellow', 'maroon', 'grey', 'lightblue', 'tomato', 'pink', 'maroon', 'cyan', 'magenta', 'blue', 'chocolate', 'DarkSlateBlue'];
Made changes to your trigonometric equations.
circleArray[i].posx = Math.round(radius * (Math.cos(theta[i]))) + 'px';
circleArray[i].posy = Math.round(radius * (Math.sin(theta[i]))) + 'px';Changed the way
.top
and.left
are assigned.circleArray[i].style.top = ((mainHeight / 2) - parseInt(circleArray[i].posy.slice(0, -2))) + 'px';
circleArray[i].style.left = ((mainHeight / 2) + parseInt(circleArray[i].posx.slice(0, -2))) + 'px';where
mainHeight
is the height of the#main
div
.
[1] 16 div
s
Demo on Fiddle
var setup = function() { var radius = 150; var main = document.getElementById('main'); var mainHeight = parseInt(window.getComputedStyle(main).height.slice(0, -2)); var theta = [0, Math.PI / 6, Math.PI / 4, Math.PI / 3, Math.PI / 2, 2 * (Math.PI / 3), 3 * (Math.PI / 4), 5 * (Math.PI / 6), Math.PI, 7 * (Math.PI / 6), 5 * (Math.PI / 4), 4 * (Math.PI / 3), 3 * (Math.PI / 2), 5 * (Math.PI / 3), 7 * (Math.PI / 4), 11 * (Math.PI / 6)]; var circleArray = []; var colors = ['red', 'green', 'purple', 'black', 'orange', 'yellow', 'maroon', 'grey', 'lightblue', 'tomato', 'pink', 'maroon', 'cyan', 'magenta', 'blue', 'chocolate', 'DarkSlateBlue']; for (var i = 0; i < 16; i++) { var circle = document.createElement('div'); circle.className = 'circle number' + i; circleArray.push(circle); circleArray[i].posx = Math.round(radius * (Math.cos(theta[i]))) + 'px'; circleArray[i].posy = Math.round(radius * (Math.sin(theta[i]))) + 'px'; circleArray[i].style.position = "absolute"; circleArray[i].style.backgroundColor = colors[i]; circleArray[i].style.top = ((mainHeight / 2) - parseInt(circleArray[i].posy.slice(0, -2))) + 'px'; circleArray[i].style.left = ((mainHeight / 2) + parseInt(circleArray[i].posx.slice(0, -2))) + 'px'; main.appendChild(circleArray[i]); }};setup();
div#main { height: 300px; width: 300px; position: absolute; margin: 0 auto; transform: translate(-50%, -50%); top: 50%; left: 50%;}div.circle { position: absolute; width: 20px; height: 20px; border: 2px solid black; border-radius: 50%;}body { margin: 0 auto; background: papayawhip;}
<div id="main"></div>
center align icon in circle/disc
just use
icon {
vertical-align: middle;
}
span.circle_icon { background: #fff; border-radius: 8em; -moz-border-radius: 8em; -webkit-border-radius: 8em; box-shadow: 0 0 0 6px #000; color: #000; display: inline-block; font-weight: bold; line-height: 10em; margin-bottom: 10px; margin-top: 10px; text-align: center; vertical-align: middle; width: 10em}icon { vertical-align: middle; }
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet"/><div class="col-md-4 padding-bottom-10 visual_preview_standard" dir="ltr" style="direction: ltr; background-color: silver;"> <div class="row"> <span class="circle_icon" dir="ltr" style="direction: ltr;"> <icon class="fa fa-graduation-cap" style="font-size: 70px;"></icon> </span> </div> <div class="row"> <b>University Name</b> </div></div>
Related Topics
Word Wrap Options in a Select List
Adding External CSS in an HTML File
Absolute Position Is Not Working
Valid to Use <A> (Anchor Tag) Without Href Attribute
Removing Ul Indentation with CSS
How to Align Input Forms in HTML
Second Line in Li Starts Under the Bullet After CSS-Reset
Prevent Bootstrap Modal from Disappearing When Clicking Outside or Pressing Escape
Why Does Margin-Top Work With Inline-Block But Not With Inline
Where Does Persistent File System Storage Store with Chrome
How to Force a Page Break in HTML Printing
How to Remove "Onclick" with Jquery
How to Stop 100% Width Text Boxes from Extending Beyond Their Containers