This is the 14th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

preface

The basic idea

1. Create A half circle of A and B 2. Cover the half of B with A half circle of the same radius with A background color of A 3. Let the semicircle rotate with the center of the circleCopy the code

Step 1: Create a two-color circle

The CSS linear liner-gradient can create multiple colored elements and is a very useful property.

Liner – gradient syntax

<linear-gradient> = linear-gradient([ [ <angle> | to <side-or-corner>]]?<color-stop>[, <color-stop>] +)<side-or-corner> = [left | right] || [top | bottom]

<color-stop> = <color> [ <length> | <percentage> ]?
Copy the code
The following values indicate the direction of the gradient, which can be set using Angle or keyword:<angle>: Specifies the direction (or Angle) of the gradient with an Angle value. To left: Sets the gradient from right to left. Equivalent to: 270deg to right: Sets the gradient from left to right. Equivalent to: 90deg to top: Sets the gradient from bottom to top. Equivalent to: 0deg to bottom: Set gradient from top to bottom. Equivalent to: 180deg. This is the default value and is equivalent to leaving it blank.<color-stop>Used to specify the start and end colors of the gradient:<color>: Specifies the color.<length>: Specifies the start and end color positions with length values. Negative values are not allowed<percentage>: Specifies the starting and ending color positions with percentages.Copy the code

code

.round{
    width:200px;height:200px;
    border-radius: 50%;
    background: yellowgreen;
    background-image: linear-gradient(to right,transparent 50%.# 655 0);
}
Copy the code

Step 2: Use pseudo classes to create semicircles for occlusion

The extended syntax of border-radius is used here.

Set or retrieve objects using rounded borders. Two parameters are separated by slashes (/). You can set one to four values for each parameter. The first parameter indicates the horizontal radius, and the second parameter indicates the vertical radius.

Horizontal radius: If all four parameters are provided, the four corners are applied in the order of top-left, top-right, bottom-right, and bottom-left. If only one is provided, it will be used for all four corners. If two are provided, the first is used for top-left and bottom-right, and the second is used for top-right and bottom-left. If three are provided, the first is used for top-left, the second for top-right, bottom-left, and the third for bottom-right. The vertical radius also follows the above four points.Copy the code

Here is a brief explanation of vertical radius and horizontal radius: because we usually use border-radius as a set of parameters, that is, there is no ‘/’, so the rounded corners are basically full circle. If you look at the picture below, 55pt is the horizontal radius, and 25pt is the vertical radius.

.round::before{
    content:' ';
    display: block;
    margin-left: 50%;
    height:100%;
    background-color: inherit;
    border-radius: 0 100% 100% 0 /50%;
}
Copy the code

For illustration, change the background of the semicircle from Inherit to Yellow.

This section of border-radius: 0 100% 100% 0/50%; To expand, the vertical radius of the upper right and lower right corner is 50% and the horizontal radius is 100%.

    border-top-left-radius: 0px 50%;
    border-top-right-radius: 100% 50%;
    border-bottom-right-radius: 100% 50%;
    border-bottom-left-radius: 0px 50%;
Copy the code

Step 3: Rotate the semicircle

To set the rotation point, use the transform-origin property. We want it to rotate around the center of the circle, which is the midpoint of the left edge of the semicircle, which is transform: 0, 50%, which is transform:left

transform-origin

Sets or retrieves an object to be converted at an origin. This property provides two parameter values. If two are provided, the first is for the x-coordinate and the second is for the y-coordinate. If only one is provided, the value will be used for the abscissa; The ordinate will default to 50%.Copy the code
The transform - origin:<percentage> | <length>| left | center (1) | right] [<percentage> | <length>| top | center (2) | bottom]?<percentage>: Specifies coordinate values as percentages. It can be negative.<length>: Specifies coordinate values with length values. It can be negative. Top: indicates the top center of the origin. 2: indicates the top center of the origin. Specify the origin's ordinate at bottomCopy the code

We then use the transform property rotate() to rotate the semicircle.

.round::before{
    content:' ';
    display: block;
    margin-left: 50%;
    height:100%;
    background-color: inherit;
    border-radius: 0 100% 100% 0 /50%;
    transform-origin: left;
    transform: rotate(.1turn)}Copy the code

However, if you want to handle angles greater than 180 degrees, you need to change the background color of the pseudo-class element.

Write a progress animation

.round::before{
    content:' ';
    display: block;
    margin-left: 50%;
    height:100%;
    background-color: inherit;
    border-radius: 0 100% 100% 0 /50%;
    transform-origin: left;
    transform: rotate(.01turn);
    animation: spin 1s linear infinite,
                changeBG 2s step-end infinite;
}

@keyframes spin{
    to {transform: rotate(.5turn)}}@keyframes changeBG{
    50% {background-color: # 655;}
}
Copy the code

animation-time-function

steps(<integer>[, [ start | end ] ]?) : a step function that takes two arguments. The first argument must be a positive integer, specifying the number of steps of the function. The second parameter, which can be start or end, specifies the point at which the value of each step changes. The second argument is optional and defaults to end. Step-start: equivalent to steps(1, start) step-end: equivalent to steps(1, end)Copy the code

Note that the spin interval is half as long as the changeBG interval, when changing the background color of the pseudo-element semicircle.

Pie charts of different proportions

For example, we need to display pie charts of different proportions based on the numbers written in HTML.

<div class="round">0.3</div>
<div class="round">0.8</div>
Copy the code

The solution is to set animation-delay to negative and set the animation to pause. This means that the animation will start playing immediately, but will automatically advance to the absolute value of the delay value, as if the animation had played for the specified time in the past. So the effect is that the animation skips the specified time and starts in the middle.

.round::before{
    /* omit the same code */
    animation: spin 0.5 s linear infinite,
                changeBG 1s step-end infinite;
    animation-play-state: paused; /* Set animation to pause */
}
Copy the code

Now we’ll read the ratio from the HTML and write the animation-delay to the pseudo class.

/* Because querySelector does not support finding pseudo-classes, you can only create 
const style = document.createElement('style');
document.head.appendChild(style);
const {sheet} = style;
/* Using es6... The rest/spread operator converts an array of classes to an array */
const eles=[...document.getElementsByClassName('round')] 
 eles.forEach((ele,idx) = >{
    const text=ele.textContent
    /* Note to add to the pseudo-class */
    sheet.addRule(`.round:nth-child(${idx+1})::before`.`animation-delay:-${text}s`)})Copy the code

End result

The resources

  1. CSS Reference Manual
  2. What are the horizontal and vertical radii of border-radius?