Is it possible to create a circle using only HTML5 / CSS3 which has a border that only goes part way around the circle? If not, what techniques can I use to accomplish this effect? I would prefer to use pure DOM elements, but if I have to I can draw on canvas or spin up an SVG.
2023 solution
This method requires no JS, no extra element and not even any pseudos, just a handful of CSS declarations. It also works if the element has a (semi)transparent background
in addition to this partial border
.
It uses a two layer mask
, one being a conic-gradient
which creates a visible pie selection and whose size is relative to the border-box
and the second being a simple full cover layer restricted to the padding-box
.
First, we make our element circular (arbitrary width
, an aspect-ratio
of 1
and a border-radius
set to 50%
) and give it a border
.
Then, we set a mask
that's a conic-gradient()
, relative to the border-box
, covering a percentage --p
of our element (by default starting from 12 o'clock and going clockwise).
On top of this mask
layer, we set a full cover one restricted to the padding-box
.
.circular-progress {
border: solid 1.5em hotpink;
width: 50vmin;
aspect-ratio: 1;
border-radius: 50%;
background: hsla(180, 100%, 50%, .5);
--mask:
linear-gradient(red, red) padding-box,
conic-gradient(red var(--p, 17%), transparent 0%) border-box;
-webkit-mask: var(--mask);
mask: var(--mask)
}
/* just to make it obvious it works with semitrasparent background */
body {
background:
url(https://images.unsplash.com/photo-1693483923875-cdd9ef4a8046?w=800)
50%/ cover
}
<div class='circular-progress'></div>
If we want to animate this, we need to also register --p
.
@property --p {
syntax: '<percentage>';
initial-value: 0%;
inherits: true
}
.circular-progress {
border: solid 1.5em hotpink;
width: 50vmin;
aspect-ratio: 1;
border-radius: 50%;
background: hsla(180, 100%, 50%, .5);
--mask:
linear-gradient(red, red) padding-box,
conic-gradient(red var(--p), transparent 0%) border-box;
-webkit-mask: var(--mask);
mask: var(--mask);
animation: p 4s linear infinite
}
@keyframes p { to { --p: 100% } }
/* just to make it obvious it works with semitrasparent background */
body {
background:
url(https://images.unsplash.com/photo-1693483923875-cdd9ef4a8046?w=800)
50%/ cover
}
<div class='circular-progress'></div>
Registering custom properties this way in order to animate them is supported in Chromium browsers and in Safari and it's coming soonish in Firefox Nightly too.
Original answer from 2012 (preserved for web history reasons)
Yes, it is possible - see this:
.circle {
position: relative;
margin: 7em auto;
width: 16em;
height: 16em;
border-radius: 50%;
background: lightblue;
}
.arc {
overflow: hidden;
position: absolute;
/* make sure top & left values are - the width of the border */
/* the bottom right corner is the centre of the parent circle */
top: -1em;
right: 50%;
bottom: 50%;
left: -1em;
/* the transform origin is the bottom right corner */
transform-origin: 100% 100%;
/* rotate by any angle */
/* the skew angle is 90deg - the angle you want for the arc */
transform: rotate(45deg) skewX(30deg);
}
.arc:before {
box-sizing: border-box;
display: block;
border: solid 1em navy;
width: 200%;
height: 200%;
border-radius: 50%;
transform: skewX(-30deg);
content: '';
}
<div class='circle'>
<div class='arc'></div>
</div>
90deg - the angle of the arc
. But more than one element is going to be needed to make it fill an entire circle. –
Spot It's possible.
- Draw two circles using
border-radius
one on top of another. - Make one or more arc of both circles
transparent
by changingborder-color
. - Use
transform
to rotate the second circle and you will have the arc of the size you need.
Here is the demo: http://jsfiddle.net/kJXwZ/2/
.wrapper {
position: relative;
margin: 20px;
}
.arc {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
border-radius: 100%;
border: 1px solid;
}
.arc_start {
border-color: transparent red red red;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
.arc_end {
border-color: red red red transparent;
-webkit-transform: rotate(75deg);
-moz-transform: rotate(75deg);
-ms-transform: rotate(75deg);
-o-transform: rotate(75deg);
transform: rotate(75deg);
}
<div class="wrapper">
<div class="arc arc_start"></div>
<div class="arc arc_end"></div>
</div>
transparent
you use the color of the background –
Lubet This uses JavaScript as well, so it breaks from the original requirement :(
.. it does however deliver
There is a >> demo << here
@gkond Thanks, I derived this from your answer
// create a circle using HTML5 / CSS3 / JS which has a border that only goes part-way around
// the circle .. and which can be smoothly animated from 0% to 100% around the circle
// this solution allows for animation and still results in relatively clean code
// we use four quarter-circles, all hidden away behind a white square to start with..
// all four are rotated out clockwise, and each quarter will stop at it's own maximum:
// q1 at 25%, q2 at 50% .. etc. once we reach a value at or over 25%, all four quarters
// should be out from behind the white square, and we can hide it.. it needs to be
// hidden if we display a value over 75%, or else q4 will end up going in behind it again
// .. also, since the top border will usually sit between the angles of -45 to 45, we
// rotate everything by an extra -45 to make it all line up with the top nicely
var fromHidden = -90;
// utility function to align 0 degrees with top
// takes degrees and returns degrees - 45
function topAlign(degrees) {
return degrees - 45
};
// utility function to rotate a jQuery element
// takes element and the degree of rotation (from the top)
function rotate(el, degrees) {
var degrees = topAlign(degrees || 0);
el.css(
'transform', 'rotate(' + degrees + 'deg)',
'-webkit-transform', 'rotate(' + degrees + 'deg)',
'-moz-transform', 'rotate(' + degrees + 'deg)',
'-ms-transform', 'rotate(' + degrees + 'deg)',
'-o-transform', 'rotate(' + degrees + 'deg)'
)
}
// function to draw semi-circle
// takes a jQuery element and a value (between 0 and 1)
// element must contain four .arc_q elements
function circle(el, normalisedValue) {
var degrees = normalisedValue * 360; // turn normalised value into degrees
var counter = 1; // keeps track of which quarter we're working with
el.find('.arc_q').each(function() { // loop over quarters..
var angle = Math.min(counter * 90, degrees); // limit angle to maximum allowed for this quarter
rotate($(this), fromHidden + angle); // rotate from the hiding place
counter++; // track which quarter we'll be working with in next pass over loop
});
if (degrees > 90) { // hide the cover-up square soon as we can
el.find('.arc_cover').css('display', 'none');
}
}
// uses the the circle function to 'animate' drawing of the semi-circle
// incrementally increses the value passed by 0.01 up to the value required
function animate(el, normalisedValue, current) {
var current = current || 0;
circle(el, current);
if (current < normalisedValue) {
current += 0.01;
setTimeout(function() {
animate(el, normalisedValue, current);
}, 1);
}
}
// kick things off ..
animate($('.circle'), 0.69);
.circle {
position: relative;
margin: 20px;
width: 120px;
height: 120px;
}
.arc_q {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
border-radius: 50%;
border: 10px solid;
border-color: transparent green transparent transparent;
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
-o-transform: rotate(-45deg);
}
.arc_cover {
position: absolute;
top: 0;
left: 0;
width: 60px;
height: 60px;
background-color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="circle">
<div class="arc_q"></div>
<div class="arc_q"></div>
<div class="arc_q"></div>
<div class="arc_q"></div>
<div class="arc_cover"></div>
</div>
Conic gradient solution (works for any degree):
:root{
--degree:80deg;
--smoothing:0.5deg;
--color:red;
}
.a{
height:200px;
width:200px;
border-radius:50%;
background: conic-gradient(
var(--color) var(--degree), transparent calc(var(--degree) + var(--smoothing)) 100%);
}
.b{
height:84%;
width:84%;
top:8%;
left:8%;
position:relative;
border-radius:50%;
background:#D3D3D3;
}
<div class ="a">
<div class="b">
</div>
To do this, you can use simple box border properties, one element and one class. This would be an inline, inline-block or block treatment, depending on where you place your easy-circle class, and how/if you style position.
<!DOCTYPE html>
<html>
<head>
<style>
.easy-circle {
background: transparent;
border: 1em solid black; /* color not required, may show device fail */
border-color: red green blue transparent;
height: 10em;
width: 10em;
-moz-border-radius: 6em; /* height/2 + border thickness */
-webkit-border-radius: 6em;
border-radius: 50%; /* more than 50, shape-size adjustment irrelevant */
-ms-transform: rotate(45deg); /* IE 9 */
-webkit-transform: rotate(45deg); /* Chrome, Safari, Opera */
transform: rotate(45deg); /* transform optional */
}
</style>
</head>
<body>
<div class="easy-circle">
</div>
</body>
</html>
This can be achieved setting a transparent border around a transparent circle and using border-top-color: ;
to give a section of the circles border a color.
background-color:transparent;
border:3px solid transparent;
border-top-color: green;
This creates a circle with a border only around the top quarter;
You can also use
border-bottom-color:green;
as well as left and right to border different quarters of the circles circumference.
Here's a fiddle for a loader with 3 partial circles that spin inside each other in alternate directions that show this in action.
Simplest way to animate this is using css keyframes.
http://jsfiddle.net/8SUPX/644/
/**
* HTML5 / CSS3 Circle with Partial Border
* https://mcmap.net/q/446113/-html5-css3-circle-with-partial-border/1397351
*/
* { margin: 0; padding: 0; }
.circle {
position: relative;
margin: 6em auto;
width: 12em; height: 12em;
border-radius: 50%;
background: transparent;
border:20px solid #efefef;
border-top-color: #efefef;
border-right-color: #efefef;
border-bottom-color: #efefef;
border-left-color: #efefef;
-webkit-transition:.5s;-moz-transition:.5s;transition:.5s;
}
.circle:hover{
-webkit-animation: animix 0.5s 1;
-webkit-animation-fill-mode: forwards;
-moz-animation: animix 0.5s 1;
-moz-animation-fill-mode: forwards;
animation: animix 0.5s 1;
animation-fill-mode: forwards;
}
//Animate
@-webkit-keyframes animix {
0% {
border-top-color: transparent;
border-right-color: transparent;
border-bottom-color: transparent;
border-left-color: transparent;
}
25% {
border-top-color: red;
border-right-color: transparent;
border-bottom-color: transparent;
border-left-color: transparent;
}
50% {
border-top-color: red;
border-right-color: red;
border-bottom-color: transparent;
border-left-color: transparent;
}
75% {
border-top-color: red;
border-right-color: red;
border-bottom-color: red;
border-left-color: transparent;
}
100% {
border-top-color: red;
border-right-color: red;
border-bottom-color: red;
border-left-color: red;
}
}
@keyframes animix {
0% {
border-top-color: transparent;
border-right-color: transparent;
border-bottom-color: transparent;
border-left-color: transparent;
}
25% {
border-top-color: red;
border-right-color: transparent;
border-bottom-color: transparent;
border-left-color: transparent;
}
50% {
border-top-color: red;
border-right-color: red;
border-bottom-color: transparent;
border-left-color: transparent;
}
75% {
border-top-color: red;
border-right-color: red;
border-bottom-color: red;
border-left-color: transparent;
}
100% {
border-top-color: red;
border-right-color: red;
border-bottom-color: red;
border-left-color: red;
}
}
<div class="circle"> </div>
if you want double border you can use it also
div
{
background-color: #ddd;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 30px;
margin: 40px auto;
color: black;
width: 200px;
height: 200px;
border-radius: 50%;
position: relative;
}
div::before
{
content: "";
width: 200px;
height: 200px;
border-radius: 50%;
position: absolute;
left: 0;
top: 0;
transform: translate(-10px, -10px);
z-index: -1;
border: 10px solid;
border-color: red red red transparent;
}
body ::after
{
content: "";
width: 220px;
height: 220px;
border-radius: 50%;
position: absolute;
left: 0;
top: 0;
transform: translate(-21px, -22px);
z-index: -8;
border: 12px solid;
background: transparent;
border-color: blue transparent blue blue;
}
<div> </div>
© 2022 - 2024 — McMap. All rights reserved.