手動實現一個速度儀表盤

前言

最近正在學習數據可視化, 這裏記錄一下一些心得與成果, 採用的技術是 (svg + react + d3)。 這種實現可視化方式本人我的感受超級不錯,若是你是有必定的基礎的同窗,強烈推薦一下。javascript

效果

總體效果以下:java

clipboard.png

這個是普通的速度儀表盤,有沒有開發太多的動態交互,惟一的交互是點擊圖片最上面的加速減速就可以調整速度了。react

開發思路

搭建開發環境

使用create-react-app建立一個新的項目,添加依賴d3git

yarn add d3

初始化數據

這裏速度範圍是[0, 200], 對應角度範圍我的設置是[150, 390], 很明顯這是一個線性比例尺。速度間隔是2。代碼以下github

const scale = d3.scaleLinear().domain([0, 200]).range([150, 360 + 30])
const ticks = scale.ticks(100) // 200 / 2 => 100

構建外部圈

function Chart(props) {
  const { width, height, margin } = props
  return (
    <svg width={width} height={height}>
      <g transform={`translate(${margin.left}, ${margin.top})`}>
        {
          props.children
        }
      </g>
    </svg>
  )
}

......
export default class Meter extends Component { 
    ...     
    render () {
        // config => {width: xxx, height: xxx, margin: xxx}
        return (
            <div className={'container'}>
                <Chart {...config}>
                    <g>
                        <circle cx={0} cy={0} r={204} fill={'rgba(158, 158, 158, .4)'}></circle>
            <circle cx={0} cy={0} r={196} fill={'#FFF'}></circle>
            <circle cx={0} cy={0} r={200} fill={'transparent'} stroke={'#000'}></circle>
                    </g>
                </Chart>
            </div>
        )
    }
}

上面實際上是繪畫了三個圓, 利用SVG後面的繪製的圖畫,會覆蓋前面的圖畫的特性。先畫最外面,而後最裏面,最後中間的圓。 就把最外層的圈給描繪出來了,效果以下:
clipboard.pngapp

描繪刻度尺

接着上面的代碼結構,咱們開始刻畫刻度尺dom

......
export default class Meter extends Component { 
    ...     
    render () {
        // config => {width: xxx, height: xxx, margin: xxx}
        return (
            <div className={'container'}>
                <Chart {...config}>
                    <g>
                        <circle cx={0} cy={0} r={204} fill={'rgba(158, 158, 158, .4)'}></circle>
            <circle cx={0} cy={0} r={196} fill={'#FFF'}></circle>
            <circle cx={0} cy={0} r={200} fill={'transparent'} stroke={'#000'}></circle>
                    </g>
                    <g fill={'transport'} stroke={'#000000'}>
                          {
                            ticks.map((tick) => {
                              let IS_20_TIME = tick % 20 === 0
                              let title = IS_20_TIME ? <text x={160} dominantBaseline={'middle'} textAnchor={'end'}>{tick}</text> : ''
                              return (
                                <g transform={`rotate(${scale(tick)})`} key={tick}>
                                  <path d={`M165, 0L185,0`} strokeWidth={IS_20_TIME ? 3 : 1}></path>
                                  {title}
                                </g>
                              )
                            })
                          }
                    </g>
                </Chart>
            </div>
        )
    }
}

這裏刻畫刻度尺,個人思路很簡單,刻度尺是對速度大小的描述,而速度跟角度又是線性相關,反過來,速度對應角度。因此,我只是須要根據速度所對應的角度,而對水平刻度進行旋轉便可。效果你們能夠看到:
clipboard.pngsvg

指向針

指向針其實就是一個圓 + 三角形的組合,代碼以下:學習

<circle cx={0} cy={0} r={10} fill={'#'}></circle>
            <path d={`M-20, 5L-20, -5L130, 0Z`} transform={`rotate(150)`}>
              <animateTransform ></animateTransform>
            </path>

上面本人實現的比較粗糙,你們能夠把這個封裝成一個shape, 之後能夠直接複用的,後面若是須要旋轉,能夠經過<g>元素來實現。 this

到這一步,一個簡單的儀表盤就初具原型了

clipboard.png

控制指針轉動

指針的轉動是根據速度來的,因此咱們須要先定義一個speed的狀態。

constructor(props) {
    super(props)
    
    this.state = {
      speed: 0
    }
  }

接下來,咱們須要把speed映射到指針上面。怎麼處理呢
還記得前面定義的scale,這個是一個線性比例尺,經過它咱們可以獲取不一樣速度對應的角度
咱們把上面的指向針代碼進行改造

const {speed} = this.state
......
<circle cx={0} cy={0} r={10} fill={'#'}></circle>
<path d={`M-20, 5L-20, -5L130, 0Z`} transform={`rotate(${scale(speed)})`}>
  <animateTransform ></animateTransform>
</path>

這樣咱們設置不一樣的speed就能在頁面控制指針指向不一樣的刻度尺。

速度標識區間

所謂的速度標識區間,其實就是幾段圓弧,經過不一樣的顏色來告知進入不一樣的速度區間。
這裏我對圓弧進行了封裝,底層經過d3的arc方法來建立圓弧。

function LArc(props) {
  const { start, end, color } = props
  
  let _arc = d3.arc()({
    innerRadius: 165,
    outerRadius: 185,
    startAngle: Math.PI * 2 * (scale(start) + 90) / 360,
    endAngle: Math.PI * 2 * (scale(end) + 90) / 360
  })
  
  return (
    <path d={_arc} fill={color}></path>
  )
}

這裏其實還有一個問題,就是須要先加載速度標識區間,而後再去添加刻度尺,否則標識區間會覆蓋刻度尺(切記)。
這樣一個基本速度儀表盤就出來了

clipboard.png

若是你能明白上面的實現思路,我以爲你能夠本身實現一個時鐘

若是你想了解更多:好比指示器如何實現的
請參考
https://github.com/cookhot/i-... (本人會在裏面不按期增長新圖表哦)

相關文章
相關標籤/搜索