让决策更智能
新一代智能数据分析平台

自定义图表

观远产品部发表于:2021年03月03日 10:18:41更新于:2021年03月08日 11:04:34

自定义图表是一种开放式的可视化类型,用户可以基于观远后台强大的数据处理能力,再借助第三方的可视化能力,构建出自定义的数据可视化展现方式。使用自定义图表,可以让平台的可视化能力不局限与当前已有的图表类型,可以根据自己的实际场景,扩展出丰富多样的可视化类型。

1. 开发要求

创建自定义图表,需要使用到观远提供的可视化SDK扩展包,以及各类可视化图表库的使用,涉及到Javascript的开发,需要用户有一定前端开发能力。

2. 使用流程

创建自定义图表,主要分为以下两步:

  • 数据准备

  • 制作图表

3. 创建一个自定义可视化图表

下面我们以echarts为例,创建一个自定义可视化图表。

步骤1:选择数据集

点击仪表板右上角的新建卡片,选择“自定义图表”,选择一个数据集。

001603eefd0ca5205b2b39caceed7ab

步骤2:准备数据

进图自定义图表编辑页后,看到编辑页主要分为三个区域:

  • 数据视图列表

  • 当前数据视图的编辑区

  • 图表视图

001603eefe84250a4b776137520959c

数据准备过程实际上和平常做可视化分析是类似的,可以在数据视图编辑区内拖拽相应的维度和数值到对应的区域,准备好用来绘制图表的数据。不同之处在于:自定义图表的数据可来自多个数据视图(一个视图类似于一个卡片计算)。多个视图的数据信息将会以数组的形式供给图表视图绘图所用。

001603eefff4de23605605b32b78e12

步骤3:制作图表

数据准备好后,我们切换到图表视图进行图表的可视化编辑。图表视图页分为四个区域,分别是:

  • HTML代码区

  •  CSS代码区

  • Java Script代码区

  • 图表预览区

点击左上角“查看数据视图结构”,可弹出“输入数据预览”区域,方便查看从数据视图传入的数据结构。

001603ef02075571830b4f34d597ad5

如上图所示,用户首次进入图表视图区域时,代码区域内已经有一些默认的代码,系统默认会将数据以表格的形式展现在图表预览区。

代码框架如下:

javascript
function renderChart (data, clickFunc,config) {
    /* ------ Custom Code Start ------ */
   // ...custom code for render chart
    /* ------ Custom Code End ------ */
}
new GDPlugin().init(renderChart)

该部分代码内跟SDK相关的部分说明如下:

1. GDPlugin类,负责数据通讯及调用图表绘制方法

2. init: 接收一个renderChart函数, GDPlugin会在接收到数据后调用renderChart, 提供三个入参(data, clickFunc,config)

3. data为数据数组, 即上图右侧输入数据预览内的数据;clickFunc为交互回调, 处理用户交互事件传递;config是图表属性设置,目前包含 theme, colors 两个属性。

data是由视图数据组成的数组,每个视图数据是由列数据组成的数组。 data的数据结构实例如下:

javascript
[
  [
    {
      "name": "综合等级", // column0
      "data": [
        "上海",
        "云南",
        "内蒙古",
        "北京",
        "台湾"
      ]
    },
    {
      "name": "Cost_Item", // column1
      "data": [
        136613.86,
        90151,
        1172.8,
        7013.28,
        201688.18
      ]
    },
    {
      "name": "Total_Amount", // column2
      "data": [
        123230.66,
        83763.53,
        1013.8,
        5682.38,
        192702.12
      ]
    }
  ]
]

clickFunc接受一个参数,用来支持自定义图表发起的联动、跳转等交互信息。参数结构如下:

javascript
{
  clickedItems: [
      {
        idx: [ dataIndex, columnIndex ],
        colName,
        value [ v1, v2 ]
      }
    ]
}

clickedItems是字段的数组。每个字段有三个属性

• idx 是字段在data中的位置信息,dataIndex是对应数据视图的索引位置,columnIndex是数据视图内对应字段的索引位置,可以用 data[dataIndex][columnIndex] 取到当前字段的列信息。

• colName 是字段名称

• value 是字段取值的数组

 4. highcharts实现带联动能力的蝴蝶图

下面用highcharts来写一个自定义实现一个支持发起联动的蝴蝶图。

javascript
const DEDAULT_COLORS = ['#2f7ed8', '#f28f43', '#1aadce', '#492970', '#f28f43', '#77a1e5', '#c42525', '#a6c96a']
const DEFALUT_THEME = 'LIGHT'
const chart = Highcharts.chart("container", {
    chart: {
        type: "bar"
    },
    title: {
        text: null
    },
    xAxis: [
        {
            categories: [],
            reversed: false,
            labels: {
                step: 1
            }
        }
    ],
    yAxis: {
        title: {
            text: null
        }
    },
    plotOptions: {
        series: {
            stacking: "normal",
        }
    },
    series: []
});
function renderChart(data, clickFunc, config) {
   /* ------ Custom Code Start ------ */
    if (!data || data[0].length < 3) {
        const dom = document.getElementById('container')
        dom.innerHTML = "<div class='hint'>需要3列数据<br/>第1列为维度<br/>第2列为数值<br/>第3列为数值</div>"
        return
    }
    const { theme = DEFALUT_THEME, colors = DEDAULT_COLORS } = config || {}
    const isDarkTheme = theme !== 'LIGHT'
    chart.update(
        {
            colors,
            xAxis: [
                {
                    categories: data[0][0].data,
                    title: data[0][0].name,
                    lineColor: isDarkTheme ? '#B4C2D6' : '#DBDFE7',
                    labels: {
                        style: {
                            color: isDarkTheme ? '#B4C2D6' : '#617282',
                        }
                    }
                }
            ],
            yAxis: {
                gridLineColor: isDarkTheme ? '#B4C2D6' : '#DBDFE7',
                labels: {
                    style: {
                        color: isDarkTheme ? '#B4C2D6' : '#617282',
                    }
                }
            },
            legend: {
                itemStyle: {
                    color: isDarkTheme ? '#B4C2D6' : '#617282',
                },
            },
            plotOptions: {
                series: {
                    stacking: "normal",
                    events: {
                        click: (event) => {
                          /* 从图表的事件到clikcFunc的调用示例 */
                            const { category } = event.point
                            if (category) {
                                const attrCol = data[0][0]
                                clickFunc({
                                    clickedItems: [
                                    {
                                        idx: [ 0, 0 ],
                                        colName: attrCol.name,
                                        value: [ category ],
                                    }
                                ]
                                })
                            }
                        }
                    }
                }
            },
        },
        false
    );
    const length = chart.series.length;
    Array.from({ length }).forEach((_, index) => {
        chart.series[length - index - 1].remove();
    });
    data[0].slice(1).forEach(function(serie, index) {
        if (index === 0) {
            chart.addSeries(
                { ...serie, data: serie.data.map(item => -item) },
                false
            );
        } else {
            chart.addSeries(serie, false);
        }
    });
    chart.redraw();
}
new GDPlugin().init(renderChart);

5. 更多样例代码

下面我们以antv-g2的动态图表为例,简要说明如何结合第三方图表库和现有的数据集生成一个酷炫的图表。

(1) 首先,我们要在页面内引入第三方可视化库。我们需要找到类似cdn或者其他在线资源,在HTML区块中通过`<script src='xx'></script>`引入。具体操作详情参照antv-g2的浏览器引入方式

(2)将该图表JavaScript实现部分的代码移植入“Custome Code Start”与“Custom Code End”区间内,点击“运行”便可将图形原模原样搬入BI平台。

(3)可以看到该图中一共需要三种数据(省份、年份、数值),在数据视图中我们也需要在新增2个维度,1个数值(必要时可以筛选排序)。步骤2中,我们已经准备了一份“日期(季度)+大区+销售金额”的数据。这时候我们要将renderChart传入的数据data转化成antv-g2的动态图表需要的数据结构。

(4)接着再根据自己的需要,对图表配置进行相关调整,一张酷炫的图表就生成了。

JavaScript代码最后一行 `new GDPlugin().init(renderChart)` ,会保证在每次数据变更时,重新执行renderChart。所以建议不要把new chart这类一次性构造,持续使用的声明放在renderChart里面。

001603ef19ee242326225667c2ba1e1

下面,我们给出以上“条形滚动轮播图”用AntV G2、Highcharts、eCharts三种方式分别实现样例代码供大家参考。

 AntV G2实现

a. HTML代码块

html
<div id="container"></div>
<button class="replay">play</button>
<script src="https://gw.alipayobjects.com/os/lib/antv/g2/4.0.9/dist/g2.min.js"></script>

b. CSS代码块

css
#container {
    width: 100%;
    height: 100%;
    overflow: auto;
    font-size: 12px;
    color: #343d50;
}
.replay {
    position: fixed;
    top: 10px;
    right: 10px;
}

c. Javascript代码块

javascript
var config = {
  loop: false, // 是否循环播放 false: 不循环播放 true: 循环播放,(布尔值,无需引号包裹)
  interval: 1200, // 轮播间隔时间 默认为1200ms, 小于1000ms 以1000ms计, (数值,无需引号包裹)
  sort: 'asc', // 图排序方式 asc: 升序  desc: 降序(默认) 以左下角为原点,(字符串,需用英文引号包裹)
  type: 'bar', // bar: 条形图  column: 柱状图,(字符串,需用英文引号包裹)
  showCount: Infinity, // Infinity: 显示全部(默认),为数字时如10,表示图表中最大显示10个分类,(数值,无需引号包裹)
  textStyle: { // 动态维度
    fontSize: 40, // 字体大小,(数值,无需引号包裹)
    fontWeight: 'bold', // 字体粗细 bold: 加粗(默认) normal: 不加粗,(字符串,需用英文引号包裹)
    color: '#ddd', // 字体颜色,(字符串,需用英文引号包裹)
  },
  padding: [ 20, 60, 20, 60 ], // 图表区域距离容器边缘的上 右 下 左 间距,主要用来腾出区域给横轴/纵轴的分类标签
  colors10: [
    '#5B8FF9',
    '#5AD8A6',
    '#5D7092',
    '#F6BD16',
    '#E86452',
    '#6DC8EC',
    '#945FB9',
    '#FF9845',
    '#1E9493',
    '#FF99C3',
  ], // 分类颜色色板,分类个数小于等于 10 时使用
  colors20: [
    '#5B8FF9',
    '#CDDDFD',
    '#5AD8A6',
    '#CDF3E4',
    '#5D7092',
    '#CED4DE',
    '#F6BD16',
    '#FCEBB9',
    '#E86452',
    '#F8D0CB',
    '#6DC8EC',
    '#D3EEF9',
    '#945FB9',
    '#DECFEA',
    '#FF9845',
    '#FFE0C7',
    '#1E9493',
    '#BBDEDE',
    '#FF99C3',
    '#FFE0ED',
  ], // 分类颜色色板,分类个数小于 20 时使用
}
var sortIsDesc = config.sort === 'desc'
var typeIsBar = config.type === 'bar'
var Chart = G2.Chart
var registerAnimation = G2.registerAnimation
registerAnimation('label-appear', function (element, animateCfg, cfg) {
  var label = element.getChildren()[0];
  var coordinate = cfg.coordinate;
  var startX = coordinate.start.x;
  var finalX = label.attr('x');
  var labelContent = label.attr('text');
  label.attr('x', startX);
  label.attr('text', 0);
  var distance = finalX - startX;
  label.animate(function (ratio) {
    var position = startX + distance * ratio;
    var text = (labelContent * ratio).toFixed(0);
    return {
      x: position,
      text: text,
    };
  }, animateCfg);
});
registerAnimation('label-update', function (element, animateCfg, cfg) {
  var startX = element.attr('x');
  var startY = element.attr('y');
  var finalX = cfg.toAttrs.x;
  var finalY = cfg.toAttrs.y;
  var labelContent = element.attr('text');
  // @ts-ignore
  var finalContent = cfg.toAttrs.text;
  var distanceX = finalX - startX;
  var distanceY = finalY - startY;
  var numberDiff = +finalContent - +labelContent;
  element.animate(function (ratio) {
    var positionX = startX + distanceX * ratio;
    var positionY = startY + distanceY * ratio;
    var text = (+labelContent + numberDiff * ratio).toFixed(0);
    return {
      x: positionX,
      y: positionY,
      text: text,
    };
  }, animateCfg);
});
function transformData (data) {
    var result = {}
    var q = data[0]
    var zones = data[1]
    var values = data[2]
    q.data.forEach(function (item, index) {
        if (result[item]) {
            result[item].push({ value: values.data[index], zone: zones.data[index] })
        } else { result[item] = [{ value: values.data[index], zone: zones.data[index] }] }
    })
    return result
}
function handleData(source) {
  source.sort(function (a, b) {
    if (sortIsDesc) {
      return b.value - a.value;
    } else {
      return a.value - b.value
    }
  });
  return source;
}
function setAnnotation (chart, content) {
  var position = ['90%', '90%']
  var textAlign = 'start'
  switch (true) {
    case typeIsBar && !sortIsDesc:
      position = ['95%', '90%']
      textAlign = 'end'
      break
    case !typeIsBar && !sortIsDesc:
      position = ['5%', '10%']
      break
    case !typeIsBar && sortIsDesc:
      position = ['95%', '10%']
      textAlign = 'end'
      break
    case typeIsBar && sortIsDesc:
    default:
      position = ['95%', '10%']
      textAlign = 'end'
  }
  config.textStyle.textAlign = textAlign
  chart.annotation().text({
    position: position,
    content: content,
    style: config.textStyle,
    animate: false,
  });
}
var $replay = document.querySelector('.replay')
var chart = new Chart({
  container: 'container',
  autoFit: true,
  height: 500,
  padding: config.padding,
});
var interval
function countUp() {
  var dataSource = countUp.dataSource
  var count = countUp.count
  var data = handleData(Object.values(dataSource)[count]).slice(0, config.showCount)
  var colors = data.length <= 10 ? config.colors10 : config.colors20
  var text = Object.keys(dataSource)[count]
  if (count === 0 && !countUp.once) {
    chart.data(data);
    // 水平柱状图 or 垂直柱状图
    if (typeIsBar) {
      chart.coordinate('rect').transpose();
    }
    chart.legend(false);
    chart.tooltip(false);
    chart.axis('zone', {
      animateOption: {
        update: {
          duration: 1000,
          easing: 'easeLinear'
        }
      }
    });
    setAnnotation(chart, text)
    
    chart
      .interval()
      .position('zone*value')
      .color('zone', colors)
      .label('value', function (value) {
        return {
          animate: {
            appear: {
              animation: 'label-appear',
              delay: 0,
              duration: 1000,
              easing: 'easeLinear'
            },
            update: {
              animation: 'label-update',
              duration: 1000,
              easing: 'easeLinear'
            }
          },
          offset: 5,
        };
      }).animate({
        appear: {
          duration: 1000,
          easing: 'easeLinear'
        },
        update: {
          duration: 1000,
          easing: 'easeLinear'
        }
      });
    countUp.once = true
    chart.render();
  } else {
    chart.annotation().clear(true);
    setAnnotation(chart, text)
    chart.changeData(data);
  }
  ++countUp.count;
  if (countUp.count === Object.keys(dataSource).length) {
    if (config.loop) {
      countUp.count = 0
    } else {
        clearInterval(interval);
        $replay.style.display = 'block'
    }
  }
}
function replay () {
  $replay.style.display = 'none'
  clearInterval(interval)
  countUp.count = 0
  countUp()
  interval = setInterval(countUp, config.interval)
}
window.addEventListener('load', function () {
  $replay.addEventListener('click', replay)
})
window.addEventListener('unload', function() {
  $replay.removeEventListener('click', replay)
});
   
function renderChart (data) {
    if (!data) return
    countUp.dataSource = transformData(data[0])
    replay()
}
new GDPlugin().init(renderChart)

Highcharts实现

Highcharts版除了javascript,其他(数据视图的维度和数值定义,CSS代码块)与AntV G2都一致。在config配置上,少了一个padding(因为highcharts能根据标签长度自动调整间距)

a. HTMl代码块

html
div id="container"></div>
<button class="replay">replay</button>
<script src="https://code.highcharts.com/highcharts.js"></script>

b. CSS代码块

css
#container {
    width: 100%;
    height: 100%;
    overflow: auto;
    font-size: 12px;
    color: #343d50;
}
.replay {
    position: fixed;
    top: 10px;
    right: 10px;
}

c. JavaScript代码块

javascript
var config = {
  loop: false, // 是否循环播放 false: 不循环播放 true: 循环播放,(布尔值,无需引号包裹)
  interval: 1200, // 轮播间隔时间 默认为1200ms, 小于1000ms 以1000ms计, (数值,无需引号包裹)
  sort: 'asc', // 图排序方式 asc: 升序  desc: 降序(默认) 以左下角为原点,(字符串,需用英文引号包裹)
  type: 'bar', // bar: 条形图  column: 柱状图,(字符串,需用英文引号包裹)
  showCount: Infinity, // Infinity: 显示全部(默认),为数字时如10,表示图表中最大显示10个分类,(数值,无需引号包裹)
  textStyle: { // 动态维度
    fontSize: 40, // 字体大小,(数值,无需引号包裹)
    fontWeight: 'bold', // 字体粗细 bold: 加粗(默认) normal: 不加粗,(字符串,需用英文引号包裹)
    color: '#ddd', // 字体颜色,(字符串,需用英文引号包裹)
  },
  colors10: [
    '#5B8FF9',
    '#5AD8A6',
    '#5D7092',
    '#F6BD16',
    '#E86452',
    '#6DC8EC',
    '#945FB9',
    '#FF9845',
    '#1E9493',
    '#FF99C3',
  ], // 分类颜色色板,分类个数小于等于 10 时使用
  colors20: [
    '#5B8FF9',
    '#CDDDFD',
    '#5AD8A6',
    '#CDF3E4',
    '#5D7092',
    '#CED4DE',
    '#F6BD16',
    '#FCEBB9',
    '#E86452',
    '#F8D0CB',
    '#6DC8EC',
    '#D3EEF9',
    '#945FB9',
    '#DECFEA',
    '#FF9845',
    '#FFE0C7',
    '#1E9493',
    '#BBDEDE',
    '#FF99C3',
    '#FFE0ED',
  ], // 分类颜色色板,分类个数小于 20 时使用
}
function transformData (data) {
    var result = {}
    var q = data[0]
    var zones = data[1]
    var values = data[2]
    
    q.data.forEach(function (item, index) {
        if (result[item]) {
            result[item].push([ zones.data[index], values.data[index] ])
        } else { result[item] = [[ zones.data[index], values.data[index] ]] }
    })
    return result
}
var sortIsDesc = config.sort === 'desc'
var typeIsBar = config.type === 'bar'
function getPosition (chart, label) {
    var x
    var y
    switch (true) {
        case typeIsBar && !sortIsDesc:
            x = chart.plotWidth + chart.plotLeft - label.width - 10
            y = chart.plotHeight + chart.plotTop - label.height - 10
            break
        case !typeIsBar && !sortIsDesc:
            x = chart.plotLeft + 10
            y = chart.plotTop + 10
            break
        case !typeIsBar && sortIsDesc:
            x = chart.plotWidth + chart.plotLeft - label.width - 10
            y = chart.plotTop + 10
            break
        case typeIsBar && sortIsDesc:
        default:
            x = chart.plotWidth + chart.plotLeft - label.width - 10
            y = chart.plotTop + 10
    }
    return {
        x: x,
        y: y,
    }
}
var chart = Highcharts.chart('container', {
    plotOptions: {
        bar: {
            colorByPoint: true,
        },
        column: {
            colorByPoint: true,
        },
    },
    chart: {
        type: config.type,
        marginLeft: 100,
        
    },
    title: {
        text: '',
    },
    yAxis: {
        title: {
            text: ''
        },
    },
    xAxis: {
        type: 'category',
        labels: {
            animate: true
        },
        reversed: config.sort === 'asc',
    },
    tooltip: {
        enabled: false,
    },
    legend: {
        enabled: false
    },
    series: [{
        dataLabels: {
            enabled: true,
            format: '{y:,.0f}'
        },
        dataSorting: {
            enabled: true,
            sortKey: 'y'
        },
        data: [],
       
    }]
});
var interval
var label
window.addEventListener('load', function () {
  document.querySelector('.replay').addEventListener('click', replay)
})
window.addEventListener('unload', function() {
  document.querySelector('.replay').removeEventListener('click', replay)
});
function replay () {
  document.querySelector('.replay').style.display = 'none'
  clearInterval(interval)
  countUp.count = 0
  countUp()
}
function countUp () {
    var count = countUp.count
    var dataSource = countUp.dataSource
  
    var textData = Object.keys(dataSource)
    
    var seriesData = Object.values(dataSource)
    
    var length = seriesData.length
    if (countUp.label) countUp.label.destroy()
    countUp.label = chart.renderer.label(textData[count], null, null).add()
    countUp.label.css({
        fontSize: `${config.textStyle.fontSize}px`,
        fontWeight: config.textStyle.fontWeight,
        color: config.textStyle.color,
    }).attr(getPosition(chart, countUp.label))
    var colors = seriesData[0].slice(0, config.showCount).length <= 10 ? config.colors10 : config.colors20
    Highcharts.setOptions({ colors: colors })
    chart.series[0].setData(
        seriesData[countUp.count].map(function (item) { return item.slice(0) }).slice(0, config.showCount),
        true,
        { duration: 1000 }
    )
    ++countUp.count
    interval = setInterval(function () {
        countUp.label.attr({
            text: textData[countUp.count]
        })
        chart.series[0].setData(
            seriesData[countUp.count].map(function (item) { return item.slice(0) }).slice(0, config.showCount),
            true,
            { duration: 1000 }
        )
        ++countUp.count
        if (countUp.count === length) {
            if (config.loop) {
                countUp.count = 0
            } else {
                clearInterval(interval)
                document.querySelector('.replay').style.display = 'block'
            }
        }
    }, config.interval < 1000 ? 1000 : config.interval)
}
function renderChart (data) {
    if (!data) return null
    countUp.dataSource = transformData(data[0])
    replay()
}
new GDPlugin().init(renderChart)

 Echarts实现

Echarts与AntV G2的不同点在于,配置项中暂时没有柱状图形式(type = 'column'),没有padding,且非无限循环时播放按钮一直存在

a. HTML代码块

html
<div id="container"></div>
<button class="replay">play</button>
<script src="https://cdn.jsdelivr.net/npm/echarts@4.7.0/dist/echarts.min.js"></script>

b. CSS代码块

css
#container {
    width: 100%;
    height: 100%;
    overflow: auto;
    font-size: 12px;
    color: #343d50;
}
.replay {
    position: fixed;
    top: 10px;
    right: 10px;
}

c. Javascript代码块

javascript
var config = {
  loop: false, // 是否循环播放 false: 不循环播放 true: 循环播放,(布尔值,无需引号包裹)
  interval: 1200, // 轮播间隔时间 默认为1200ms, (数值,无需引号包裹)
  sort: 'asc', // 图排序方式 asc: 升序  desc: 降序(默认) 以左下角为原点,(字符串,需用英文引号包裹)
  showCount: Infinity, // Infinity: 显示全部(默认),为数字时如10,表示图表中最大显示10个分类,(数值,无需引号包裹)
  textStyle: { // 动态维度
    fontSize: 40, // 字体大小,(数值,无需引号包裹)
    fontWeight: 'bold', // 字体粗细 bold: 加粗(默认) normal: 不加粗,(字符串,需用英文引号包裹)
    color: '#ddd', // 字体颜色,(字符串,需用英文引号包裹)
  },
  colors10: [
    '#5B8FF9',
    '#5AD8A6',
    '#5D7092',
    '#F6BD16',
    '#E86452',
    '#6DC8EC',
    '#945FB9',
    '#FF9845',
    '#1E9493',
    '#FF99C3',
  ], // 分类颜色色板,分类个数小于等于 10 时使用
  colors20: [
    '#5B8FF9',
    '#CDDDFD',
    '#5AD8A6',
    '#CDF3E4',
    '#5D7092',
    '#CED4DE',
    '#F6BD16',
    '#FCEBB9',
    '#E86452',
    '#F8D0CB',
    '#6DC8EC',
    '#D3EEF9',
    '#945FB9',
    '#DECFEA',
    '#FF9845',
    '#FFE0C7',
    '#1E9493',
    '#BBDEDE',
    '#FF99C3',
    '#FFE0ED',
  ], // 分类颜色色板,分类个数小于 20 时使用
}
var sortIsDesc = config.sort === 'desc'
function transformData (data) {
    var result = {}
    var q = data[0]
    var zones = data[1]
    var values = data[2]
    q.data.forEach(function (item, index) {
        if (result[item]) {
            result[item].push({ value: values.data[index], name: zones.data[index] })
        } else { result[item] = [{ value: values.data[index], name: zones.data[index] }] }
    })
    return result
}
function handleData(source) {
  source.sort(function (a, b) {
    return a.value - b.value
  });
  return source;
}
function getPosition () {
  var left
  var top
  var bottom
  switch (true) {
      case !sortIsDesc:
          right = 10
          bottom = 20
          break
      case sortIsDesc:
      default:
          right = 10
          top = 10
  }
  return {
      right: right,
      top: top,
      bottom: bottom,
  }
}
var position = getPosition()
var $replay = document.querySelector('.replay')
var myChart = echarts.init(document.getElementById('container'))
var option = {
  baseOption: {
    animationDurationUpdate: 1000,
    animationEasingUpdate: 'quinticInOut',
    title: {
      textStyle: config.textStyle,
      right: position.right,
      top: position.top,
      bottom: position.bottom,
    },
    timeline: {
        data: [],
        axisType: 'category',
        autoPlay: true,
        show: false,
        playInterval: config.interval,//播放速度
        loop: config.loop,
    },
    grid: {
      left: 0,
      bottom: 0,
      top: 0,
      right: '5%',
      containLabel: true,
    },
    xAxis: {
      type: 'value'
    },
    yAxis: {
      type: 'category',
      inverse: config.sort === 'desc',
      data: [],
    },
    series: {
        name: '销量',
        type: 'bar',
        label: {      
          show: true,
          position: 'right',
        },
        itemStyle: {
        }
    },
  },
  options: []
};
// 使用刚指定的配置项和数据显示图表。
var $replay = document.querySelector('.replay')
function replay () {
  myChart.setOption(option, true)
}
window.addEventListener('load', function () {
  if (config.loop) {
    $replay.style.display = 'none'
  } else {
    $replay.addEventListener('click', replay)
  }
  
})
window.addEventListener('unload', function() {
  $replay.removeEventListener('click', replay)
});
   
function renderChart (data) {
    if (!data) return
    const dataSource = transformData(data[0])
    
    for (var dynamicVar in dataSource) {
      handleData(dataSource[dynamicVar])
      const yAxisData = dataSource[dynamicVar].map(item => item.name).slice(0, config.showCount)
      option.options.push({
        title: {
          text: dynamicVar
        },
        yAxis: {
          data: yAxisData,
        },
        series: {
          data: dataSource[dynamicVar].slice(0, config.showCount)
        }
      })
      option.baseOption.series.itemStyle.color = function (params) {
        const colors = dataSource[dynamicVar].slice(0, config.showCount).length <= 10 ? config.colors10 : config.colors20
        return colors[yAxisData.indexOf(params.name)]
      }
      option.baseOption.timeline.data.push(dynamicVar)
    }
    myChart.setOption(option);
    
}
new GDPlugin().init(renderChart)


    您需要登录后才可以回复