1

I have a CSS animation where by I am trying to get the child element to rotate around the parent element by a certain amount of degrees. But I cannot get it to rotate around the parent origin, nor can I get it to rotate from the correct radius, it seems to rotate around an invisible smaller circle.

Below is the broken animation. How do I fix this?

.center-circle {
    top:100px;
    left:100px;
    width: 180px;
    height: 180px;
  
    border:1px solid black;
    border-radius: 100%;
    position: relative;    
}
.quarter-circle {
    width:65px;
    height:65px;
    position: absolute;
    top: 0%;
    left: 50%;
    border:1px solid #F28B46;
    border-radius: 100%;
    animation: spin-back 2s ease-in-out;
    -webkit-transform-origin: 0% 50%;
}
@-webkit-keyframes spin-back {
    0% {
        transform:rotate(-120deg);
    }
    100% {
        transform:rotate(0deg);
    }
}
<div class="center-circle">
    <div class="quarter-circle"></div>
</div>

JSFiddle

Harry
  • 83,910
  • 24
  • 185
  • 203
Sir
  • 7,905
  • 16
  • 79
  • 144
  • Do you want it like [this](https://jsfiddle.net/hari_shanx/h4uLwub7/1/)? – Harry Dec 27 '15 at 06:13
  • You got the right idea yeah, though i wanted the entire orange circle outside of the black circle, as if it's orbiting around it. Think like planet and a moon. – Sir Dec 27 '15 at 06:14

1 Answers1

2

This orbiting like animation can be achieved by setting the smaller circle outside of the parent circle at start (using top, left) and then setting the transform-origin as parent's radius + smaller circle's radius. This is because the center of rotation should be at the center of the larger circle which is offset by the top-left of the smaller circle by the combined distance of their radii.

When we set the transform-origin as 0% 50%, the 50% is half of the smaller circle's height (which is 32.5px) and so the rotation diameter and so the rotation happens around a smaller imaginary circle. Whereas what we actually need is for it to animate around the larger circle and so imaginary's circles radius has to be higher.

.center-circle {
  position: relative;
  top: 100px;
  left: 100px;
  width: 180px;
  height: 180px;
  border: 1px solid black;
  border-radius: 100%;
}
.quarter-circle {
  position: absolute;
  width: 65px;
  height: 65px;
  top: -32.5px;
  left: -32.5px;
  border: 1px solid #F28B46;
  border-radius: 100%;
  transform: rotate(-45deg);
  animation: spin-back 2s ease-in-out forwards;
  transform-origin: 122.5px 122.5px;
}
@keyframes spin-back {
  0% {
    transform: rotate(-45deg);
  }
  100% {
    transform: rotate(135deg);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class="center-circle">
  <div class="quarter-circle"></div>
</div>

The below snippet is bit more responsive (and in my opinion a bit better) but it still needs hard-coding of the parent's radius into the transform-origin. In this snippet, the top left of the smaller circle is at a slightly different offset from the parent's center circle (compared to the previous snippet) and hence the transform-origin calculation is also different.

.center-circle {
  position: relative;
  top: 100px;
  left: 100px;
  width: 180px;
  height: 180px;
  border: 1px solid black;
  border-radius: 100%;
}
.quarter-circle {
  position: absolute;
  width: 80px;
  height: 80px;
  top: 50%;
  left: 0%;
  border: 1px solid #F28B46;
  border-radius: 100%;
  transform: translateX(-100%) translateY(-50%) rotate(0deg);
  animation: spin-back 2s ease-in-out forwards;
  transform-origin: calc(100% + 90px) 40px; /* 90px is radius of the parent */
}
@keyframes spin-back {
  0% {
    transform: translateX(-100%) translateY(-50%) rotate(0deg);
  }
  100% {
    transform: translateX(-100%) translateY(-50%) rotate(180deg);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class="center-circle">
  <div class="quarter-circle"></div>
</div>
Harry
  • 83,910
  • 24
  • 185
  • 203
  • Is there a way to have it start at a given angle and end at a given angle? My end goal is to have it rotate a certain amount but not infinitely repeat so its a one time animation rotating about a certain range. – Sir Dec 27 '15 at 06:20
  • Yes @Dave. I modified the rotation and iteration count (to infinite) only for the animation to be visible for a longer time. Otherwise they can be the same as in your original code. Only the positioning and transform origin settings need change. – Harry Dec 27 '15 at 06:21
  • So if i wanted it to start at a 9 o Clock position and rotate to 3 o clock, aka 180 degrees, what values do i have to alter to get it to that starting position ? – Sir Dec 27 '15 at 06:23
  • @Dave: The smaller circle's original position makes a 45 degree angle (roughly like 10:30) and so you'd have to rotate from (-45deg) to (135deg). – Harry Dec 27 '15 at 06:28
  • Ah thank you :) It was a bit confusing! So (135deg) is not "total number of degrees to rotate from start position" but actually a fixed position on the circle. – Sir Dec 27 '15 at 06:30
  • Sort of @Dave basically the rotation from 9 to 3 is 180 deg rotation. Since our starting position is -45deg, the ending has to be 180-45deg :) – Harry Dec 27 '15 at 06:34
  • Side question, can this also be achieved if the child element was not in a parent element? Such as just on its own rotating around a invisible circle of a given size? – Sir Dec 27 '15 at 22:03
  • @Dave: Yes, it can be. Change the positioning of the child so that it doesn't go out of screen. Otherwise no other change is required. Give the `transform-origin` according to the size of the invisible circle. – Harry Dec 28 '15 at 03:41