First, the effect picture:

You’ve all seen the graphics, commonly known as the dashboard, of course, above is just a basic dashboard shelf, may still in actual scene many other fussy ornament, whatever that is not the key, it often meet, but not true in person to fit in the code level, recently had a demand to have this scenario, up to here

Canvas implementation

In most cases, canvas is usually used to draw such visual elements. Now it has been 9120 years, and there is no problem to use canvas online

Instrument panel as a whole is a complex graph, and the complex graph is composed of simple graphics, as long as all the simple graph composition of the instrument panel is drawn out, and then combined, the whole instrument panel will be drawn out naturally

Therefore, firstly, the instrument panel is decomposed into the basic graph that can be drawn by Canvas. Its main body actually consists of two arcs, one is the semicircular track with blue bottom and the other is the red arc representing progress. In fact, both are arcs, and Canvas just has the ability to draw arcs, namely:

ctx.arc
Copy the code

For dynamic drawing, just work with requestAnimationFrame

const trackW = 6
const rx = 500
const ry = 500
const radius = 400
const innerLineW = 20
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
function draw (toAngle, currentAngle = Math.PI) {
  ctx.clearRect(0.0, canvas.width, canvas.height)
  // Semicircular orbit
  ctx.beginPath()
  ctx.strokeStyle = '#ad80fc'
  ctx.lineWidth = trackW
  ctx.arc(rx, ry, radius, Math.PI, 0.false)
  ctx.stroke()
  / / arc
  ctx.beginPath()
  ctx.lineCap = 'round'
  ctx.strokeStyle = '#fe4d55'
  ctx.lineWidth = innerLineW
  ctx.arc(rx, ry, radius, Math.PI, currentAngle, false)
  ctx.stroke()
  if (currentAngle < toAngle) {
    currentAngle += 0.02
    if (currentAngle > toAngle) currentAngle = toAngle
    requestAnimationFrame((a)= > {
      draw(toAngle, currentAngle)
    })
  }
}
draw(1.5 * Math.PI)
Copy the code

It can be completed by adding variable definitions, curly braces and dozens of lines of code, which shows that canvas drawing is very convenient. Therefore, in the field of visualization, for example, some libraries or UI components are basically built on canvas

CSS implementation

Canvas is essentially using JS to manipulate browser API for rendering, but UI rendering should be done by CSS. It seems that drawing with JS directly will affect performance (in fact, it is not), and CSS is not as smooth as CSS. In fact, CSS can do it completely

The instrument panel is decomposed from the perspective of CSS, which is also two arcs. By setting the border-radius attribute, the element can present the full circle effect, and then masked with a rectangular element. The part to be displayed is the arc, and the dynamic drawing effect is presented by controlling the area of the mask

<div class="arc-wrapper">
  <p class="track-arc"></p>
  <div class="round-box">
    <p class="round"></p>
  </div>
</div>
Copy the code
:root {
  --arcRadius: 200px;
  --rectWidth: calc(var(--arcRadius) * 2);
  --trackWidth: 4px;
  --roundWidth: 10px;
}
.arc-wrapper {
  position: relative;
  margin: 0 auto;
  width: var(--rectWidth);
  height: var(--arcRadius);
  overflow: hidden;
  background-color: pink;
}
.track-arc {
  width: 100%;
  height: var(--rectWidth);
  box-sizing: border-box;
  border-radius: 50%;
  border: var(--trackWidth) solid #ad80fc;
}
.round-box {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  transform-origin: 50% 100%;
  transform: rotate(-45deg);
  z-index: 20;
}
.round {
  width: 100%;
  height: var(--rectWidth);
  box-sizing: border-box;
  border-radius: 50%;
  border: var(--roundWidth) solid #fe4d55;
}
Copy the code

There’s not much code, and nothing hard to understand, except that the effect seems to be slightly tweaked:

Since the line of an arc has width, it can not be ignored in a mathematical sense. A canvas arc is drawn according to the coordinates and radius of the center of the circle. The drawn arc will be automatically adjusted according to the width of the arc line, that is, the radius of the arc is the distance between the center of the arc line and the coordinates of the center of the circle

For an arc drawn by CSS, the radius of this arc is the coordinate distance between the outermost edge of the arc and the center of the circle:

The problem can be solved by reducing the radius of the orbital semicircle and shifting it by a certain amount:

.track-arc {
  --trackArcSize: calc(var(--rectWidth) - var(--roundWidth) + var(--trackWidth));
  /* Size changes */
  width: var(--trackArcSize);
  height: var(--trackArcSize);
  box-sizing: border-box;
  border-radius: 50%;
  border: var(--trackWidth) solid #ad80fc;
  /* Position offset */
  transform: translate(calc(var(--roundWidth) / 2 - var(--trackWidth) / 2), calc(var(--roundWidth) / 2 - var(--trackWidth) / 2));
}
Copy the code

And then it gets a lot nicer:

The effect shown above is directly truncated:

This is basically why most people don’t use CSS to draw dashboards. Canvas can be solved simply by setting ctx.lineCap = ’round’. It looks like CSS is out of solutions

At first glance, it seems that there is no good way to do this, but if you think about it a little bit, isn’t it a rounded corner? It is completely within the scope of CSS, but the implementation is not so direct

The method is very simple, is to use a rounded rectangle to cover the top of the arc, the top of the rectangle of the arc itself covered, the rounded rectangle is regarded as the top of the arc, so it looks like the head of the circle

<div class="round-box">
  <p class="round"></p>
  <p class="dot-r-box">
    <span class="dot-r"></span>
  </p>
</div>
Copy the code
.dot-r-box {
  position: absolute;
  right: 0;
  bottom: 0;
  width: var(--roundWidth);
  height: var(--dotHeight);
  background-color: var(--backColor);
}
.dot-r {
  display: inline-block;
  width: 100%;
  height: 100%;
  /* The 100px is just for maximum roundness */
  border-bottom-left-radius: 100px;
  border-bottom-right-radius: 100px;
  background-color: var(--roundColor);
}
Copy the code

The effect is as follows:

Rounded corner top get

You can do the same thing for the top left of the arc

However, there is a slight note about the top of the left, because it exists to serve as the left break point of the arc, but when the progress is 0, the arc should not be displayed at all, or when the progress is very small, the arc should not be displayed as large as the height of the dot-L, which gives away:

But in turn thought, the general practical scenario, even if progress is 0, we actually to look more in line with common sense intuition, will make arc show a little bit, as long as the height of the dot – l is not too big, or as long as the width of the circular arc not too wide, actually reserved this arc can be completely cover, not betrayed

SVG implementation

SVG means scalable vector graphics, since it is a graph, of course, you can achieve graphics, because of its professional, compared with the above two methods, SVG implementation is simpler

First, define an SVG fragment:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="border: 1px solid red; width: 500px; height: 300px;">
  <path fill="none" stroke-linecap="round" class="outerArc" />
  <path fill="none" stroke-linecap="round" class="innerArc" />
</svg>
Copy the code

The first outerArc is used to draw a semicircle and the second is used to draw a real progress bar. You can do this by setting the D property of these two paths. There are many ways to do this. The method selected in this article is implemented by controlling the stroke-Dasharray and stroke-dashoffset of path. If you are not familiar with these two attributes, please refer to the article:

  • Zhang Xinxu pure CSS to achieve handsome SVG path stroke animation effect
  • A first look at SVG Path animations

The effect is as follows:

summary

The new CSS features var and calc are quite useful

The Live Demo of this article has been uploaded, so you can try it out for yourself