Home  >  Q&A  >  body text

A method to dynamically render circular sectors without using HTML5 canvas

<p>I'm making a fortune wheel and I need to create a wheel or circle based on the number of sectors and fill it with the prize name. </p> <p>I have completed the code for a circle with a fixed number of sectors. Here is an example of a circle containing 6 sectors. </p> <p> <pre class="brush:css;toolbar:false;">.wheel_container { position: relative; --wheel-size: 360px; width: var(--wheel-size); height: var(--wheel-size); margin-bottom: 2.4em; } .wheel { display: flex; justify-content: center; position: relative; overflow: hidden; width: 100%; height: 100%; border-radius: 50%; background-color: aquamarine; --segment-deg: 60deg; } .wheel div { display: flex; justify-content: center; align-items: center; position: absolute; width: calc((2 * 3.141592653589793 * (var(--wheel-size) / 2)) / 6); height: 50%; clip-path: polygon(0 0, 50% 100%, 100% 0); transform-origin: bottom; writing-mode: vertical-rl; } .wheel div > span { font-weight: 500; font-size: 1rem; text-align: center; color: rgba(0, 0, 0, 0.7); } .wheel div:nth-child(1) { background-color: beige; transform: rotate(calc(-1 * var(--segment-deg) / 2)); } .wheel div:nth-child(2) { background-color: blueviolet; transform: rotate(calc(-3 * var(--segment-deg) / 2)); } .wheel div:nth-child(3) { background-color: crimson; transform: rotate(calc(-5 * var(--segment-deg) / 2)); } .wheel div:nth-child(4) { background-color: orange; transform: rotate(calc(-7 * var(--segment-deg) / 2)); } .wheel div:nth-child(5) { background-color:violet; transform: rotate(calc(-9 * var(--segment-deg) / 2)); } .wheel div:nth-child(6) { background-color: yellow; transform: rotate(calc(-11 * var(--segment-deg) / 2)); }</pre> <pre class="brush:html;toolbar:false;"><div class='wheel_container'> <div class='wheel'> <div><span>Apple</span></div> <div><span>Durian</span></div> <div><span>Banana</span></div> <div><span>Mango</span></div> <div><span>Strawberry</span></div> <div><span>Jackfruit</span></div> </div> </div></pre> </p> <p>I tried to determine the <code>width</code> property of the <code>.wheel div</code> by calculating the circumference of the wheel divided by the number of sectors. However, this doesn't work because the polygon in <code>clip-path</code> is not curved, while the <code><div></code> that contains it is still a box. </p> <p>I was able to achieve the effect I wanted of 6 sector circles by adding some pixels to the width of <code><div></code>. </p> <p> <pre class="brush:css;toolbar:false;">.wheel_container { position: relative; --wheel-size: 360px; width: var(--wheel-size); height: var(--wheel-size); margin-bottom: 2.4em; } .wheel { display: flex; justify-content: center; position: relative; overflow: hidden; width: 100%; height: 100%; border-radius: 50%; background-color: aquamarine; --segment-deg: 60deg; } .wheel div { display: flex; justify-content: center; align-items: center; position: absolute; /* Modification */ width: calc((2 * 3.141592653589793 * ((var(--wheel-size) 37px) / 2)) / 6); height: 50%; clip-path: polygon(0 0, 50% 100%, 100% 0); transform-origin: bottom; writing-mode: vertical-rl; } .wheel div>span { font-weight: 500; font-size: 1rem; text-align: center; color: rgba(0, 0, 0, 0.7); } .wheel div:nth-child(1) { background-color: beige; transform: rotate(calc(-1 * var(--segment-deg) / 2)); } .wheel div:nth-child(2) { background-color: blueviolet; transform: rotate(calc(-3 * var(--segment-deg) / 2)); } .wheel div:nth-child(3) { background-color: crimson; transform: rotate(calc(-5 * var(--segment-deg) / 2)); } .wheel div:nth-child(4) { background-color: orange; transform: rotate(calc(-7 * var(--segment-deg) / 2)); } .wheel div:nth-child(5) { background-color:violet; transform: rotate(calc(-9 * var(--segment-deg) / 2)); } .wheel div:nth-child(6) { background-color: yellow; transform: rotate(calc(-11 * var(--segment-deg) / 2)); }</pre> <pre class="brush:html;toolbar:false;"><div class='wheel_container'> <div class='wheel'> <div><span>Apple</span></div> <div><span>Durian</span></div> <div><span>Banana</span></div> <div><span>Mango</span></div> <div><span>Strawberry</span></div> <div><span>Jackfruit</span></div> </div> </div></pre> </p> <p>However, the code that works for 6 sectors will not work for 8 sectors and so on...</p> <p>I think the solution might be in the SVG padding rules using <code>clip-path</code>. However, my knowledge of SVG only goes so far and I need some help. Other solutions are also welcome. </p>
P粉731861241P粉731861241437 days ago526

reply all(1)I'll reply

  • P粉460377540

    P粉4603775402023-09-03 09:02:45

    The problem you are having is that the width and height of the .wheel div are calculated incorrectly. If the height is the radius of the circle: --radius: calc(var(--wheel-size) / 2 );, then the width is width: calc( 2 * var(--radius ) / 1.732);, where 1.732 is Math.sqrt(3). This works for a wheel with 6 parts, where the triangle (for the clipping path) is an equilateral triangle.

    In your example, the width is equal to the radius. This is not enough because the div exceeds the circle and you calculated the clipping path based on the size of the div.

    To understand what's going on, remove border-radius: 50%; and add a semi-transparent unclipped portion to the wheel (clip-path: none;)

    console.log(Math.sqrt(3))
    *{margin:0;padding:0}
    
    
    .wheel_container {
      position: relative;
      
      --wheel-size: 360px;
      width: var(--wheel-size);
      height: var(--wheel-size);
      
      margin-bottom: 2.4em;
    }
    
    .wheel {
      display: flex;
      justify-content: center;
      
      position: relative;
      overflow: hidden;
      
      width: var(--wheel-size);
      height: var(--wheel-size);
      
      border-radius: 50%;
      background-color: aquamarine;
      --segment-deg: 60deg;
    }
    
    .wheel div {
      display: flex;
      justify-content: center;
      align-items: center;
      
      position: absolute;
      
      
      --radius: calc(var(--wheel-size) / 2 );
      height: var(--radius);
      
      width: calc( 2 * var(--radius ) / 1.732);
      clip-path: polygon(0 0, 50% 100%, 100% 0);
      
      transform-origin: bottom;
      writing-mode: vertical-rl;
    }
    
    .wheel div > span {
      font-weight: 500;
      font-size: 1rem;
      text-align: center;
      color: rgba(0, 0, 0, 0.7);
    }
    
    .wheel div:nth-child(1) {
      background-color: beige;
      transform: rotate(calc(-1 * var(--segment-deg) / 2));
    }
    
    .wheel div:nth-child(2) {
      background-color: blueviolet;
      transform: rotate(calc(-3 * var(--segment-deg) / 2));
    }
    
    .wheel div:nth-child(3) {
      background-color: crimson;
      transform: rotate(calc(-5 * var(--segment-deg) / 2));
    }
    
    .wheel div:nth-child(4) {
      background-color: orange;
      transform: rotate(calc(-7 * var(--segment-deg) / 2));
    }
    
    .wheel div:nth-child(5) {
      background-color: violet;
      transform: rotate(calc(-9 * var(--segment-deg) / 2));
    }
    
    .wheel div:nth-child(6) {
      background-color: yellow;
      transform: rotate(calc(-11 * var(--segment-deg) / 2));
    }
    
    
    ..wheel div {transform:none!important}
    <div class='wheel_container'>
      <div class='wheel'>
        <div><span>Apple</span></div>
        <div><span>Durian</span></div>
        <div><span>Banana</span></div>
        <div><span>Mango</span></div>
        <div><span>Strawberry</span></div>
        <div><span>Jackfruit</span></div>
      </div>
    </div>

    In order to do an 8-segment wheel, you will need a --segment-deg:45 and a different .wheel div width. I'm using width: calc( 2 * var(--radius ) / 2.414);, where 2.414 is the tangent of (180 - 45) / 2.

    let a = 67.5;
    const rad = Math.PI / 180;
    
    console.log((Math.tan( a * rad)))
    *{margin:0;padding:0}
    
    
    .wheel_container {
      position: relative;
      
      --wheel-size: 360px;
      width: var(--wheel-size);
      height: var(--wheel-size);
      
      margin-bottom: 2.4em;
    }
    
    .wheel {
      display: flex;
      justify-content: center;
      
      position: relative;
      overflow: hidden;
      
      width: var(--wheel-size);
      height: var(--wheel-size);
      
      border-radius: 50%;
      background-color: aquamarine;
      --segment-deg: 45deg;
    }
    
    .wheel div {
      display: flex;
      justify-content: center;
      align-items: center;
      
      position: absolute;
      
      
      --radius: calc(var(--wheel-size) / 2 );
      height: var(--radius);
      
      width: calc( 2 * var(--radius ) / 2.414);
      clip-path: polygon(0 0, 50% 100%, 100% 0);
      
      transform-origin: bottom;
      writing-mode: vertical-rl;
    }
    
    .wheel div > span {
      font-weight: 500;
      font-size: 1rem;
      text-align: center;
      color: rgba(0, 0, 0, 0.7);
    }
    
    .wheel div:nth-child(1) {
      background-color: beige;
      transform: rotate(calc(-1 * var(--segment-deg) / 2));
    }
    
    .wheel div:nth-child(2) {
      background-color: blueviolet;
      transform: rotate(calc(-3 * var(--segment-deg) / 2));
    }
    
    .wheel div:nth-child(3) {
      background-color: crimson;
      transform: rotate(calc(-5 * var(--segment-deg) / 2));
    }
    
    .wheel div:nth-child(4) {
      background-color: orange;
      transform: rotate(calc(-7 * var(--segment-deg) / 2));
    }
    
    .wheel div:nth-child(5) {
      background-color: violet;
      transform: rotate(calc(-9 * var(--segment-deg) / 2));
    }
    
    .wheel div:nth-child(6) {
      background-color: yellow;
      transform: rotate(calc(-11 * var(--segment-deg) / 2));
    }
    
    
    .wheel div:nth-child(7) {
      background-color: red;
      transform: rotate(calc(-13 * var(--segment-deg) / 2));
    }
    
    .wheel div:nth-child(8) {
      background-color: blue;
      transform: rotate(calc(-15 * var(--segment-deg) / 2));
    }
    <div class='wheel_container'>
      <div class='wheel'>
        <div><span>Apple</span></div>
        <div><span>Durian</span></div>
        <div><span>Banana</span></div>
        <div><span>Mango</span></div>
        <div><span>Strawberry</span></div>
        <div><span>Jackfruit</span></div>
        
        <div><span>red</span></div>
        <div><span>blue</

    reply
    0
  • Cancelreply