Recently, in a mobile terminal requirement, there is a need to achieve the effect of the progress bar with the shape of a ring. I would like to share with you some of the progress bars drawn in the ring in the APP that are very poor

There are two ways to achieve the dynamic progress bar effect, CSS drawing and SVG drawing, both have their advantages and disadvantages.

However, unless some scenes cannot use SVG, it is better to use simpler and more convenient SVG to draw, especially in THE APP H5 page, 0.5px problem will lead to CSS drawing performance can not achieve the desired effect, let’s take a look at the specific implementation and performance results

The CSS draws a circular progress bar

Implementation approach

First, let’s look at the following static two-color ring of rotation:

Inside the circle, you can see alternating red and orange cycles, which at first glance doesn’t look like much, but what if we covered half of the circle? Let’s cover the left and right halves separately:

Is it just half of a complete circle? Combine these two circles and control the tansform Angle of the two circles to make a complete circle. This is how to draw circles in CSS. Draw two different semicircles and control the progress display under different percentages

borderDraw progress bar

The idea is to use different CSS styles to draw the difference between circles

The simplest is to use a border to draw the circle

Use Transition to realize the transition animation when the progress changes

// Background ring
.circle-progress-border {
  position: absolute;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  border: 8px solid $defaultColor;

  // Semicircle display box
  .wrapper {
    overflow: hidden;
    position: absolute;
    width: 48px;
    height: 96px;
    border-radius: 50%;
  }
  // Ring unified style
  .circle {
    width: 48px;
    height: 96px;
    border-radius: 50%;
    position: absolute;
  }

  / / the left semicircle
  .wrapper-left {
    left: 0;
  }
  .circle-left {
    left: 0;
    transform-origin: center;
    border: 8px solid transparent;
    border-bottom: 8px solid $runningColor;
    border-right: 8px solid $runningColor;
  }
  / / right semicircle
  .wrapper-right {
    right: 0;
  }
  .circle-right {
    right: 0;
    transform-origin: center;
    border: 8px solid transparent;
    border-top: 8px solid $runningColor;
    border-left: 8px solid $runningColor; }}Copy the code
<div className="circle-progress-border">
  <div className="wrapper wrapper-left">
    <div className="circle circle-left" style={{ transform: "rotate(Xdeg) ",transition: "all 1s}} "/ >
  </div>
  <div className="wrapper wrapper-right">
    <div className="circle circle-right" style={{ transform: "rotate(Xdeg) ",transition: "all 1s}} "/ >
  </div>
</div>
Copy the code

BorderDrawing flaw

Although Border is said to be able to draw the circle, such drawing in some H5 projects that will transform PX into REM will cause the deviation of the transform due to the rem problem of 0.5px, and the marked position in the figure will be offset. The progress bar (orange circle) cannot completely cover the background color (red circle)

To solve this problem, you can use Div or SVG to draw a circle

I only found this problem when I opened the H5 page in some apps

DivDraw progress bar

Div is used to draw the progress bar of the circle, which has the same idea as border, and the code is very similar. Both draw two semicircular circles, and then use transform to control the rotation Angle to achieve the progress percentage effect

However, Div uses background to draw two orange and red semicircles to form an outer circle, and then covers a circle in the center. The central circle and outer circle are combined to generate a circle, and the two semicircles outside the transform are used to realize the progress bar effect

.circle-progress {
  / / outer circle
  width: 96px;
  height: 96px;
  border-radius: 50%;
  position: absolute; ..circle {
    width: 48px;
    height: 96px;
    border-radius: 50%;
    background-position: 0 0.100% 0.100% 100%.0 100%;
    background-repeat: no-repeat, no-repeat;
  }

  .circle-left {
    left: 0;
    transform-origin: center;
    background-size: 50% 100%.50% 100%;
    background-image: linear-gradient($defaultColor.$defaultColor), linear-gradient($runningColor.$runningColor);
  }
  .circle-right {
    right: 0;
    transform-origin: center;
    background-size: 50% 100%.50% 100%;
    background-image: linear-gradient($runningColor.$runningColor), linear-gradient($defaultColor.$defaultColor);
  }

  / / the center circle
  .circle-center {
    width: 80px;
    height: 80px;
    border-radius: 50%;
    background: linear-gradient(180deg, rgba(255.242.194.1) 0%, rgba(255.173.36.1) 100%); }}Copy the code
<div className="circle-progress">
  <div className="wrapper wrapper-left">
    <div className="circle circle-left" style={{ transform: "rotate(Xdeg) ",transition: "all 1s}} "/ >
  </div>
  <div className="wrapper wrapper-right">
    <div className="circle circle-right" style={{ transform: "rotate(Xdeg) ",transition: "all 1s}} "/ >
  </div>
  <div className="circle-center" />
</div>
Copy the code

Left: outer circle right: outer circle + center circle

Div drawing effects and defects

Once we encounter the offset problem of the border drawing method mentioned above, although the circle drawn by Div will not have color difference, if the circle is relatively small, it may have obvious upper ring width (center circle is not in the center) at the bottom. This situation rarely occurs, and the browser will basically not have this problem

Use Clip to simplify the drawing style of the ring progress bar

An outer div. wrapper, half the width, can be replaced with a Clip property. Clipping creates a semicyclic circle by setting the following property values:

clip: rect(0 48px 96px 0);
Copy the code

Using SVG makes it easier to draw a circular progress bar

At the moment, drawing semicircle in SVG is arguably the simplest and least error-prone way to write it

Using SVG, after drawing a circle, the strokeDasharray and strokeDashoffset attributes can be used to directly simulate the progress bar effect, which is more in line with our cognition and does not need other fantastic ideas

Use strokeDasharray to set the offset starting point and the solid line length, use strokeDashoffset to set the offset, the solid line progress bar percentage effect, change the strokeDashoffset value, can realize the progress bar, add a background ring, It’s a complete circle of progress bars

However, some older browsers (IE) do not support SVG, so you need to evaluate the usage scenarios when using SVG

// d perimeter x percentage of progress bars
<svg width="96px" height="96px">
  <circle id="circleBg" className="svg-circle-bg" />
  <circle className="svg-circle-running" strokeDasharray={d} strokeDashoffset={d - x} / >
</svg>
Copy the code
.svg-circle-bg {
  r: 44px;
  stroke-width: 8;
  stroke: rgba(255.161.39.1);
}
.svg-circle-running {
  r: 44px;
  stroke-width: 8;
  transition: all 1s linear;
  stroke: rgba(248.31.30.1);
}
Copy the code

Left: unshaded circle right: shaded circle