支持個性化配置的柱狀圖——canvas(再也不用和設(shè)計說你這個設(shè)計效果我做不到啦)

因為公司的業(yè)務(wù)需要,highcharts&echarts這些圖表庫的樣式不符合設(shè)計的要求锉罐,所以在考慮了不用考慮ie8等瀏覽器的情況下驻粟,選擇canvas封裝了一個支持個性化配置的柱狀圖瘾晃。希望有需要類似圖的前端伙伴较性,可以有用撵割。
因為個性化弄诲,需要各種自定義大小顏色的參數(shù)菱鸥,所以我把參數(shù)分為了4類——基礎(chǔ)參數(shù)、坐標(biāo)軸抚垄、柱子數(shù)據(jù)蜕窿、增強(qiáng)屬性。
  • 先看個效果圖


    柱狀圖.png
  • 接入方式

    • html中放置一個canvas
    <canvas id="canvas"></canvas>
    
    • 引入下面的function drawHistogram

    • 執(zhí)行方法

    drawHistogram(chartsConfigObj);
    
  • 參數(shù)解釋

    • 第一層參數(shù)解釋
    /**
     * 第一層參數(shù)
     * @param {basicData} 必傳呆馁,基礎(chǔ)參數(shù)
     * @param {axisData} 必傳桐经,坐標(biāo)軸參數(shù)
     * @param {columnData} 必傳,柱子數(shù)據(jù)
     * @param {enhancementAttr} 非必傳浙滤,一些增強(qiáng)屬性
     */
    
    • 第二層參數(shù)
    let {
        basicData: {
          wrapEleId,
          width,
          height
        },
        axisData: {
          basic: {
            axislineWidth,
            axisColor,
            axisTextColor,
            axisTextFont,
          },
          axisX: {
            axisXStart,
            axisXWidth,
            axisXTextDistance
          },
          axisY: {
            axisYStart,
            axisYWidth,
            axisYIntervalNum,
            axisYTextDistance,
            axisYMaxNum
          }
        },
        columnData: {
          eachColumnWidth,
          spaceDistance,
          columnMaxHeight,
          columnColor,
          firstColumnStartX,
          firstColumnStartY
        },
        graphs
      } = chartsConfigObj;
      const {enhancementAttr} = chartsConfigObj;
      let {
        isColumnNeedRadius,
        columnGradientData,
        isRemoveAxisArrow,
        horizontalLineObj
      } = enhancementAttr || {}; 
      let {
        gradientStartColor,
        gradientEndColor
      } = columnGradientData || {};
      let {
        horizontalLineColor,
        horizontalLineDistance
      } = horizontalLineObj || {};
    /**
    * 具體的每個柱狀圖參數(shù)
    * @param {wrapEleId} canvas畫布id
    * @param {width} 畫布寬
    * @param {height} 畫布高
    * @param {eachColumnWidth} 一個柱子的寬度
    * @param {spaceDistance} 柱子間的間距
    * @param {firstColumnStartX} 第一個柱子左下角的X軸坐標(biāo)
    * @param {firstColumnStartY} 第一個柱子左下角的Y軸坐標(biāo)
    * @param {columnMaxHeight} 柱子最大高度
    * @param {axislineWidth} 坐標(biāo)軸的線的寬度
    * @param {axisXStart} 坐標(biāo)軸X軸起點坐標(biāo)
    * @param {axisYStart} 坐標(biāo)軸Y軸起點坐標(biāo)
    * @param {axisXWidth} 坐標(biāo)軸X軸長度
    * @param {axisYWidth} 坐標(biāo)軸Y軸長度
    * @param {axisXTextDistance} 坐標(biāo)軸X軸文字距離X軸垂直方向的偏移量 
    * @param {axisYTextDistance} 坐標(biāo)軸Y軸水平方向的偏移量
    * @param {axisYIntervalNum} 左邊軸y軸間隔
    * @param {axisYMaxNum} 左邊軸y軸標(biāo)尺最大值
    * @param {axisColor} 坐標(biāo)軸線顏色
    * @param {axisTextColor} 坐標(biāo)軸文本顏色
    * @param {axisTextFont} 坐標(biāo)軸文本fontSize阴挣、fontFamily
    * @param {columnColor} 柱狀圖顏色
    * @param {graphs} 圖表數(shù)據(jù)-數(shù)組,[{x:纺腊,y:},...]
    * @param {columnGradientData} 柱子漸變色信息畔咧,不需要漸變就不傳此參數(shù)
    * @param {gradientStartColor} 漸變開始色(靠近x軸)
    * @param {gradientEndColor} 漸變結(jié)束色
    * @param {isColumnNeedRadius} 柱子是否需要圓角
    * @param {gradientEndColor} 漸變結(jié)束色
    * @param {gradientEndColor} 漸變結(jié)束色
    * @param {gradientEndColor} 漸變結(jié)束色
    * @param {horizontalLineObj} 水平虛線背景參數(shù),不需要則不傳{color,space}
    */
    
  • 功能實現(xiàn)代碼

export default function drawHistogram(chartsConfigObj)  {

    let {
      basicData: {
        wrapEleId,
        width,
        height
      },
      axisData: {
        basic: {
          axislineWidth,
          axisColor,
          axisTextColor,
          axisTextFont,
        },
        axisX: {
          axisXStart,
          axisXWidth,
          axisXTextDistance
        },
        axisY: {
          axisYStart,
          axisYWidth,
          axisYIntervalNum,
          axisYTextDistance,
          axisYMaxNum
        }
      },
      columnData: {
        eachColumnWidth,
        spaceDistance,
        columnMaxHeight,
        columnColor,
        firstColumnStartX,
        firstColumnStartY
      },
      graphs
    } = chartsConfigObj;
  
    const {enhancementAttr} = chartsConfigObj;
    
    let {
      isColumnNeedRadius,
      columnGradientData,
      isRemoveAxisArrow,
      horizontalLineObj
    } = enhancementAttr || {};
    
    let {
      gradientStartColor,
      gradientEndColor
    } = columnGradientData || {};
  
    let {
      horizontalLineColor,
      horizontalLineDistance
    } = horizontalLineObj || {};
  
    horizontalLineColor = horizontalLineColor ? horizontalLineColor : '#eee';
    horizontalLineDistance = horizontalLineDistance ? horizontalLineDistance: 5;

    isRemoveAxisArrow = isRemoveAxisArrow ? isRemoveAxisArrow : false;
  
    var canvas = document.getElementById(wrapEleId);
    var context = canvas.getContext("2d");
    canvas.width = width;
    canvas.height = height;
    context.beginPath();
  
    //畫x軸
    context.beginPath();
    context.moveTo(axisXStart, axisYStart);
    context.lineTo(axisXStart + axisXWidth, axisYStart);
    context.strokeStyle = axisColor;
    context.lineWidth = axislineWidth;
    context.stroke();
    context.beginPath();

    if (!isRemoveAxisArrow) {
      context.moveTo(axisXStart + axisXWidth - 6, axisYStart-6);
      context.lineTo(axisXStart + axisXWidth, axisYStart);
      context.lineTo(axisXStart + axisXWidth - 6, axisYStart+6);
      context.strokeStyle = axisColor;
      context.lineWidth = axislineWidth;
      context.stroke();
    }
  
    //畫y軸
    context.beginPath();
    var axisYEnd = height - (height - axisYStart + axisYWidth);
    context.moveTo(axisXStart, axisYStart);
    context.lineTo(axisXStart, axisYEnd);
    context.strokeStyle = axisColor;
    context.lineWidth = axislineWidth;
    context.stroke();
    context.beginPath();
    if (!isRemoveAxisArrow) {
      context.moveTo(axisXStart - 6, axisYEnd + 6);
      context.lineTo(axisXStart, axisYEnd);
      context.lineTo(axisXStart + 6, axisYEnd + 6);
      context.strokeStyle = axisColor;
      context.lineWidth = axislineWidth;
      context.stroke();
    }
  
    //畫y軸上的數(shù)字
    var axisYNum = axisYMaxNum / axisYIntervalNum;
    var axisYLength = columnMaxHeight / axisYNum;
    for (var j = 0; j <= axisYNum; j++) {
      context.beginPath();
      context.moveTo(axisXStart, axisYStart);
      context.font = axisTextFont;
      context.textAlign = "center";
      context.fillStyle = axisTextColor;
      context.fillText(j*axisYIntervalNum,axisXStart - 15, axisYStart - j * axisYLength + axisYTextDistance);
      
      context.beginPath();
      context.moveTo(axisXStart, axisYStart - j * axisYLength);
      context.lineTo(axisXStart+5, axisYStart - j * axisYLength);
      context.strokeStyle = axisColor;
      context.lineWidth = axislineWidth;
      context.stroke();
  
      if (horizontalLineObj && j !== 0) {
        context.beginPath();
        context.moveTo(axisXStart, axisYStart - j * axisYLength);
        context.lineTo(axisXStart+axisXWidth, axisYStart - j * axisYLength);
        context.setLineDash([horizontalLineObj.space || 10]);
        context.strokeStyle = horizontalLineObj.color || '#eee';
        context.stroke();
      }
    }
    context.beginPath();
  
    //畫柱狀圖
    var columnLength = graphs.length;
    for (var i = 0; i < columnLength; i++) {
      var colData = graphs[i];
      var colStratX = firstColumnStartX+(i*spaceDistance)+(i*eachColumnWidth);
      var colStratY = height - (height - firstColumnStartY + (colData.y/100) * columnMaxHeight);
  
      if(isColumnNeedRadius) {
        const r = eachColumnWidth / 2;
        const w = eachColumnWidth;
        const h = -((colData.y/100) * columnMaxHeight);
        const x = colStratX;
        const y = firstColumnStartY;
        context.beginPath();
        context.moveTo(x + r, y);
        context.arcTo(x + w, y, x + w, y + h, r);
        context.arcTo(x + w, y + h, x, y + h, r);
        context.arcTo(x, y + h, x, y, r);
        context.arcTo(x, y, x + w, y, r);
        context.closePath();
      } else {
        context.beginPath();
        context.moveTo(colStratX, firstColumnStartY);
        context.lineTo(colStratX, colStratY);
        context.lineTo(colStratX+eachColumnWidth, colStratY);
        context.lineTo(colStratX+eachColumnWidth, firstColumnStartY);
        context.closePath();
      }
      
  
      //需要漸變
      if (columnGradientData) {
        var grad  = context.createLinearGradient(0, firstColumnStartY, 0,colStratY);
        grad.addColorStop(0, gradientStartColor); // 綠
        grad.addColorStop(1, gradientEndColor);  // 紫
        context.fillStyle = grad;
      } else {
        context.fillStyle = columnColor;
      }
  
      context.fill();
      context.font = axisTextFont;
      context.textAlign="center";
      context.fillStyle = axisTextColor;
      context.fillText(colData.x, colStratX+eachColumnWidth/2,  firstColumnStartY + axisXTextDistance);
  
    }
    
  }
  • 例子數(shù)據(jù)
const chartsConfigObj = {
    basicData: {
      wrapEleId: 'canvas',
      width: 860,
      height: 400
    },
    axisData: {
      basic: {
        axislineWidth: 1,
        axisColor: '#999',
        axisTextColor: '#666',
        axisTextFont: '12px PingFangSC-Regular',
      },
      axisX: {
        axisXStart: 26,
        axisXWidth: 850,
      },
      axisY: {
        axisYStart: 365,
        axisYWidth: 190,
        axisYIntervalNum: 20,
        axisYTextDistance: 2,
        axisYMaxNum: 100
      }
    },
    columnData: {
      eachColumnWidth: 14,
      spaceDistance: 30,
      columnMaxHeight: 156,
      columnColor: '#f00',
      firstColumnStartX: 50,
      firstColumnStartY: 365
    },
    enhancementAttr: {
      isColumnNeedRadius: true,
      columnGradientData: {
        gradientStartColor: '#35E1A0',
        gradientEndColor: '#29CDC0'
      },
      horizontalLineObj: {
        horizontalLineColor: '#999',
        horizontalLineDistance: 5
      },
      isRemoveAxisArrow: true
    },
    graphs: [
      {
      'x': '2011',
      'y': '100'
      }, {
      'x': '2012',
      'y': '90'
      }, {
      'x': '2013',
      'y': '60'
      }, {
      'x': '2014',
      'y': '90'
      }, {
      'x': '2015',
      'y': '70'
      }, {
      'x': '2016',
      'y': '10'
      }, {
      'x': '2011',
      'y': '100'
      }, {
      'x': '2012',
      'y': '90'
      }, {
      'x': '2013',
      'y': '60'
      }, {
      'x': '2014',
      'y': '90'
      }, {
      'x': '2015',
      'y': '70'
      }, {
      'x': '2016',
      'y': '10'
      }, {
      'x': '2011',
      'y': '100'
      }, {
      'x': '2012',
      'y': '90'
      }, {
      'x': '2013',
      'y': '60'
      }, {
      'x': '2014',
      'y': '30'
      }, {
      'x': '2015',
      'y': '10'
      }, {
      'x': '2016',
      'y': '95'
      }
    ]
  };
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末揖膜,一起剝皮案震驚了整個濱河市誓沸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌壹粟,老刑警劉巖拜隧,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異趁仙,居然都是意外死亡洪添,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門雀费,熙熙樓的掌柜王于貴愁眉苦臉地迎上來干奢,“玉大人,你說我怎么就攤上這事盏袄÷烧停” “怎么了宋光?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長炭菌。 經(jīng)常有香客問我,道長逛漫,這世上最難降的妖魔是什么黑低? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮酌毡,結(jié)果婚禮上克握,老公的妹妹穿的比我還像新娘。我一直安慰自己枷踏,他們只是感情好菩暗,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著旭蠕,像睡著了一般停团。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上掏熬,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天佑稠,我揣著相機(jī)與錄音,去河邊找鬼旗芬。 笑死舌胶,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的疮丛。 我是一名探鬼主播幔嫂,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼誊薄!你這毒婦竟也來了履恩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤暇屋,失蹤者是張志新(化名)和其女友劉穎似袁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咐刨,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡昙衅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了定鸟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片而涉。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖联予,靈堂內(nèi)的尸體忽然破棺而出啼县,到底是詐尸還是另有隱情材原,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布季眷,位于F島的核電站余蟹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏子刮。R本人自食惡果不足惜威酒,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望挺峡。 院中可真熱鬧葵孤,春花似錦、人聲如沸橱赠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽狭姨。三九已至宰啦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間送挑,已是汗流浹背绑莺。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留惕耕,地道東北人纺裁。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像司澎,于是被迫代替她去往敵國和親欺缘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354