参考
5 分钟上手 ECharts
引入echarts.js:
- 从 GitHub 中的
echarts/dist
文件夹中获取构建好的 echarts - 使用完全版js:echarts/dist/echarts.js,体积最大,包含所有的图表和组件,复制该文件到
static/js
文件夹下 - 在html文件中引入:
<script src="/static/js/echarts.js"></script>
使用cdn的引入方式,参考:https://www.jsdelivr.com/package/npm/echarts, 在 <head>
引入:
<script src="https://cdn.jsdelivr.net/npm/echarts@5.0.1/dist/echarts.min.js"></script>
创建文件夹 flaskAPP\templates
, 创建模版 index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ECharts</title>
<!-- 引入 echarts.js -->
<script src="/static/js/echarts.js"></script>
</head>
<body>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
var myChart2 = echarts.init(document.getElementById('main2'));
// 指定图表的配置项和数据
var option = {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data:['销量']
},
xAxis: {
data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
myChart2.setOption(option);
</script>
</body>
</html>
根目录下创建 server.py
from flask import Flask,render_template
app = Flask(__name__, static_folder="templates")
@app.route("/")
def index():
return render_template("index.html")
if __name__ == '__main__':
app.run(host='127.0.0.1',port='5000',debug=True)
执行 python server.py
后访问 http://127.0.0.1:5000/
ECharts 基础概念
一个网页中可以创建多个 echarts 实例,每个 echarts 实例独占一个 DOM 节,如上例中的 myChart。每个 echarts 实例 中可以创建多个图表和坐标系等等(用 option 来描述)。准备一个 DOM 节点(作为 echarts 的渲染容器),就可以在上面创建一个 echarts 实例。
以下建了两个 div标签装两个chart。
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
var option = {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data:['销量']
},
xAxis: {
data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
</script>
<div id="main2" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
var myChart2 = echarts.init(document.getElementById('main2'));
myChart2.setOption(option);
</script>
系列(series):一组数值以及他们映射成的图。“系列”这个词原本可能来源于“一系列的数据”,而在 echarts 中取其扩展的概念,不仅表示数据,也表示数据映射成为的图。所以,一个 系列 包含的要素至少有:
- 一组数值
- 图表类型(series.type)
- 以及其他的关于这些数据如何映射成图的参数。
echarts 里系列类型(series.type)就是图表类型。系列类型(series.type)至少有:line(折线图)、bar(柱状图)、pie(饼图)、scatter(散点图)、graph(关系图)、tree(树图)、…
var option = {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data:['销量','价格']
},
xAxis: {
data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
},
{
name: '价格',
type: 'line',
data: {{data2}}
},
]
};
组件(component):在系列之上,echarts 中各种内容,被抽象为“组件”。组件和系列应该是平级的。系列(series)也是一种组件,可以理解为:系列是专门绘制“图”的组件。
echarts 中至少有这些组件:xAxis(直角坐标系 X 轴)、yAxis(直角坐标系 Y 轴)、grid(直角坐标系底板)、angleAxis(极坐标系角度轴)、radiusAxis(极坐标系半径轴)、polar(极坐标系底板)、geo(地理坐标系)、dataZoom(数据区缩放组件)、visualMap(视觉映射组件)、tooltip(提示框组件)、toolbox(工具栏组件)、series(系列)、…
option:echarts 使用 option 来描述其对图表的各种需求,包括:有什么数据、要画什么图表、图表长什么样子、含有什么组件、组件能操作什么事情等等。简而言之,option 表述了:数据、数据如何映射成图形、交互行为。
所以正常的echats处理逻辑:
- js里为各个图形在 html中申请一个容器,比如某id的 div
- js里配置option:包括数据和其他图形配置选项
- 每个图形调用特定的option进行渲染,option可以在python中定义
python中定义 echarts 的option,传递给模版渲染
index.html,注意使用 tojson:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ECharts</title>
<!-- 引入 echarts.js -->
<script src="/static/js/echarts.js"></script>
</head>
<body>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript" >
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
var option = {{myChartOption|tojson}};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
</script>
</body>
</html>
server.py,注意使用 JSON_AS_ASCII避免中文乱码:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask import Flask,render_template,url_for
app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False
@app.route("/")
def index():
echatsjs = url_for("static",filename="js/echarts.js")
myChartOption = {
'title': {
'text': 'ECharts 入门示例'
},
'tooltip': {},
'legend': {
'data':['销量','价格']
},
'xAxis': {
'data': ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
},
'yAxis': {},
'series': [{
'name': '销量',
'type': 'bar',
'data': [5, 20, 36, 10, 10, 20]
},
{
'name': '价格',
'type': 'line',
'data': [10, 40, 76, 20, 20, 400]
}]
}
return render_template("index.html",echatsjs=echatsjs,myChartOption = myChartOption)
if __name__ == '__main__':
app.run(host='127.0.0.1',port='5000',debug=True)
这样模版页面只需要定义图形id等, 图形的渲染信息由flask后端传递给模版。
K线图
参考: ECharts图表使用之K线图.
echarts官方版本: https://echarts.apache.org/examples/zh/editor.html?c=candlestick-brush
效果:
源码:
<div id="mainec" style="width: 600px;height:400px;"></div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.0.1/dist/echarts.min.js"></script>
<script type="text/javascript">
//var echarts = require('echarts');
var ROOT_PATH = 'https://cdn.jsdelivr.net/gh/apache/echarts-website@asf-site/examples';
var chartDom = document.getElementById('mainec');
var myChart = echarts.init(chartDom);
var option;
var upColor = '#00da3c';
var downColor = '#ec0000';
function splitData(rawData) {
var categoryData = [];
var values = [];
var volumes = [];
for (var i = 0; i < rawData.length; i++) {
categoryData.push(rawData[i].splice(0, 1)[0]);
values.push(rawData[i]);
volumes.push([i, rawData[i][4], rawData[i][0] > rawData[i][1] ? 1 : -1]);
}
return {
categoryData: categoryData,
values: values,
volumes: volumes
};
}
function calculateMA(dayCount, data) {
var result = [];
for (var i = 0, len = data.values.length; i < len; i++) {
if (i < dayCount) {
result.push('-');
continue;
}
var sum = 0;
for (var j = 0; j < dayCount; j++) {
sum += data.values[i - j][1];
}
result.push(+(sum / dayCount).toFixed(3));
}
return result;
}
$.get(ROOT_PATH + '/data/asset/data/stock-DJI.json', function (rawData) {
var data = splitData(rawData);
myChart.setOption(option = {
animation: false,
legend: {
bottom: 10,
left: 'center',
data: ['Dow-Jones index', 'MA5', 'MA10', 'MA20', 'MA30']
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
borderWidth: 1,
borderColor: '#ccc',
padding: 10,
textStyle: {
color: '#000'
},
position: function (pos, params, el, elRect, size) {
var obj = {top: 10};
obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 30;
return obj;
}
// extraCssText: 'width: 170px'
},
axisPointer: {
link: {xAxisIndex: 'all'},
label: {
backgroundColor: '#777'
}
},
toolbox: {
feature: {
dataZoom: {
yAxisIndex: false
},
brush: {
type: ['lineX', 'clear']
}
}
},
brush: {
xAxisIndex: 'all',
brushLink: 'all',
outOfBrush: {
colorAlpha: 0.1
}
},
visualMap: {
show: false,
seriesIndex: 5,
dimension: 2,
pieces: [{
value: 1,
color: downColor
}, {
value: -1,
color: upColor
}]
},
grid: [
{
left: '10%',
right: '8%',
height: '50%'
},
{
left: '10%',
right: '8%',
top: '63%',
height: '16%'
}
],
xAxis: [
{
type: 'category',
data: data.categoryData,
scale: true,
boundaryGap: false,
axisLine: {onZero: false},
splitLine: {show: false},
splitNumber: 20,
min: 'dataMin',
max: 'dataMax',
axisPointer: {
z: 100
}
},
{
type: 'category',
gridIndex: 1,
data: data.categoryData,
scale: true,
boundaryGap: false,
axisLine: {onZero: false},
axisTick: {show: false},
splitLine: {show: false},
axisLabel: {show: false},
splitNumber: 20,
min: 'dataMin',
max: 'dataMax'
}
],
yAxis: [
{
scale: true,
splitArea: {
show: true
}
},
{
scale: true,
gridIndex: 1,
splitNumber: 2,
axisLabel: {show: false},
axisLine: {show: false},
axisTick: {show: false},
splitLine: {show: false}
}
],
dataZoom: [
{
type: 'inside',
xAxisIndex: [0, 1],
start: 98,
end: 100
},
{
show: true,
xAxisIndex: [0, 1],
type: 'slider',
top: '85%',
start: 98,
end: 100
}
],
series: [
{
name: 'Dow-Jones index',
type: 'candlestick',
data: data.values,
itemStyle: {
color: upColor,
color0: downColor,
borderColor: null,
borderColor0: null
},
tooltip: {
formatter: function (param) {
param = param[0];
return [
'Date: ' + param.name + '<hr size=1 style="margin: 3px 0">',
'Open: ' + param.data[0] + '<br/>',
'Close: ' + param.data[1] + '<br/>',
'Lowest: ' + param.data[2] + '<br/>',
'Highest: ' + param.data[3] + '<br/>'
].join('');
}
}
},
{
name: 'MA5',
type: 'line',
data: calculateMA(5, data),
smooth: true,
lineStyle: {
opacity: 0.5
}
},
{
name: 'MA10',
type: 'line',
data: calculateMA(10, data),
smooth: true,
lineStyle: {
opacity: 0.5
}
},
{
name: 'MA20',
type: 'line',
data: calculateMA(20, data),
smooth: true,
lineStyle: {
opacity: 0.5
}
},
{
name: 'MA30',
type: 'line',
data: calculateMA(30, data),
smooth: true,
lineStyle: {
opacity: 0.5
}
},
{
name: 'Volume',
type: 'bar',
xAxisIndex: 1,
yAxisIndex: 1,
data: data.volumes
}
]
}, true);
myChart.dispatchAction({
type: 'brush',
areas: [
{
brushType: 'lineX',
coordRange: ['2016-06-02', '2016-06-20'],
xAxisIndex: 0
}
]
});
});
option && myChart.setOption(option);
</script>