A method to dynamically render circular sectors without using HTML5 canvas
P粉731861241
2023-09-02 10:43:07
<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>
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 iswidth: calc( 2 * var(--radius ) / 1.732);
, where 1.732 isMath.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;)
In order to do an 8-segment wheel, you will need a --segment-deg:45 and a different
.wheel div
width. I'm usingwidth: calc( 2 * var(--radius ) / 2.414);
, where 2.414 is the tangent of (180 - 45) / 2.