数据可视化1_echarts

文章目录


最好的选择,echarts和d3.js

动态界面

动态添加删除水果

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>动态列表 - Vue.js</title>
   <style>
      * {
         margin: 0;
         padding: 0;
      }
      body {
         background-color: #000;
         color: #fff;
      }
      #app {
         width: 40%;
         margin: 20px auto;
      }
      #fruits>li {
         width: 90%;
         height: 50px;
         background-color: #6ca;
         margin: 4px 0;
         text-align: center;
         font-size: 20px;
         list-style-type: none;
         line-height: 50px;
      }
      #fruits+div {
         margin-top: 20px;
      }
      #app>div>input[type=text] {
         width: 70%;
         height: 40px;
         color: #fff;
         border-radius: 8px;
         border: none;
         outline: none;
         font-size: 20px;
         text-align: center;
         vertical-align: middle;
         background-color: #999;
      }
      #ok {
         width: 19%;
         height: 40px;
         color: #fff;
         background-color: #a45;
         border: none;
         outline: none;
         font-size: 16px;
         vertical-align: middle;
      }
   </style>
</head>
<body>
   <div id="app">
      <ul id="fruits">
         <li v-for="fruit in fruits" @click="removeItem(fruit)">{{ fruit }}</li>
      </ul>
      <div>
         <input type="text" v-model="fruitName" @keydown.enter="addItem()">
         <button id="ok" @click="addItem()">确定</button>
      </div>
   </div>
   <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.min.js"></script>
   <script>
      let app = new Vue({
         el: '#app',
         data: {
            fruits: ['榴莲', '番茄', '葡萄'],
            fruitName: ''
         },
         methods: {
            removeItem(fruit) {
               let index = this.fruits.indexOf(fruit)
               this.fruits.splice(index, 1)
            },
            addItem() {
               let name = this.fruitName.trim()
               if (name.length > 0) {
                  this.fruits.push(name)
               }
               this.fruitName = ''
            }
         }
      })
   </script>
</body>
</html>

渲染

后端渲染

通过Java、Python、PHP等的代码渲染模板页面,生成动态内容
缺点:
1.搞后端的人需要具备前端知识,
2.开销较大,在后端渲染会增加服务器的负担和压力,

前端渲染

后端是负责处理业务和提供数据,前端实现页面渲染(前后端分离开发)
优点:
1.前后端相互不影响,工作是独立的
2.通过JS引擎实现页面渲染,不增加服务器的负担
Python程序会提供JSON格式的数据,前端通过JavaScript请求JSON数据,
获得数据后通过Vue.js实现页面的渲染(把数据动态地填写到页面上)

Echarts

K线图

<head>
    <meta charset="UTF-8">
    <link href="/static/css/index.css" rel="stylesheet">
    <title>首页</title>
</head>
<body>
    <!-- 
        后端渲染:通过Java、Python、PHP等的代码渲染模板页面,生成动态内容
        缺点:
            1.搞后端的人需要具备前端知识,
            2.开销较大,在后端渲染会增加服务器的负担和压力,
        前端渲染:后端是负责处理业务和提供数据,前端实现页面渲染(前后端分离开发)
            优点:
                1.前后端相互不影响,工作是独立的
                2.通过JS引擎实现页面渲染,不增加服务器的负担
            Python程序会提供JSON格式的数据,前端通过JavaScript请求JSON数据,
            获得数据后通过Vue.js实现页面的渲染(把数据动态地填写到页面上)    
    -->
<div id="app">
    <ul id="general">
        <li v-for="item in data_items">
            <img :src="'/static/images/'+item.icon" width="36">
<!--           ‘:’ ---动态属性-->
            <div style="display: inline-block">
                <div >{{ item.value | numberFormat }}</div>
                <div>{{ item.name }}</div>
            </div>
        </li>

    </ul>
</div>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.min.js"></script>
    <script>
        // 通过JS代码获取JSON数据,然后交给Vue.js渲染界面

        let app = new Vue({
            el:'#app',
            data:{
                data_items:[]
            },
            //过滤器
            filters:{
                numberFormat(x){
                    return x.toLocaleString()
                }
            },
            created(){
                // Promise
                // fetch('/api/fruits')
                // .then(function(resp) {return resp.json()})
                // .then(function(json){app.selected_fruits=json.fruits})
                                    // 这个是app不是this是因为进了function函数之后
                                    // this会变成promise,而不是app
                                    // 但是我们要给app的水果赋值
                //  .then(resp=>resp.json())
            //  .then(json=>this.selected_fruits=json.fruits)
             //  =>可以不改变上下文环境,上面是app,下面也是app
                fetch('/api/general_data')
                    .then(resp=>resp.json())
                    .then(json=>this.data_items=json.items)
            }

        })

    </script>
<!--2.创建div-->
<div id="main" style="width: 800px">

</div>
<!--1.引入echarts-->
<script src="https://cdn.jsdelivr.net/npm/echarts@5.1.2/dist/echarts.min.js"></script>
<script>
    //将指定的div处理成绘图的画布
    let myChart = echarts.init(document.querySelector('#main'))
    // 准备图标需要使用到的数据

	let colorList = ['#c23531','#2f4554', '#61a0a8', '#d48265', '#91c7ae','#749f83',  '#ca8622', '#bda29a','#6e7074', '#546570', '#c4ccd3'];
	let labelFont = 'bold 12px Sans-serif';
// 计算MA函数
function calculateMA(dayCount, data) {
    let result = [];
    for (let i = 0, len = data.length; i < len; i++) {
        if (i < dayCount) {
            result.push('-');
            continue;
        }
        let sum = 0;
        for (let j = 0; j < dayCount; j++) {
            sum += data[i - j][1];
        }
        result.push((sum / dayCount).toFixed(2));
    }
    return result;
}
//fetch部分
fetch('/api/k_data')
    .then(resp=>resp.json())
    .then(json=>{
    let dataMA5 = calculateMA(5, json.data);
    let dataMA10 = calculateMA(10, json.data);
    let dataMA20 = calculateMA(20, json.data);

    let option = {
        animation: false,
        color: colorList,
        title: {
            left: 'center',
            text: '移动端 K线图'
        },
        legend: {
            top: 30,
            data: ['日K', 'MA5', 'MA10', 'MA20', 'MA30']
        },
        tooltip: {
            triggerOn: 'none',
            transitionDuration: 0,
            confine: true,
            borderRadius: 4,
            borderWidth: 1,
            borderColor: '#333',
            backgroundColor: 'rgba(255,255,255,0.9)',
            textStyle: {
                fontSize: 12,
                color: '#333'
            },
            position: function (pos, params, el, elRect, size) {
                let obj = {
                    top: 60
                };
                obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 5;
                return obj;
            }
        },
        axisPointer: {
            link: [{
                xAxisIndex: [0, 1]
            }]
        },
        dataZoom: [{
            type: 'slider',
            xAxisIndex: [0, 1],
            realtime: false,
            start: 20,
            end: 70,
            top: 65,
            height: 20,
            handleIcon: 'path://M10.7,11.9H9.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.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: '120%'
        }, {
            type: 'inside',
            xAxisIndex: [0, 1],
            start: 40,
            end: 70,
            top: 30,
            height: 20
        }],
        xAxis: [{
            type: 'category',
            data: json.dates,
            boundaryGap : false,
            axisLine: { lineStyle: { color: '#777' } },
            axisLabel: {
                formatter: function (value) {
                    return echarts.format.formatTime('MM-dd', value);
                }
            },
            min: 'dataMin',
            max: 'dataMax',
            axisPointer: {
                show: true
            }
        }, {
            type: 'category',
            gridIndex: 1,
            data: json.dates,
            scale: true,
            boundaryGap : false,
            splitLine: {show: false},
            axisLabel: {show: false},
            axisTick: {show: false},
            axisLine: { lineStyle: { color: '#777' } },
            splitNumber: 20,
            min: 'dataMin',
            max: 'dataMax',
            axisPointer: {
                type: 'shadow',
                label: {show: false},
                triggerTooltip: true,
                handle: {
                    show: true,
                    margin: 30,
                    color: '#B80C00'
                }
            }
        }],
        yAxis: [{
            scale: true,
            splitNumber: 2,
            axisLine: { lineStyle: { color: '#777' } },
            splitLine: { show: true },
            axisTick: { show: false },
            axisLabel: {
                inside: true,
                formatter: '{value}\n'
            }
        }, {
            scale: true,
            gridIndex: 1,
            splitNumber: 2,
            axisLabel: {show: false},
            axisLine: {show: false},
            axisTick: {show: false},
            splitLine: {show: false}
        }],
        grid: [{
            left: 20,
            right: 20,
            top: 110,
            height: 120
        }, {
            left: 20,
            right: 20,
            height: 40,
            top: 260
        }],
        graphic: [{
            type: 'group',
            left: 'center',
            top: 70,
            width: 300,
            bounding: 'raw',
            children: [{
                id: 'MA5',
                type: 'text',
                style: {fill: colorList[1], font: labelFont},
                left: 0
            }, {
                id: 'MA10',
                type: 'text',
                style: {fill: colorList[2], font: labelFont},
                left: 'center'
            }, {
                id: 'MA20',
                type: 'text',
                style: {fill: colorList[3], font: labelFont},
                right: 0
            }]
        }],
        series: [{
            name: 'Volume',
            type: 'bar',
            xAxisIndex: 1,
            yAxisIndex: 1,
            itemStyle: {
                color: '#7fbe9e'
            },
            emphasis: {
                itemStyle: {
                    color: '#140'
                }
            },
            data: json.volumes
        }, {
            type: 'candlestick',
            name: '日K',
            data: json.data,
            itemStyle: {
                color: '#ef232a',
                color0: '#14b143',
                borderColor: '#ef232a',
                borderColor0: '#14b143'
            },
            emphasis: {
                itemStyle: {
                    color: 'black',
                    color0: '#444',
                    borderColor: 'black',
                    borderColor0: '#444'
                }
            }
        }, {
            name: 'MA5',
            type: 'line',
            data: dataMA5,
            smooth: true,
            showSymbol: false,
            lineStyle: {
                width: 1
            }
        }, {
            name: 'MA10',
            type: 'line',
            data: dataMA10,
            smooth: true,
            showSymbol: false,
            lineStyle: {
                width: 1
            }
        }, {
            name: 'MA20',
            type: 'line',
            data: dataMA20,
            smooth: true,
            showSymbol: false,
            lineStyle: {
                width: 1
            }
        }]
    }
    myChart.setOption(option);
})
    // 把数据加到图表上

</script>
</body>
</html>

后端部分

import pymysql
from flask import Flask, redirect

app = Flask(__name__)


@app.route('/', endpoint='index')
def show_index():
    # 将请求重定向到static目录下的index.html
    return redirect(r'static\index.html')


# API  -   Application Programming Interface - 应用程序编程接口
# 网络API(网络数据接口) - 请求这个URL就可以获得对应的数据(JSON格式)

@app.route('/api/k_data')
def get_stock_data():
    conn = pymysql.connect(host='127.0.0.1', port=3306,
                           user='root', password='root',
                           database='stock', charset='utf8mb4')
    dates, data, volumes = [], [], []
    try:
        with conn.cursor(pymysql.cursors.DictCursor) as cursor:
            cursor.execute(
                'select trade_date,open_price,close_price,low_price,high_price,trade_volume from tb_baba_stock;'
            )

            row_dict = cursor.fetchone()
            print(row_dict)
            while row_dict:
                dates.append(row_dict['trade_date'].strftime('%Y-%m-%d'))
                volumes.append(float(row_dict['trade_volume']))
                tmp_data=[float(row_dict['open_price']),float(row_dict['close_price']),float(row_dict['low_price']),float(row_dict['high_price']),float(row_dict['trade_volume'])]
                data.append(tmp_data)
                row_dict = cursor.fetchone()

    except pymysql.MySQLError as err:
        print(err)
    finally:
        # 一定要释放连接
        conn.close()
    return {'dates': dates, 'data': data, 'volumes': volumes}
    # https://echarts.apache.org/examples/en/editor.html?c=candlestick-touch


@app.route('/api/general_data')
def get_general_data():
    return {'items': [
        {'icon': 'house.png', 'value': 100000, 'name': '实验室'},
        {'icon': 'what.png', 'value': 200000, 'name': '项目数'},
        {'icon': '2.png', 'value': 300000, 'name': '成果数'},
        {'icon': 'people.png', 'value': 40000, 'name': '专家数'},
        {'icon': 'prize.png', 'value': 5000, 'name': '经费数'},

    ]}


if __name__ == '__main__':
    app.run(host='10.7.174.71', debug=True)

D3.JS

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <!-- 引入 D3.JS 文件 -->
    <script src="https://d3js.org/d3.v5.min.js"></script>
<style>
    #main{
        width: 800px;
        height: 420px;
        margin: 0 auto;
        border: 1px solid black;
    }
</style>
</head>
<body>
<!--<div id="main" style="width: 800px">-->
<!--</div>-->
    <p>dog</p>
    <p>cat</p>
    <script>
        var p=d3.select("body").selectAll("p");
        // d3里选择元素的函数有两个
        // d3.select()
        // d3.selectAll()
        p.text("helloWorld!")
    //D3.js中绑定数据的两个函数
        // data():讲一个数组绑定到选择集上,数组各项和选择集各元素绑定,也就是一一对应的关系(这里或许敏锐的你会发现问题,下一章节讲)
        // datum():将一个数据绑定到所有选择集上
        
        var str = "is an animal";
        var p = d3.select("body").selectAll("p");
        p.datum(str).text(function (d,i) {
            return "第"+i+"个元素"+d;
        })
    //    将str绑定在三个<p>选择集上,通过匿名函数
    //function(d,i)访问到绑定的元素,d:数据,i:索引
    </script>
</body>

</html>
var dataset = ["cute","fat","mad"];
var p = d3.select("body").selectAll("p");
p.data(dataset)
    .text(function (d,i) {
        return "第"+i+"个动物"+d;
    })
// 第x个动物xx

下一章节讲)
// datum():将一个数据绑定到所有选择集上

var str = “is an animal”;
var p = d3.select(“body”).selectAll(“p”);
p.datum(str).text(function (d,i) {
return “第”+i+“个元素”+d;
})
// 将str绑定在三个

选择集上,通过匿名函数
//function(d,i)访问到绑定的元素,d:数据,i:索引

```
var dataset = ["cute","fat","mad"];
var p = d3.select("body").selectAll("p");
p.data(dataset)
    .text(function (d,i) {
        return "第"+i+"个动物"+d;
    })
// 第x个动物xx
上一篇:《ECharts》伪立体柱状图


下一篇:echarts 双y轴 实现