HTML5 / CSS3 Circle with Partial Border
Asked Answered
P

8

32

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.

Pavlish answered 24/10, 2012 at 23:4 Comment(2)
what's "part way"? 1/4, 1/2, 3/4? or something more arbitrary?Decennium
Something which can be smoothly animated from 0% to 100% around the circle.Pavlish
S
53

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>
Spot answered 24/10, 2012 at 23:28 Comment(5)
You can't make N-degree arc this way. Only 90,180 or 270.Chally
Excuse my ignorance, but how do you change the length of the arc? KPthunder mentioned he wanted to be able to smoothly animate it from 0 - 100% around the circle.Darmstadt
You change the skew angle. The skew angle is 90deg - the angle of the arc. But more than one element is going to be needed to make it fill an entire circle.Spot
I could not make it work. Could you specify, please, which parameter should I change for arc to animated smoothly from 0% to 100% around the circle as required?Bromberg
Was looking for a way to make half of the border on a circle transparent. This was the only method I could find that works. Thanks.Ordinand
C
15

It's possible.

  • Draw two circles using border-radius one on top of another.
  • Make one or more arc of both circles transparent by changing border-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>
Chally answered 24/10, 2012 at 23:35 Comment(2)
You can't get less than 1/4 of a circle with this method though, since one of the border quarters will always be showing....Ation
True, but you could less than 1/4 if instead of transparent you use the color of the backgroundLubet
I
13

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>
Inpour answered 23/2, 2014 at 22:2 Comment(1)
Oh awesome - thank you so much! Just used on a website I'm building - will attribute to you in the code :)Petiolate
G
5

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>
Gastrointestinal answered 12/3, 2021 at 14:2 Comment(0)
F
3

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>
Forty answered 26/1, 2015 at 1:33 Comment(0)
N
2

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.

Here's a fiddle for a loader with 3 partial circles that spin inside each other in alternate directions that show this in action.

Nubianubian answered 16/10, 2018 at 15:37 Comment(0)
A
0

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>
Abstracted answered 7/4, 2015 at 21:43 Comment(0)
A
0

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>
Anent answered 27/4, 2021 at 8:40 Comment(1)
this one is useful, and to narrow the gap a little you can use double circles with different rotations.Bruin

© 2022 - 2024 — McMap. All rights reserved.