Canvas && CSS && SVG 三種實現儀表盤的方式

先上效果圖:css

這種圖形你們應該都見過,俗稱儀表盤,固然,上圖只是個最基本的儀表盤架子,可能在實際場景中還會其餘不少花裏胡哨的點綴,那些暫且無論,不是關鍵,這東西常常見到,但還沒親自上手在代碼層面實現過,最近作的一個需求剛好有這個場景,這裏概括一下html

Canvas 實現

大部分狀況下,對於這種偏可視化的元素,通常都選擇使用 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進行構建瀏覽器

CSS 實現

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片斷:

<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用於繪製半圓軌道,第二個用於繪製真正的進度條,經過設置這兩個 pathd屬性,便可實現效果,實現的方法有不少,本文選取的方法是經過控制 pathstroke-dasharraystroke-dashoffset進行實現,不熟悉這兩個屬性的能夠參考文章:

效果以下:

小結

css的新特性 varcalc還挺好用的嘛

本文示例的 Live Demo已經上傳,感興趣的能夠親自試下

相關文章
相關標籤/搜索