先上效果图:css
这种图形你们应该都见过,俗称仪表盘,固然,上图只是个最基本的仪表盘架子,可能在实际场景中还会其余不少花里胡哨的点缀,那些暂且无论,不是关键,这东西常常见到,但还没亲自上手在代码层面实现过,最近作的一个需求刚好有这个场景,这里概括一下html
大部分状况下,对于这种偏可视化的元素,通常都选择使用 canvas
来进行绘制,如今已经 9120
年了,线上使用 canvas
彻底没问题git
仪表盘总体是一个复杂图形,而复杂图形是由简单图形组合而成,只要把全部组成这个仪表盘的简单图形绘制出来,再进行组合,整个仪表盘天然也就绘制出来了github
因此,首先对仪表盘进行分解,分解成 canvas
能绘制出的基本图形,其主体其实就两个圆弧,一个是底部蓝色的半圆轨道,一个是表明进度的红色圆弧,其实都是圆弧,canvas
恰好有绘制圆弧的能力,即:web
ctx.arc
复制代码
至于动态绘制,只须要配合 requestAnimationFrame
便可canvas
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)
// 半圆轨道
ctx.beginPath()
ctx.strokeStyle = '#ad80fc'
ctx.lineWidth = trackW
ctx.arc(rx, ry, radius, Math.PI, 0, false)
ctx.stroke()
// 圆弧
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(() => {
draw(toAngle, currentAngle)
})
}
}
draw(1.5 * Math.PI)
复制代码
加上变量定义,花括号等几十行代码便可完成,因而可知,canvas
绘图仍是很方便的,因此在可视化领域,例如一些库或者UI
组件基本上都是以canvas
进行构建浏览器
canvas
实质上就是借助 js
操纵浏览器 API
进行渲染,然而 UI
渲染这种事情本应该交给 CSS
来作才是,感受用 js
直接画多影响性能啊(实际上并不),哪有 css
来的流畅,实际上,css
彻底能够作到app
从 css
的角度对仪表盘进行分解,一样仍是两个圆弧,经过设置 border-radius
属性便可让元素呈现整圆效果,而后再用一个矩形元素进行遮罩,决定展示出来的部分,即为圆弧,经过控制遮罩的面积来呈现动态绘制的效果svg
<div class="arc-wrapper">
<p class="track-arc"></p>
<div class="round-box">
<p class="round"></p>
</div>
</div>
复制代码
: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;
}
复制代码
其实没多少代码,也没什么难以理解的,只不过效果彷佛微调:wordpress
因为圆弧的线是存在宽度的,并非数学意义上的能够忽略,canvas
绘制圆弧,是根据圆心坐标和半径进行绘制的,绘制出来的圆弧会自动根据圆弧 line
的宽度进行调整,即圆弧的半径是圆弧线的中心位置与圆心坐标距离
而经过 css
绘制的圆弧,此圆弧的半径则是圆弧最外层边线与圆心的坐标距离:
知道了问题其实就好解决了,只要缩减轨道半圆的半径,并对其进行必定的偏移便可:
.track-arc {
--trackArcSize: calc(var(--rectWidth) - var(--roundWidth) + var(--trackWidth));
/* 尺寸改变 */
width: var(--trackArcSize);
height: var(--trackArcSize);
box-sizing: border-box;
border-radius: 50%;
border: var(--trackWidth) solid #ad80fc;
/* 位置偏移 */
transform: translate(calc(var(--roundWidth) / 2 - var(--trackWidth) / 2), calc(var(--roundWidth) / 2 - var(--trackWidth) / 2));
}
复制代码
而后就顺眼多了:
而上述呈现出来的效果是直接截断的:
通常人之因此不使用 css
来绘制仪表盘,基本都是由于这个缘由,canvas
简简单单经过设置一个 ctx.lineCap = 'round'
就能解决的问题。彷佛 css
无解了
乍一看好像确实没什么好办法,但稍微思考下,这不就是一个圆角吗,彻底在css
能力范围内啊,只不过实现的方式不太那么直接罢了
方法很简单,就是使用一个圆角矩形覆盖在圆弧的顶端,将圆弧自己的矩形顶端覆盖住,圆角矩形当作是圆弧的顶端,这样视觉上看起来不就是圆头了吗
<div class="round-box">
<p class="round"></p>
<p class="dot-r-box">
<span class="dot-r"></span>
</p>
</div>
复制代码
.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%;
/* 这里的100px只是为了呈现出最大限度的圆角 */
border-bottom-left-radius: 100px;
border-bottom-right-radius: 100px;
background-color: var(--roundColor);
}
复制代码
效果以下:
圆角顶端 get
一样的,圆弧左边的顶端也能够这么作
不过左边这个顶端有个稍微须要注意的地方,由于其存在的目的是为了当作圆弧的左断点,可是当进度为 0
的时候,圆弧应该是彻底不展示的,或者当进度很小的时候,圆弧应该展示的长度尚未 dot-l
的高度大,这样就露馅了:
不过呢转而又一想,通常实际场景中,就算进度为 0
,咱们其实为了看起来更符合常识直觉,也会让圆弧展示一点点出来,只要 dot-l
的高度不是太大,或者说只要圆弧的宽度不要太宽,其实预留的这点圆弧彻底就能够 cover
住了,不至于露馅
SVG
意为可缩放矢量图形,既然是图形固然就能实现图形,因为其专业性,相比于上述两种方法来讲,SVG
的实现更加简单
首先,定义一段 SVG
片断:
<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>
复制代码
两个 path
元素,第一个 outerArc
用于绘制半圆轨道,第二个用于绘制真正的进度条,经过设置这两个 path
的 d
属性,便可实现效果,实现的方法有不少,本文选取的方法是经过控制 path
的 stroke-dasharray
和 stroke-dashoffset
进行实现,不熟悉这两个属性的能够参考文章:
效果以下:
css
的新特性 var
和 calc
还挺好用的嘛
本文示例的 Live Demo已经上传,感兴趣的能够亲自试下