Split CSS circle into 3 equal parts with linear gr

2019-05-21 11:01发布

问题:

I'm trying to create a progress indicator - and to a certain extent I've done it, but it's not what I want to end up with. Here's an image:

As you can see the indicator shows 1 third, 2 thirds, then full. This is what I want to achieve except that I want the first, second and third sections to be different colors, so I would end up with 3 equal slices in 3 different colors starting at 12 o'clock.

Here's the code - I've removed the centre section because it's not relevant to the question: (the CSS is in Less syntax)

.timeline {
    h3 {
        position:relative;

        & > .step {
            background-color:@accentColor;
            background-clip:padding-box;
            border:solid 1px @divider;
            border-radius:50%;
            height:52px;
            position:absolute;
            top:0;
            left:0;
            width:52px;

            &.one {
                background-image: linear-gradient(210deg, transparent 50%, white 50%),
                    linear-gradient(90deg, white 50%, transparent 50%);
            }
            &.two {
                background-image: linear-gradient(90deg, transparent 50%, @accentColor 50%),
                    linear-gradient(144deg, white 50%, transparent 50%);
            }
            &.three {}
        }
    }
}

<div class="timeline">
    <h3><span class="step one"></span></h3>
    <h3><span class="step two"></span></h3>
    <h3><span class="step three"></span></h3>
</div>

I'm not really undersanding the interaction between the gradients, and despite several edits I'm no nearer to any kind of solution.

Is this possible using linear gradients or do I need to look again?

回答1:

It isn't possible to get three different colored parts using the gradient approach. A much better option would be the one described in this answer. I am adding this answer only to explain how the gradient actually works because I believe that is also a part of your question.

For Step One:

  • There are two gradients that are there on your .step element. When multiple images are added to an element, the one specified first is the topmost layer and the last one is the bottom layer.
  • The lower layer of the gradient divides the element into two halves (widthwise). It is white for 50% and transparent for the other (see first block in snippet). The transparent portion should show the background-color of the .step element through (but here it does not because I've wantedly not set any background-color to the .step element).
  • The upper layer of the gradient is an angled gradient (210deg) which again is white for 50% (the lower part) and transparent for the other. The top part being transparent will show the color of the layers below it.
  • Now imagine the output of the second block (in the below snippet) being placed on top of the first one. The angled bottom portion will be white in color (due to the upper layer). The upper portion is transparent in the top layer and so will show the lower layer's white color for half (widthwise) and the actual background color of the .step element for rest.
  • So the end output is what you see in the third block. The bottom white part is produced by the top layer, the red portion is produced by the bottom layer of the gradient, the black portion is actually transparent but if you add a background-color to the element then that color will show through.

.timeline h3 {
  position: relative;
}
.timeline h3 > .step {
  border: solid 1px yellowgreen;
  border-radius: 50%;
  height: 52px;
  position: absolute;
  top: 0;
  left: 0;
  width: 52px;
}
.timeline h3 > .step.part-one {
  background-image: linear-gradient(90deg, white 50%, transparent 50%);
}
.timeline h3 > .step.part-two {
  background-image: linear-gradient(210deg, transparent 50%, white 50%);
}
.timeline h3 > .step.one {
  background-image: linear-gradient(210deg, transparent 50%, white 50%), linear-gradient(90deg, red 50%, transparent 50%);
}
/* just for demo */

div {
  height: 100px;
}
body {
  background: black;
}
<div class="timeline">
  <h3><span class="step part-one"></span></h3>
</div>
<div class="timeline">
  <h3><span class="step part-two"></span></h3>
</div>
<div class="timeline">
  <h3><span class="step one"></span></h3>
</div>


For Step Two:

  • Similar to the step one, here also there are two gradients and the below snippet has them broken down into layers.
  • The top layer is white for one half and has the same color as the element's background-color for the other half (widthwise). This provides the appearance of 50% progress.
  • The lower layer is an angled gradient which is white for 50% and transparent for the rest. Its angle means that the transparent portion is at the bottom and the white portion is on the top.
  • Now again imagine placing the second block on top of the first block. It will show the color that we have given for the right half, the other half being transparent will show the background color of the lower layer. The lower layer is also transparent for the bottom part and so it will show through the element's background color in that area and the rest will be white (I've made it red to show which portion of the background is produced by the lower layer).

.timeline h3 {
  position: relative;
}
.timeline h3 > .step {
  border: solid 1px yellowgreen;
  border-radius: 50%;
  height: 52px;
  position: absolute;
  top: 0;
  left: 0;
  width: 52px;
}
.timeline h3 > .step.part-one {
  background-image: linear-gradient(144deg, white 50%, transparent 50%);
}
.timeline h3 > .step.part-two {
  background-image: linear-gradient(90deg, transparent 50%, yellowgreen 50%);
}
.timeline h3 > .step.two {
  background-image: linear-gradient(90deg, transparent 50%, yellowgreen 50%), linear-gradient(144deg, red 50%, transparent 50%);
}
/* just for demo */

div {
  height: 100px;
}
body {
  background: black;
}
<div class="timeline">
  <h3><span class="step part-one"></span></h3>
</div>
<div class="timeline">
  <h3><span class="step part-two"></span></h3>
</div>
<div class="timeline">
  <h3><span class="step two"></span></h3>
</div>


For Step Three:

  • You actually don't need a gradient at all here. All you need is only a full solid color.

    .timeline h3 {
      position: relative;
    }
    .timeline h3 > .step {
      background: yellowgreen;
      border: solid 1px yellowgreen;
      border-radius: 50%;
      height: 52px;
      position: absolute;
      top: 0;
      left: 0;
      width: 52px;
    }
    /* just for demo */
    
    div {
      height: 100px;
    }
    body {
      background: black;
    }
    <div class="timeline">
      <h3><span class="step three"></span></h3>
    </div>

  • Or, you can use two gradients also to mimic the solid fill like here.

    .timeline h3 {
      position: relative;
    }
    .timeline h3 > .step {
      background: yellowgreen;
      border: solid 1px yellowgreen;
      border-radius: 50%;
      height: 52px;
      position: absolute;
      top: 0;
      left: 0;
      width: 52px;
    }
    .timeline h3 > .step.three {
      background-image: linear-gradient(90deg, transparent 50%, yellowgreen 50%), linear-gradient(90deg, yellowgreen 50%, transparent 50%);
    }
    
    /* just for demo */
    
    div {
      height: 100px;
    }
    body {
      background: black;
    }
    <div class="timeline">
      <h3><span class="step three"></span></h3>
    </div>


Complete Solution:

Now coming to the final bit, you need to place a smaller circle with a different color on top to make the fill look like it is only the border which is getting filled.

.timeline h3 {
  position: relative;
}
.timeline h3 > .step {
  background: yellowgreen;
  border: solid 1px yellowgreen;
  border-radius: 50%;
  height: 52px;
  position: absolute;
  top: 0;
  left: 0;
  width: 52px;
}
.timeline h3 > .step.one {
  background-image: linear-gradient(210deg, transparent 50%, #ffffff 50%), linear-gradient(90deg, #ffffff 50%, transparent 50%);
}
.timeline h3 > .step.two {
  background-image: linear-gradient(90deg, transparent 50%, yellowgreen 50%), linear-gradient(144deg, #ffffff 50%, transparent 50%);
}
.timeline h3 > .step.three {
  background-image: linear-gradient(90deg, transparent 50%, yellowgreen 50%), linear-gradient(90deg, yellowgreen 50%, transparent 50%);
}
.timeline h3 > .step:after {
  position: absolute;
  content: '';
  height: calc(100% - 10px);
  width: calc(100% - 10px);
  top: 0px;
  left: 0px;
  padding: 5px;
  border-radius: inherit;
  background-color: blue;
  background-clip: content-box;
  /* meaning the background will be transparent in the padding area */
}
/* just for demo */

div {
  height: 100px;
}
body {
  background: black;
}
<div class="timeline">
  <h3><span class="step one"></span></h3>
</div>
<div class="timeline">
  <h3><span class="step two"></span></h3>
</div>
<div class="timeline">
  <h3><span class="step three"></span></h3>
</div>