canvas && CSS 两种实现仪表盘的方式

栏目: Html5 · 发布时间: 6年前

内容简介:先上效果图:这种图形大家应该都见过,俗称仪表盘,当然,上图只是个最基本的仪表盘架子,可能在实际场景中还会其他很多花里胡哨的点缀,那些暂且不管,不是关键,这东西经常见到,但还没亲自上手在代码层面实现过,最近做的一个需求恰好有这个场景,这里归纳一下大部分情况下,对于这种偏可视化的元素,一般都选择使用

先上效果图:

canvas && CSS 两种实现仪表盘的方式

这种图形大家应该都见过,俗称仪表盘,当然,上图只是个最基本的仪表盘架子,可能在实际场景中还会其他很多花里胡哨的点缀,那些暂且不管,不是关键,这东西经常见到,但还没亲自上手在代码层面实现过,最近做的一个需求恰好有这个场景,这里归纳一下

canvas实现

大部分情况下,对于这种偏可视化的元素,一般都选择使用 canvas 来进行绘制,现在已经 9120 年了,线上使用 canvas 完全没问题

仪表盘整体是一个复杂图形,而复杂图形是由简单图形组合而成,只要把所有组成这个仪表盘的简单图形绘制出来,再进行组合,整个仪表盘自然也就绘制出来了

所以,首先对仪表盘进行分解,分解成 canvas 能绘制出的基本图形,其主体其实就两个圆弧,一个是底部蓝色的半圆轨道,一个是代表进度的红色圆弧,其实都是圆弧, canvas 刚好有绘制圆弧的能力,即:

ctx.arc
复制代码

至于动态绘制,只需要配合 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)
  // 半圆轨道
  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 进行构建

css绘制

canvas 实质上就是借助 js 操纵浏览器 API 进行渲染,然而 UI 渲染这种事情本应该交给 CSS 来做才是,感觉用 js 直接画多影响性能啊(实际上并不),哪有 css 来的流畅,实际上, css 完全可以做到

css 的角度对仪表盘进行分解,同样还是两个圆弧,通过设置 border-radius 属性即可让元素呈现整圆效果,然后再用一个矩形元素进行遮罩,决定展现出来的部分,即为圆弧,通过控制遮罩的面积来呈现动态绘制的效果

<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;
}
复制代码

其实没多少代码,也没什么难以理解的,只不过效果似乎微调:

canvas && CSS 两种实现仪表盘的方式

由于圆弧的线是存在宽度的,并不是数学意义上的可以忽略, canvas 绘制圆弧,是根据圆心坐标和半径进行绘制的,绘制出来的圆弧会自动根据圆弧 line 的宽度进行调整,即圆弧的半径是圆弧线的中心位置与圆心坐标距离

canvas && CSS 两种实现仪表盘的方式

而通过 css 绘制的圆弧,此圆弧的半径则是圆弧最外层边线与圆心的坐标距离:

canvas && 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));
}
复制代码

然后就顺眼多了:

canvas && CSS 两种实现仪表盘的方式

然而,还有个问题,一般为了呈现更加圆润的效果,设计稿上圆弧的断点处一般都是圆头:

canvas && CSS 两种实现仪表盘的方式

而上述呈现出来的效果是直接截断的:

canvas && CSS 两种实现仪表盘的方式

一般人之所以不使用 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);
}
复制代码

效果如下:

canvas && CSS 两种实现仪表盘的方式

圆角顶端 get

同样的,圆弧左边的顶端也可以这么做

canvas && CSS 两种实现仪表盘的方式

不过左边这个顶端有个稍微需要注意的地方,因为其存在的目的是为了当做圆弧的左断点,但是当进度为 0 的时候,圆弧应该是完全不展现的,或者当进度很小的时候,圆弧应该展现的长度还没有 dot-l 的高度大,这样就露馅了:

canvas && CSS 两种实现仪表盘的方式

不过呢转而又一想,一般实际场景中,就算进度为 0 ,我们其实为了看起来更符合常识直觉,也会让圆弧展现一点点出来,只要 dot-l 的高度不是太大,或者说只要圆弧的宽度不要太宽,其实预留的这点圆弧完全就可以 cover 住了,不至于露馅


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Programming Collective Intelligence

Programming Collective Intelligence

Toby Segaran / O'Reilly Media / 2007-8-26 / USD 39.99

Want to tap the power behind search rankings, product recommendations, social bookmarking, and online matchmaking? This fascinating book demonstrates how you can build Web 2.0 applications to mine the......一起来看看 《Programming Collective Intelligence》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具