從柱狀堆疊圖帶你入門Echarts

語言: CN / TW / HK

前言

實習一個多月了,在部門裡負責了度量模組的頁面編寫,主要涉及的技術就是Echatrs。在此之前,我只稍微接觸過python的資料視覺化,並沒有接觸過Echarts。不過現在將度量模組的幾個頁面寫下來,已經可以說是入門了,本文從圖的展示需求著手,以柱狀堆疊圖的各種需求圖為例,一步步帶你們瞭解Echarts

Echarts入門

1 安裝

  • ①輸入npm install echarts安裝
  • ②然後在main.js中全域性引入import echarts from "echarts"Vue.prototype.$echarts = echarts,加入以上兩行程式碼

2 如何在vue中使用echarts

2.1 給定一塊區域

<template></template>中給定一塊區域

<div id="myChart1" :style="{width: '100%', height: '700px'}"></div>
複製程式碼

2.2 在methods中寫入一個方法

drawImg1() {
    // 基於準備好的dom,初始化echarts例項
    let myChart1 = this.$echarts.init(
        document.getElementById("myChart1")
    );
    // 繪製圖表
    myChart1.setOption({

    });
},
複製程式碼

setOption中寫入官方的例子,官方的樣例連結官方柱狀堆疊圖 直接複製過來,然後程式碼如下

  drawImg1() {
        // 基於準備好的dom,初始化echarts例項
        let myChart1 = this.$echarts.init(
            document.getElementById("myChart1")
        );
        // 繪製圖表
        myChart1.setOption({
            tooltip: {
            trigger: 'axis',
              axisPointer: {            // 座標軸指示器,座標軸觸發有效
                  type: 'shadow'        // 預設為直線,可選為:'line' | 'shadow'
              }
            },
            legend: {
                data: ['直接訪問', '郵件營銷', '聯盟廣告', '影片廣告', '搜尋引擎']
            },
            grid: {
                left: '3%',
                right: '4%',
                bottom: '3%',
                containLabel: true
            },
            xAxis: {
                type: 'value'
            },
            yAxis: {
                type: 'category',
                data: ['週一', '週二', '週三', '週四', '週五', '週六', '週日']
            },
            series: [
                {
                    name: '直接訪問',
                    type: 'bar',
                    stack: '總量',
                    label: {
                        show: true,
                        position: 'insideRight'
                    },
                    data: [320, 302, 301, 334, 390, 330, 320]
                },
                {
                    name: '郵件營銷',
                    type: 'bar',
                    stack: '總量',
                    label: {
                        show: true,
                        position: 'insideRight'
                    },
                    data: [120, 132, 101, 134, 90, 230, 210]
                },
                {
                    name: '聯盟廣告',
                    type: 'bar',
                    stack: '總量',
                    label: {
                        show: true,
                        position: 'insideRight'
                    },
                    data: [220, 182, 191, 234, 290, 330, 310]
                },
                {
                    name: '影片廣告',
                    type: 'bar',
                    stack: '總量',
                    label: {
                        show: true,
                        position: 'insideRight'
                    },
                    data: [150, 212, 201, 154, 190, 330, 410]
                },
                {
                    name: '搜尋引擎',
                    type: 'bar',
                    stack: '總量',
                    label: {
                        show: true,
                        position: 'insideRight'
                    },
                    data: [820, 832, 901, 934, 1290, 1330, 1320]
                }
            ]
        });
   },
複製程式碼

上述程式碼,建議初學者還是在官方例項網站上多自己改改,觀察圖表變化。這樣有助於理解,官方柱狀堆疊圖

2.3 在mounted()掛載上面所寫方法

mounted(){
    this.drawImg1()
}
複製程式碼

這樣,我們的一個官方樣圖也就在頁面渲染好了。

3 簡單需求

需求的效果圖如下

3.1 分析需求

對比需求效果圖跟官方樣圖的差異

  • ①首先看到上面按鈕,資料肯定得是根據使用者選擇來動態從後端獲取再渲染繪製出圖的
  • ②x軸y軸方向與官方樣圖反過來
  • ③主管增加需求:增加如圖,能切換資料檢視的按鈕跟下載圖片按鈕
  • ④多了兩根折線,並且折線要顯示的是資料百分比,而不是直接顯示資料
  • ⑤柱狀圖的顏色
  • ⑥需求效果圖中左右兩邊都有y軸,也就是有兩根y軸,並且柱狀對應的是左邊的y軸資料,折線對應的是右邊的y軸資料
  • ⑦柱狀圖以及折線圖上資料的展示顏色
  • ⑧主管增加需求:在橫軸資料量過大的時候,可以放大縮小橫軸,這樣能清晰展示某一個區域的資料

3.2 完成需求

這裡可以看一下完成後的圖如下 具體完成過程以及踩坑過程,請看下面說明及程式碼中的註釋。

  • ①【踩坑點】獲取動態資料時,切換不同選擇按鈕,然後重新從後端獲取資料,重新渲染的時候,會發現有些資料不對,新的圖中有的資料是從老的圖裡帶過來的,這樣也就導致了主管跟我說資料不對。我百度找啊找,才發現原來echarts有快取功能,有的資料如果沒有重新賦值是會保留下來帶到下一個新的圖然後繼續渲染的。於是才發現要在setOption前加上myChart1.clear();這一行清除已繪製圖表的程式碼
  • ②【完成需求】按鈕控制邏輯。我們可以用Vuewatch監聽按鈕值的變化,變化之後就執行重新從後端獲取資料的方法再執行drawImg1()方法重新渲染
  • ③【完成需求】x軸y軸方向與官方樣圖反過來。這個很容易解決,我們直接將官方樣例的xAxis改成yAxisyAxis改成xAxis這樣就好了,具體邏輯可以自己嘗試修改程式碼進行理解
  • ④【完成主管增加需求】增加能切換資料檢視的按鈕跟下載圖片按鈕。這個echarts已經自帶有這個封裝功能,我們將程式碼增加寫入即可,在下面程式碼中的toolbox
  • ⑤【完成需求】多了兩根折線,並且折線要顯示的是資料百分比,而不是直接顯示資料。這個也簡單,我們只需要在series中增加兩個控制折線的物件,在下面程式碼的series末尾中可以看到詳細說明,至於展示資料顯示為百分比,這個要用formatter進行控制
  • ⑥【完成需求】柱狀圖的顏色。這個是可以在每一個series物件中單獨通過color來設定控制,如果你不用的話,預設採用官方的柱狀堆疊圖例項的隨機色
  • ⑦【完成需求】需求圖中左右兩邊都有y軸,也就是有兩根y軸,並且柱狀對應的是左邊的y軸資料,折線對應的是右邊的y軸資料。我們yAxis中寫入兩個物件,一個控制左邊的y軸,一個控制右邊的y軸,只需更改positionleft或者right即可,這樣就能有兩根y軸了。這樣之後我們還需要控制柱狀對應的是左邊的y軸,折線對應的是右邊的y軸,因為yAxis裡面放的是陣列,預設對應第一個y軸,所以我們在series折線物件中加上yAxisIndex: 1這樣就能對應上右邊的百分比y軸,具體請看下面程式碼中末尾兩個series物件的程式碼
  • ⑧【完成需求】柱狀圖以及折線圖上資料的展示顏色。這個在serieslabeltextStylecolor中寫入即可
  • ⑨【完成主管增加需求】在橫軸資料量過大的時候,可以放大縮小橫軸,清晰展示某一個區域的資料。加上如下程式碼中的dataZoom即可,具體控制邏輯在註釋中說明
 drawImg1() {
      // 基於準備好的dom,初始化echarts例項
      let myChart1 = this.$echarts.init(
          document.getElementById("myChart1")
      );
      myChart1.clear();  // ①此處為踩坑點,最好最好要加上這一行程式碼,以免動態切換圖示帶來的資料渲染錯誤
      // 繪製圖表
      myChart1.setOption({
          title: { text: "定製版本" },  // 圖表左上方的標題
          tooltip: {
              trigger: "axis",
              axisPointer: {
                  // 座標軸指示器,座標軸觸發有效
                  type: "shadow", // 預設為直線,可選為:'line' | 'shadow',自己切換再把滑鼠放到資料檢視上,能觀察到指示器有所改變,一般還是建議採用shadow比較美觀
              },
          },
          toolbox: {    // 此處則是控制右上角的按鈕,能夠切換資料展示檢視,挺好用的,建議加上
              show: true,
              feature: {
                  // 控制是否出現數據檢視,展示資料
                  dataView: {
                      show: true,
                      readOnly: false,
                  },
                  // 儲存圖片按鈕,官方文件中還有其他按鈕,可自行檢視
                  saveAsImage: { show: true },
              },
          },
          legend: {
              // 此處控制上方圖例,因為改圖的圖例定的比較死,所以我沒用動態資料,直接在此寫入
              data: [
                  "版本未申請釋出量",
                  "釋出成功數",
                  "釋出失敗數",
                  "釋出成功率",
                  "申請率",
              ],
          },
          grid: {
              left: "3%",
              right: "4%",
              bottom: "10%", // 控制底部拖動條的位置,如果不改變這裡,用預設的bottom值,拖動條會影響到資料檢視
              containLabel: true,
          },
          yAxis: [
              {
                  type: "value",
                  position: "left",  // 左邊,第一個y軸,控制展示位置
              },
              {
                  type: "value",
                  position: "right", // 右邊,第二個y軸
                  axisLabel: {
                      formatter: "{value}%", // 控制y軸間隔展示資料的格式
                  },
                  splitLine: {
                      show: false, // 控制每一個間隔都是否有一條分割線
                  },
              },
          ],
          xAxis: {
              type: "category",
              data: this.Img1xAxisData, // 動態使用data中的資料,先用獲取資料方法獲取資料存入到data中,再呼叫drawImg1()渲染
          },
          //  dataZoom為控制是否可以進行資料放大放小
          dataZoom: [
              {
                  type: "inside",  // 加上這一個inside,即可以將滑鼠放在資料檢視上,然後用滑鼠的滾輪滾動上下進行x軸的放大與縮小,具體區別可以自己刪了跟增加這一行程式碼對比。如果沒有這一行,那麼就只能通過滾動下方拖動條來控制x軸上下
              },
              {
                  start: 0,
                  end: 10,
                  handleIcon:
                      "M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z",
                  handleSize: "80%",
                  handleStyle: {
                      color: "#fff",
                      shadowBlur: 3,
                      shadowColor: "rgba(0, 0, 0, 0.6)",
                      shadowOffsetX: 2,
                      shadowOffsetY: 2,
                  },
              }, // 這個控制拖動條的樣式,一般這個不需更改
          ],
          series: [
              {
                  name: "版本未申請釋出量",  // name會對應圖例進行控制,不能夠亂填,後期也應該做成動態的
                  type: "bar", // 柱狀圖的柱子,line則為折線
                  stack: "總量",
                  color: ["#ff9933"], // 控制柱子顏色
                  label: {
                      show: true,  // 控制展示資料在柱子的展示與否
                      position: "inside", // 控制戰術資料在柱子的哪個位置,可以是inside、top、insideRight
                      textStyle: {  // 控制柱子中資料的格式
                          // fontWeight: "bolder", // 加粗
                          fontSize: "12", // 字型大小
                          color: "#141414", // 字型顏色
                      },
                  },
                  data: this.Img1unpublishversion,
              },
              {
                  name: "釋出成功數",
                  type: "bar",
                  stack: "總量",
                  color: ["#ffff99"],
                  label: {
                      show: true,
                      position: "inside",
                      textStyle: {
                          // fontWeight: "bolder",
                          fontSize: "12",
                          color: "#141414",
                      },
                  },
                  data: this.Img1successversion,
              },
              {
                  name: "釋出失敗數",
                  type: "bar",
                  stack: "總量",
                  color: ["#3333ff"],
                  label: {
                      show: true,
                      position: "inside",
                      textStyle: {
                          // fontWeight: "bolder",
                          fontSize: "12",
                          color: "#141414",
                      },
                  },
                  data: this.Img1failversion,
              },
              {
                  name: "釋出成功率",
                  type: "line", // 折線
                  stack: "", // 此處要為空,不能對應總量
                  symbolSize: 10, // 拐點大小
                  yAxisIndex: 1, // 對應第二根y軸
                  lineStyle: {
                      // color: "#66cc33", //改變折線顏色
                      width: 2// 改變折線粗細
                  },
                  itemStyle: {
                      normal: {
                          label: {
                              show: true, // 折線圖展示數字與否
                              formatter: "{c}%", // 自定義資料值加百分比,控制展示資料格式
                          },
                      },
                  },
                  data: this.Img1successpercent,
              },
              {
                  name: "申請率",
                  type: "line",
                  stack: "",
                  symbolSize: 10, //拐點大小
                  yAxisIndex: 1,
                  lineStyle: {
                      color: "#3366cc",
                      width: 2,
                      //改變折線顏色
                  },
                  itemStyle: {
                      normal: {
                          label: {
                              show: true,
                              formatter: "{c}%", // 自定義資料值加百分比
                          },
                      },
                  },
                  data: this.Img1failpercent,
              },
          ],
      });
  },
複製程式碼

看完上面的程式碼及註釋說明,你應該能夠掌握基礎的用法了

4 需求進階

其實真正難的還在後面,因為上面資料還很少,我們的物件是手動寫入的,但如果我們的物件特別多,難道你要在series中一個個將物件新增進去嗎?而且每一次獲取新的資料,series的物件也是不一樣的,所以很顯然我們需要一個動態的series。我這裡就不放新的需求圖是怎樣的了,直接放完成的圖。就像下面這個圖,每一條柱子上是特別多的資料的,每多一個圖例資料,我們就要多一個series,所以我們需要動態寫入series

4.1 分析需求

對比這個新的圖跟上面的第一個完成圖又有哪些不一樣的地方

  • ①圖例、每一根柱子上的物件特別多
  • ②左邊y軸的文字是藍色的,並且離資料檢視有一定小小的距離
  • ③圖例在資料檢視的下方
  • ④每一根柱子的最右邊有一個總計數值
  • dataZoom在右邊

4.2 完成需求

  • ①【完成需求、踩坑點】不能一個個寫入,因為是動態的,所以要寫一個方法動態將series加入。這裡我一開始想了好久也沒想到怎麼處理,後來才想到我直接根據後端獲取的資料存到data中,然後我在computed寫一個方法,根據data中獲取的資料,動態返回一個series物件陣列,然後再渲染到資料檢視中,具體在下面程式碼的computed中檢視
  • ②【完成需求】左邊y軸的文字是藍色的,並且離資料檢視有一定小小的距離。在yAxis中加入axisLabel,然後用textStyle來控制文字樣式,marigin來控制文字離資料檢視的距離
  • ③【完成需求】圖例在資料檢視的下方。在legend中加入y: "bottom"
  • ④【完成需求】每一根柱子的最右邊有一個總計數值,新增一個計算總量的series物件,具體在下面程式碼的computed中檢視。具體不同的實現方法參考echarts 堆疊柱狀圖頂部顯示總和
  • ⑤【完成需求】dataZoom在右邊。因為在這圖裡,x軸放到了左邊,也就是變成了y軸,所以我們在dataZoom中加入兩行yAxisIndex: 0,這樣滾動條即可對應到左邊的y軸,具體請看下列程式碼及註釋
   drawImg2() {
      // 基於準備好的dom,初始化echarts例項
      let myChart2 = this.$echarts.init(
          document.getElementById("myChart2")
      );
      myChart2.clear();
      // 繪製圖表
      myChart2.setOption({
          tooltip: {
              trigger: "axis",
              axisPointer: {
                  // 座標軸指示器,座標軸觸發有效
                  type: "shadow", // 預設為直線,可選為:'line' | 'shadow'
              },
          },
          toolbox: {
              show: true,
              feature: {
                  //控制是否出現數據檢視
                  dataView: {
                      show: true,
                      readOnly: false,
                  },
                  saveAsImage: { show: true },
              },
          },
          legend: {
              data: this.Img2Legend, // 動態獲取從後端獲得的圖例資料
              y: "bottom", // 控制圖例在下方位置
          },
          grid: {
              left: "3%",
              right: "4%",
              bottom: "10%", //    控制底部拖動條的位置
              containLabel: true,
          },
          xAxis: [
              {
                  type: "value",
                  position: "left",
              },
          ],
          yAxis: {
              type: "category",
              data: this.Img2xAxiasData,
              axisLabel: {
                  // interval: 0,
                  // rotate: 30, 這裡這兩個可以控制文字傾斜度
                  textStyle: {
                      color: "#3399ff",
                  }            // 座標軸刻度的樣式
                  margin: 20, // 控制文字離資料檢視的距離
              },
          },
          dataZoom: [
              {
                  type: "inside",
                  yAxisIndex: 0, // 這裡圖裡在右邊,要對應的是左邊的y軸豎軸
              },
              {
                  start: 0,
                  end: 10,
                  handleIcon:
                      "M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z",
                  handleSize: "80%",
                  handleStyle: {
                      color: "#fff",
                      shadowBlur: 3,
                      shadowColor: "rgba(0, 0, 0, 0.6)",
                      shadowOffsetX: 2,
                      shadowOffsetY: 2,
                  },
                  yAxisIndex: 0, // 這裡圖裡在右邊,要對應的是左邊的y軸豎軸
              },
          ],
          series: this.Img2series, // 動態新增series物件,具體請看下面的程式碼
      });
  },
複製程式碼

上面是該資料檢視的方法,具體的series動態資料程式碼在computed中,請檢視下列程式碼

computed:{
    Img2series: function () {
        let Arr = [];
        let Img2sumData = [];
        let sum = 0;
        if (this.Img2yAxiasEndData.length) {
            // 在y軸有資料的情況下,如果還有可能報錯,要在再多加一層if(this.Img2yAxiasEndData)判斷,然後再進行統計每一根柱子加起來的總值
            for (let j = 0; j < this.Img2yAxiasEndData[0].length; j++) {
                sum = 0;
                for (let i = 0; i < this.Img2yAxiasEndData.length; i++) {
                    sum = sum + this.Img2yAxiasEndData[i][j];
                }
                Img2sumData.push(sum);
            }
        }
        //  新增總計值
        Arr.push({
            name: "總計",
            type: "bar",
            barGap: "-100%", // 這裡是關鍵,因為這個總計其實是多加一個透明的柱狀圖物件疊加在原來的圖上
            label: {
                normal: {
                    show: true,
                    position: "right",
                    formatter: "總計{c}",
                    textStyle: { color: "#000" },
                },
            },
            itemStyle: {
                normal: {
                    color: "rgba(128, 128, 128, 0)", // 透明的
                },
            },
            data: Img2sumData,
        });
        if (this.Img2Sixvalue === "按審計碼") { // 按鈕控制邏輯
            //  遍歷新增series
            for (let i = 0; i < this.Img2Legend.length; i++) {
                Arr.push({
                    name: this.Img2Legend[i], // 對應第幾個圖例
                    type: "bar",
                    stack: "總量",
                    label: {
                        show: true,
                        position: "inside",
                        textStyle: {
                            fontSize: "12",
                            color: "#141414",
                        },
                    },
                    data: this.Img2yAxiasEndData[i], // 對應第幾組資料
                });
            }
        } else {
            this.Img2Legend.push("總量");
            Arr.push({
                name: "總量",
                type: "bar",
                stack: "總量",
                color: ["#3399ff"],
                label: {
                    show: true,
                    position: "inside",
                    textStyle: {
                        fontSize: "12",
                        color: "#141414",
                    },
                },
                data: Img2sumData,
            });
        }
        return Arr; // 將這個陣列返回出去,那麼drawImg2()中的series就能渲染上去了
    },
}
複製程式碼

總結

    用了Echarts之後,真心感覺Echarts是一款很強大的視覺化圖示庫,就像年初的疫情的疫情地圖,也是通過Echarts進行展示的,很直觀的一種展示方式,我自己也做過一個,如圖(該圖資料為虛擬資料,僅供參考)     本篇文章,只能夠將你領入門而已,想要真正玩轉Echarts,還是要自己看官方文件,根據例項不斷嘗試修改

寫在文末

    如果你覺得我寫得還不錯的話,可以給我點個贊哦^^,如果哪裡寫錯了、寫得不好的地方,也請大家評論指出,以供我糾正。(這個系列也快完結了,大家可以複習複習前面的,接下來我會更新一些別的文章,也請大家可以繼續關注哦)

其它文章