13

I know it is possible to draw and animate arcs in SVG and canvas. However, is it possible in CSS?

I have created an arc using the following method:

.arc{
    width:150px;
    height:400px;
    border-radius:50%;
    border-right:1px solid black;
    border-left:1px solid black;
    border-top:1px solid black;
    border-bottom:1px solid white;
}

But, how can I animate this? The only way I can think of is having a pure white div over it and sliding that div to the right gradually revealing the arc. Is there a better way?

doğukan
  • 23,073
  • 13
  • 57
  • 69
Conqueror
  • 4,265
  • 7
  • 35
  • 41

5 Answers5

5

Here is working demo with minimum of hard-coded variables. This works based on animated circle halves:

.circle {
  display: inline-flex;
  overflow: hidden;
}

.circle__half {
  height: 200px;
  width: 100px;
  position: relative;
  overflow: hidden;
}

.circle__half:before {
  height: inherit;
  width: inherit;
  position: absolute;
  content: "";
  border-radius: 100px 0 0 100px;
  background-color: lime;
  transform-origin: 100% 50%;
  /* hidden by default */
  transform: rotate(180deg);
  opacity: 0.65;
  animation-name: rotate-circle-half;
  animation-duration: 4s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

.circle__half--right {
  transform: scale(-1, -1);
}

.circle .circle__half--right:before {
  animation-name: rotate-circle-half--right;
}

/* show half of circle half of the time */
@keyframes rotate-circle-half {
  0% {
    transform: rotate(180deg);
  }
  50% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(0deg);
  }
}

@keyframes rotate-circle-half--right {
  0% {
    transform: rotate(180deg);
  }
  50% {
    transform: rotate(180deg);
  }
  100% {
    transform: rotate(0deg);
  }
}
<div class="circle">
  <div class="circle__half"></div>
  <div class="circle__half circle__half--right"></div>
</div>

Also the same look as iConnor's answer but doesn't have drawback of hardcoded background-color:

*,
*:before,
*:after {
  box-sizing: border-box;
}

.circle {
  display: inline-flex;
  overflow: hidden;
}

.circle__half {
  height: 200px;
  width: 100px;
  position: relative;
  overflow: hidden;
}

.circle__half:before {
  height: inherit;
  width: inherit;
  position: absolute;
  content: "";
  border-radius: 100px 0 0 100px;
  border: 10px solid #00507c;
  border-right-color: transparent;
  background-color: #0087cf;
  transform-origin: 100% 50%;
  /* hidden by default */
  transform: rotate(180deg);
  opacity: 0.65;
  animation-name: rotate-circle-half;
  animation-duration: 4s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

.circle__half--right {
  transform: scale(-1, -1);
}

.circle .circle__half--right:before {
  animation-name: rotate-circle-half--right;
}

/* show half of circle half of the time */
@keyframes rotate-circle-half {
  0% {
    transform: rotate(180deg);
  }
  50% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(0deg);
  }
}

@keyframes rotate-circle-half--right {
  0% {
    transform: rotate(180deg);
  }
  50% {
    transform: rotate(180deg);
  }
  100% {
    transform: rotate(0deg);
  }
}
<div class="circle">
  <div class="circle__half"></div>
  <div class="circle__half circle__half--right"></div>
</div>
Vadim Ovchinnikov
  • 13,327
  • 5
  • 62
  • 90
3

If you need sole CSS3, then you can set a width+height, set border-radius to 100%, disable the extra borders (use only 1 or 2) and add some good pixels to it.

Then you can animate using animate: time animation ease timingFunction; Declare the animation itself using @-prefix-keyframes { . . . } (Eh yea, looks like most browser engines require prefix for this one, chrome does :S) I think I might have something close to what you mean:

.qLoader2 {
  border: 4px solid blue;
  width: 10vw;
  height: 10vw;
  width: 72px;
  height: 72px;
  position: absolute;
  top: 12vh;
  right: 45vw;
  left: 45vw;
  background: white;
  opacity: 0.45;
  border-right: none;
  border-top: none;
  border-left: none;
  z-index: 2000;
  background-color: transparent;
  border-radius: 100%;
  transform: rotateZ(0);
  -webkit-animation: spin 2s linear infinite;
  animation: spin 2s linear infinite;
}


/* @-moz-keyframes spin {  . . . } */


/* @-ms-keyframes spin {  . . . } */


/* @-o-keyframes spin { . . . } */

@-webkit-keyframes spin {
  from {
    transform: rotateZ(0deg) scale(1);
  }
  50% {
    transform: rotateZ(540deg) scale(0.9);
    border-color: #0099ff;
  }
  to {
    transform: rotateZ(1080deg) scale(1);
  }
}

@keyframes spin {
  from {
    transform: rotateZ(0deg) scale(1);
  }
  50% {
    transform: rotateZ(540deg) scale(0.9);
    border-color: #0099ff;
  }
  to {
    transform: rotateZ(1080deg) scale(1);
  }
}
<div class="qLoader2"></div>

On JSFiddle

Feel free to use and modify. Alternatively you could check something with SVG it's fairly decent as well and supported by most nowadays browsers.

Vadim Ovchinnikov
  • 13,327
  • 5
  • 62
  • 90
Felype
  • 3,087
  • 2
  • 25
  • 36
1

EDIT: Using two arcs, you can have the animation draw cleanly from left-to-right AND have the background show through:

http://jsfiddle.net/sPv4A/6/

Vendor prefixes not included for CSS:

.arcContain {
  width: 150px;
  height: 400px;
  position: relative;
  margin: 20px;
}

.arc {
  width: 150px;
  height: 400px;
  border-radius: 50%;
  border: 2px solid black;
  border-bottom: 2px solid transparent;
  position: absolute;
  top: 0;
  right: 0;
  -moz-box-sizing: border-box;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
}

.archideLeft .arc {
  top: auto;
  bottom: 0;
  right: auto;
  left: 0;
}

.archide {
  width: 50%;
  height: 0%;
  position: absolute;
  top: 0;
  right: 0;
  overflow: hidden;
  animation: appear 1.2s ease-in 1.2s forwards;
}

.archideLeft {
  top: auto;
  bottom: 0;
  right: auto;
  left: 0;
  animation: appear 1.2s ease-out forwards;
}

@keyframes appear {
  to {
    height: 100%;
  }
}
<div class="arcContain">
  <div class="archide archideLeft">
    <div class="arc"></div>
  </div>
  <div class="archide">
    <div class="arc"></div>
  </div>
</div>

OLD ANSWER: Maybe using two child divs to cover it up, and then have them shrink away to reveal it:

.arc {
  width: 150px;
  height: 400px;
  border-radius: 50%;
  border-right: 1px solid black;
  border-left: 1px solid black;
  border-top: 1px solid black;
  border-bottom: 1px solid white;
  -moz-box-sizing: border-box;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  position: relative;
}

.arcInner {
  background: white;
  height: 402px;
  width: 77px;
  position: absolute;
}

.arcLeft {
  top: -2px;
  left: -2px;
  -webkit-transition: height 2s linear;
  -moz-transition: height 2s linear;
  -ms-transition: height 2s linear;
  -o-transition: height 2s linear;
  transition: height 2s linear;
}

.arcRight {
  bottom: 0;
  right: -2px;
  -webkit-transition: height 2s 2s linear;
  -moz-transition: height 2s 2s linear;
  -ms-transition: height 2s 2s linear;
  -o-transition: height 2s 2s linear;
  transition: height 2s 2s linear;
}

.appear .arcInner {
  height: 0;
}
<div class="arc">
  <div class="arcInner arcLeft"></div>
  <div class="arcInner arcRight"></div>
</div>
Vadim Ovchinnikov
  • 13,327
  • 5
  • 62
  • 90
Ben Jackson
  • 11,722
  • 6
  • 32
  • 42
  • I would appreciate if you go full CSS as per the question. Also, this is significantly more complex than the original approach, yet suffers the same problem (only works on same color backgrounds) – Conqueror Sep 16 '13 at 20:20
  • @Conqueror i just used JS as an easy way to add in the `appear` class, you could have it triggered by `:hover`, or use css animations easy enough to avoid JS. – Ben Jackson Sep 16 '13 at 20:23
0

As Per Chris B's suggestion on the original question, the answer is to contain the arc in another div and then animate the width of the container:

http://jsfiddle.net/AZb3X/

CSS:

body{
    background:orange;    
}

.arc{
    width:150px;
    height:400px;
    border-radius:50%;
    border-right:1px solid black;
    border-left:1px solid black;
    border-top:1px solid black;
    border-bottom:1px solid white;
    float:left;
}

.hider{
    width:0px;
    overflow:hidden;    
    -webkit-animation:unhide 12s;
}

@-webkit-keyframes unhide{
    100%{width:400px}
}    

HTML:

<div class='hider'>
    <div class="arc"></div>
</div>
Conqueror
  • 4,265
  • 7
  • 35
  • 41
0

I may be a little late, but I think using two "hiders" and translating one up and one down will look a little better.

Working Example

<div class="wrap">
    <div class="arc"></div>
</div>

body {
    background:orange;
}
.wrap {
    position:absolute;
    height:400px;
    width:170px;
    overflow: hidden;
}
.arc {
    position:absolute;
    width:150px;
    height:400px;
    margin:10px;
    border-radius:50%;
    border-right:1px solid black;
    border-left:1px solid black;
    border-top:1px solid black;
    border-bottom:1px solid transparent;
}
.arc:before {
    content:"";
    position:absolute;
    left:-1px;
    top:-2px;
    background: orange;
    width:76px;
    height:375px;
    animation:unhide1 5s linear both;
}
.arc:after {
    content:"";
    position:absolute;
    left:75px;
    top:-2px;
    background: orange;
    float: right;
    width:76px;
    height:375px;
    animation: unhide2 5s linear 5s both;
}
@keyframes unhide1 {
    100% {
        transform: translatey(-375px);
    }
}
@keyframes unhide2 {
    100% {
        transform: translatey(375px);
    }
}
apaul
  • 16,092
  • 8
  • 47
  • 82