Vue+D3.js製做儀表圖組件

基於Vue+D3.js的儀表圖組件

實現的效果以下圖所示app

clipboard.png

1.定義data數據,實現組件的可定製化svg

  • panel組件能定製的參數包括如下幾項函數

    config: {
           size: 200,             //panel的大小
           label: '採油量',       //panel的名稱
           min: 0,                //量程的最小值
           max: 100,              //量程的最大值
           majorTicks: 6,         //主刻度的個數
           minorTicks: 4,         //次刻度的個數
           greenColor: '#109618', //區域1顏色
           yellowColor:'#FF9900', //區域2顏色
           redColor:'#DC3912',    //區域3顏色
           duration: 500,         //轉動一次動畫須要的時間
           value: 10}             //panel顯示的值

2.處理數據函數setConfig()字體

  • 處理config數據動畫

    return {
           value: config.value,               
           size: config.size,
           cx: config.size / 2,                 //圓心的x座標
           cy: config.size / 2,                 //圓心的y座標
           label: config.label,
           max: config.max,
           min: config.min,
           range: config.max - config.min,      //量程
           radius: config.size * 0.97 / 2,      //半徑(稍許留白)
           minorTicks: config.minorTicks,         
           majorTicks: config.majorTicks,
           duration: config.duration,
           greenColor: config.greenColor,
           yellowColor : config.yellowColor,
           greenZones: {from: config.min, to: config.min + (config.max - config.min) * 0.75},                           //區域1範圍
           yellowZones: { from: config.min + (config.max - config.min)*0.75, to: config.min +(config.max - config.min)*0.9 } ,
           redColor:config.redColor,            //區域2範圍
           redZones: { from: config.min +(config.max - config.min)*0.9, to: config.max }                               //區域3範圍
         }

3.render函數ui

  • 3.1獲取數據this

    let {size, cx, cy, radius, range,  min, max, label, minorTicks, 
       majorTicks, duration, value,greenZones,greenColor,yellowZones,yellowColor,redZones,redColor} = this.setConfig(config);
  • 3.2添加svgspa

    let self = this;                   //添加上下文
     let panel = d3.select("#panel").append("svg:svg")
           .attr("class", "gauge")      //添加類名gauge
           .attr("width", size)         //設置svg寬度   
           .attr("height", size);       //設置svg高度
  • 3.3畫外圓指針

    panel.append("svg:circle")        //添加circle,圓  
             .attr("cx", cx)            //設置circle的座標
             .attr("cy", cy)
             .attr("r", radius)         //設置circle的半徑
             .style("fill", "#ccc")     //設置circle的填充顏色
             .style("stroke", "#000")   //設置circle的輪廓顏色
             .style("stroke-width", "0.5px");//設置circle的輪廓寬度
  • 3.4畫內圓code

    panel.append("svg:circle")  
             .attr("cx", cx)
             .attr("cy", cy)
             .attr("r", 0.9 * radius)    //半徑不同
             .style("fill", "#fff")
             .style("stroke", "#e0e0e0")
             .style("stroke-width", "2px");
  • 3.5畫顏色區域

    黃色和綠色同下
    panel.append("svg:path")             //添加路徑        
             .style("fill", greenColor)
             .attr("d", d3.svg.arc()  //添加圓弧
               .startAngle(this.valueToRadians(greenZones.from, config))  
                                      //開始弧度
               .endAngle(this.valueToRadians(greenZones.to, config))  
                                     //結束弧度            
               .innerRadius(0.65 * radius)  //內圈半徑
               .outerRadius(0.85 * radius))  //外圈半徑
             .attr("transform", function () {//移動+旋轉
               return "translate(" + cx + ", " + cy + ") rotate(270)"
             });
  • 3.6設置label

    let fontSize = Math.round(size / 9); //字體大小
        panel.append("svg:text")         //添加文本
             .attr("x", cx)              //文本位置
             .attr("y", cy / 2 + fontSize / 2)
             .attr("dy", fontSize / 2)
             .attr("text-anchor", "middle")//文字角度
             .text(label)                 //文字內容
             .style("font-size", fontSize + "px")//字體大小
             .style("fill", "#333")        //顏色
             .style("stroke-width", "0px");
  • 3.7設置大小刻度線

    let deltaSize = Math.round(size / 16); //
     let majorDelta = range / (majorTicks - 1);//大刻度之間的距離
         {
           for (let major = min; major <= max; major += majorDelta) {
                                               //循環每一個大刻度
             let minorDelta = majorDelta / minorTicks;//小刻度之間的距離
             for (let minor = major + minorDelta; minor < Math.min(major + majorDelta, max); minor += minorDelta) {      //循環每一個小刻度
               let point1 = this.valueToPoint(minor, config, 0.75);
               let point2 = this.valueToPoint(minor, config, 0.85);
                                     //獲取小刻度線的(x1,y1)(x2,y2)位置
               //添加小刻度線
               panel.append("svg:line")
                 .attr("x1", point1.x) //小刻度線的兩點座標
                 .attr("y1", point1.y)
                 .attr("x2", point2.x)
                 .attr("y2", point2.y)
                 .style("stroke", "#666")//小刻度線顏色
                 .style("stroke-width", "1px");//小刻度的寬度
             }
             //獲取大刻度線的(x1,y1)(x2,y2)位置
             let point3 = this.valueToPoint(major, config, 0.7);
             let point4 = this.valueToPoint(major, config, 0.85);
             //添加大刻度線
             panel.append("svg:line")
               .attr("x1", point3.x)
               .attr("y1", point3.y)
               .attr("x2", point4.x)
               .attr("y2", point4.y)
               .style("stroke", "#333")
               .style("stroke-width", "2px");
             //標記最大值和最小值
             if (major == min || major == max) {
               let point = this.valueToPoint(major, config, 0.63);
               panel.append("svg:text")
                 .attr("x", point.x)
                 .attr("y", point.y)
                 .attr("dy", fontSize / 3)
                 .attr("text-anchor", major == min ? "state" : "end")
                 .text(major)
                 .style("font-size", deltaSize + "px")
                 .style("fill", "#333")
                 .style("stroke-width", "0px")
             }
           }
         }
  • 3.8設置指針

  1. pointerContainer = panel.append("svg:g").attr("class", "pointerContainer");

    //中間值
      let midValue = (min + max) / 2;
      //指針的點軌跡
      let pointerPath = this.buildPointerPath(midValue, config);
      //曲線生成器
      let pointerLine = d3.svg.line()
        .x(function (d) {
          return d.x
        })
        .y(function (d) {
          return d.y
        })
        .interpolate("basis"); //貝斯曲線
      //畫指針
      pointerContainer.selectAll("path")
        .data([pointerPath])
        .enter()
        .append("svg:path")
        .attr("d", pointerLine)
        .style("fill", "#dc3912")  //填充的顏色
        .style("stroke", "#c63310") //輪廓的顏色
        .style("fill-opacity", 0.7);  //填充的透明度
  • 3.9畫指針中心圓

    pointerContainer.append("svg:circle")
              .attr("cx", cx)
              .attr("cy", cy)
              .attr("r", 0.12 * radius)
              .style("fill", "#4684EE")
              .style("stroke", "#666")
              .style("opacity", 1);
  • 3.10設置value

    let valueSize = Math.round(size / 10);
    pointerContainer.append("svg:text")
            .attr("x", cx)
            .attr("y", size - (cy / 4) - valueSize)
            .text(midValue)
            .attr("dy", valueSize / 2)
            .attr("text-anchor", "middle")
            .style("font-size", valueSize + "px")
            .style("fill", "#000")
            .style("stroke-width", "0px");
          this.redraw(value,duration,min,max,range,config,cy,cx)//指針渲染函數

4.設置指針渲染函數

  • 渲染指針

    redraw(value,duration,min,max,range,config,cy,cx){
    let self =this;
    let panel = d3.select("#panel").select(".gauge");
    //指針旋轉
    let pointContainer = panel.select(".pointerContainer");
    //設置value值
    pointContainer.selectAll('text').text(value);
    let pointer = pointContainer.selectAll("path");
    //指針移動
    pointer.transition()
     .duration(duration)//持續時間
     .attrTween("transform", function () {
       let pointerValue = value;
       //判斷超出量程的問題
       if (value > max) pointerValue = max + 0.02 * range;
       else if (value < min) pointerValue = min - 0.02 * range;
       //目標旋轉角度:將數值轉化爲角度,並減去一個直角,
       let targetRotation = (self.valueToDegrees(pointerValue, config) - 90);
       //如今的角度
       let currentRotation = self._currentRotation || targetRotation;
       self._currentRotation = targetRotation;
       return function (step) {
         let rotation = currentRotation + (targetRotation - currentRotation) * step;
         return " rotate(" + rotation + ", " + cx + ", " + cy + ")";   //定義旋轉
       }
     })
         }

5.公共函數

  • 轉化爲弧度

    valueToRadians(value, config) {
     return this.valueToDegrees(value, config) * Math.PI / 180
         }
  • 轉化爲角度

    valueToDegrees(value, config) {
    let {range, min} = this.setConfig(config);
    return value / range * 270 - (min / range * 270 + 45)
       }
  • 生成指針點數據

    valueToPoint(value, config, factor){
    let {cx, cy, radius} = this.setConfig(config);
    return {
     x: cx - radius * factor * Math.cos(this.valueToRadians(value, config)),
     y: cy - radius * factor * Math.sin(this.valueToRadians(value, config))
    }
         }
  • 生成指針

    buildPointerPath(value, config){
    let {range} = this.setConfig(config);
    let delta = range / 13,
     head = this.valueToPoint(value, config, 0.85),
     head1 = this.valueToPoint(value - delta, config, 0.12),
     head2 = this.valueToPoint(value + delta, config, 0.12),
     tailValue = value - (range * (1 / (270 / 360)) / 2),
     tail = this.valueToPoint(tailValue, config, 0.28),
     tail1 = this.valueToPoint(tailValue - delta, config, 0.12),
     tail2 = this.valueToPoint(tailValue + delta, config, 0.12);
    return [head, head1, tail2, tail, tail1, head2, head];
         }
相關文章
相關標籤/搜索