csscss-shapescss-transforms

Div elements to follow a curved path with CSS3


So the basic idea is 1 - 9 seats at a curved table as though you are looking at them through first person view. I'm trying to get div elements which would be seats to flow on the outside of another div element that has a border radius to make it a semi oblong circle. I've found a few examples with an element being animated to flow across the container in an arc, but I will need the div/seats to be static. I am looking for any ideas or examples that can lead me in the right path.


Solution

  • Finding points on ellipse and translating:

    If your oblong circle resembles an ellipse then you can find points on the ellipse using mathematical formula and then translate each div element to that particular point.

    Mathematical formula for calculating point (x,y) on an ellipse is(a * cos(t), b * sin(t)). In this formula, a represents the radius of the ellipse in x-axis, b represents the radius of the ellipse in the y-axis and t represents the angle in radians. Angle in radians = Angle in degrees * pi / 180.

    To make use of this approach, we do the following:

    .container {
      position: relative;
      height: 400px;
      width: 600px;
      padding: 12.5px;
      border: 1px solid;
      border-radius: 50%;
    }
    div > div {
      position: absolute;
      top: 0px;
      left: 0px;
      height: 50%;
      width: 50%;
      transform-origin: bottom right;
    }
    div > div:after {
      position: absolute;
      content: '';
      bottom: 0px;
      right: 0px;
      height: 25px;
      width: 25px;
      background: black;
      border-radius: 50%;
      transform: translateX(50%) translateY(50%);
    }
    div > div:after {
      background: red;
    }
    div > div:nth-child(n+4):after {
      background: orange;
    }
    div > div:nth-child(n+7):after {
      background: green;
    }
    div > div:nth-child(1) {
      transform: translateX(-300px) translateY(0px);
    }
    div > div:nth-child(2) {
      transform: translateX(-277.17px) translateY(-76.5px);
    }
    div > div:nth-child(3) {
      transform: translateX(-212.13px) translateY(-141.42px);
    }
    div > div:nth-child(4) {
      transform: translateX(-114.80px) translateY(-184.77px);
    }
    div > div:nth-child(5) {
      transform: translateX(0px) translateY(-200px);
    }
    div > div:nth-child(6) {
      transform: translateX(114.80px) translateY(-184.77px);
    }
    div > div:nth-child(7) {
      transform: translateX(212.13px) translateY(-141.42px);
    }
    div > div:nth-child(8) {
      transform: translateX(277.17px) translateY(-76.5px);
    }
    div > div:nth-child(9) {
      transform: translateX(300px) translateY(0px);
    }
    <div class="container">
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
    </div>

    enter image description here

    Note: The coordinates are approximate values and hence they may not align 100% correctly.


    Using rotation and scale transform: (original idea)

    The below snippet provides a very rough idea on how to position the elements along a circle. It is by no means a complete output but you can adapt it to fit your needs.

    The components are very simple:

    .container {
      position: relative;
      height: 200px;
      width: 200px;
      border: 1px solid;
      border-radius: 50%;
    }
    div > div {
      position: absolute;
      top: 0px;
      left: 0px;
      height: 50%;
      width: 50%;
      transform-origin: bottom right;
    }
    div > div:after {
      position: absolute;
      content: '';
      bottom: 0px;
      left: 0px;
      height: 25px;
      width: 25px;
      background: black;
      border-radius: 50%;
      transform: translateY(50%);
    }
    div > div:nth-child(1) {
      transform: rotate(0deg);
    }
    div > div:nth-child(2) {
      transform: rotate(22.5deg);
    }
    div > div:nth-child(3) {
      transform: rotate(45deg);
    }
    div > div:nth-child(4) {
      transform: rotate(67.5deg);
    }
    div > div:nth-child(5) {
      transform: rotate(90deg);
    }
    div > div:nth-child(6) {
      transform: rotate(112.5deg);
    }
    div > div:nth-child(7) {
      transform: rotate(135deg);
    }
    div > div:nth-child(8) {
      transform: rotate(157.5deg);
    }
    div > div:nth-child(9) {
      transform: rotate(180deg);
    }
    div > div:after {
      background: red;
    }
    div > div:nth-child(n+4):after {
      background: orange;
    }
    div > div:nth-child(n+7):after {
      background: green;
    }
    
    /* Just for demo */
    
    .container{
      transition: all 1s;
    }  
    .container:hover {
      height: 400px;
      width: 400px;
      transition: all 1s;
    }
    <div class="container">
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
    </div>

    There is a simple method to convert the above into an oblong circle and that would be to scale the container in the X-axis. One point to note is that the children would also get scaled and hence would need to be reverse transformed.

    .container {
      position: relative;
      height: 200px;
      width: 200px;
      border: 1px solid;
      border-radius: 50%;
      transform: scaleX(1.25);
      transform-origin: left;
    }
    div > div {
      position: absolute;
      top: 0px;
      left: 0px;
      height: 50%;
      width: 50%;
      transform-origin: bottom right;
    }
    div > div:after {
      position: absolute;
      content: '';
      bottom: 0px;
      left: 0px;
      height: 25px;
      width: 25px;
      background: black;
      border-radius: 50%;
      transform: translateY(50%);
    }
    div > div:nth-child(1) {
      transform: rotate(0deg);
    }
    div > div:nth-child(2) {
      transform: rotate(22.5deg);
    }
    div > div:nth-child(3) {
      transform: rotate(45deg);
    }
    div > div:nth-child(4) {
      transform: rotate(67.5deg);
    }
    div > div:nth-child(5) {
      transform: rotate(90deg);
    }
    div > div:nth-child(6) {
      transform: rotate(112.5deg);
    }
    div > div:nth-child(7) {
      transform: rotate(135deg);
    }
    div > div:nth-child(8) {
      transform: rotate(157.5deg);
    }
    div > div:nth-child(9) {
      transform: rotate(180deg);
    }
    div > div:after {
      background: red;
    }
    div > div:nth-child(n+4):after {
      background: orange;
    }
    div > div:nth-child(n+7):after {
      background: green;
    }
    
    /* Just for demo */
    
    .container {
      transition: all 1s;
    }
    .container:hover {
      height: 400px;
      width: 400px;
      transform: scaleX(1.25);
      transform-origin: left;
    }
    <div class="container">
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
    </div>

    The first method is the perfect and recommended approach as it doesn't cause any distortion to the div elements. The second is rough idea which avoids complex trigonometric calculations.