数据可视化概述

什么是数据可视化?

1、基本概念

数据可视化,是关于数据视觉表现形式的科学技术研究。就是将数据转换成易于人员辨识和理解的视觉表现形式,如各种2D图表、3D图标、地图、矢量图等等,随着技术的不断进步,数据可视化的边界也在不断扩大。

2、发展历史

起源:数据可视化起源于20世界60年代诞生的计算机图形学

计算机图形学:是一种使用数学算法将二维或三维图形转化为计算机显示器的栅格形式的科学。

计算机图形学入门:https://zhihu.com/question/41468803
3、典型应用: excel,xmind,visio,数据大屏,数据报表,地图

数据可视化的入门技术

Chrome使用Skia作为绘图引擎,向上层开放了canvas,svg,WebGl,HTML等绘图能力。
1、canvas介绍

绘制点、矩形、直线和圆。

<!DOCTYPE html>
<html>
  <head></head>
  <body>
      <canvas id="canvas" width="800" height="800"></canvas>
      <script>
          const canvas = document.getElementById('canvas');
          const ctx = canvas.getContext('2d');//获取到Canvas对象
          ctx.fillStyle = 'red';
          ctx.fillRect(0,0,50,50);
          
          //绘制线
          ctx.beginPath();
          ctx.lineWidth = 2;
          ctx.strokeStyle = 'blue';
          ctx.moveTo(100,100);
          ctx.lineTo(250,75);
          ctx.lineTo(300,100);
          ctx.stroke();
          
          //绘制圆
          ctx.beginPath();
          ctx.lineWidth = 2;
          ctx.strokeStyle = 'green';
          ctx.fillStyle = 'red';
          ctx.arc(200,200,50,0,2 * Math.PI);
          ctx.stroke();
          ctx.fill();

          //绘制点
          ctx.beginPath();
          ctx.lineWidth = 1;
          ctx.strokeStyle = 'red';
          ctx.moveTo(300,300);
          ctx.lineTo(301,301);
          ctx.stroke();
      </script>
  </body>
</html>

图片压缩:

<!DOCTYPE html>
<html>
  <head></head>
  <body>
      <input type="file" id="upload" />
      <script>
         const ACCEPT = ['image/jpg','image/png','image/jpeg'];
         const MAXSIZE = 3*1024*1024;
         const MAXSIZE_STR = '3MB';
         function convertImageToBase64(file,callback){
             let reader = new FileReader();
             reader.addEventListener('load',function(e){
                const base64Image = e.target.result;
                callback && callback(base64Image);
                reader = null;
             })
             reader.readAsDataURL(file);
         }
         function compress(base64Image,callback){
              let maxW = 1024;
              let maxH = 1024;
              const image = new Image();
              image.addEventListener('load',function(e){
                let ratio;//图片的压缩比
                let needCompress = false;//是否需要压缩
                if(maxW < image.naturalWidth){
                  needCompress = true;
                  ratio = image.naturalWidth/maxW;
                  maxW = image.naturalWidth/ratio;
                }
                if(!needCompress){
                  maxW = image.naturalWidth;
                  maxH = image.naturalHeight;
                }
                const canvas = document.createElement('canvas');
                canvas.setAttribute('id','__compress__');
                canvas.width = maxW;
                canvas.height = maxH;
                canvas.style.visibility = 'hidden';
                document.body.appendChild(canvas);

                const ctx = canvas.getContext('2d');
                ctx.clearRect(0,0,maxW,maxH);
                ctx.drawImage(image,0,0,maxW,maxH);
                const compressImage = canvas.toDataURL('image/jpeg',0.1);
                callback && callback(compressImage);
                const _image = new Image();
                _image.src = compressImage;
                document.body.append(_image);
                console.log(image.src.length/_image.src.length)
                canvas.remove();
              })
              image.src = base64Image;
              document.body.appendChild(image);
         }
         function uploadToServer(compressImage){
            console.log('upload to server ...',compressImage)
        }
         const upload = document.getElementById('upload');
         upload.addEventListener('change',function(e){
              const [file] = e.target.files;
              if(!file){
                  return;
              }
              const { type:fileType,size:fileSize } = file;
              if(!ACCEPT.includes(fileType)){
                  alert(`不支持[${fileType}]文件类型!`);
                  upload.value = '';
                  return;
              }
              if(fileSize > MAXSIZE){
                  alert(`文件超出${MAXSIZE_STR}`);
                  upload.value = '';
                  return;
              }
              convertImageToBase64(file,(base64Image)=>compress(base64Image,uploadToServer))
         })
      </script>
  </body>
</html>

2、 SVG介绍
是一种基于XML的图像文件格式,它的英文全称为Scalable Vector Graphics,可缩放的矢量图形。

<!DOCTYPE html>
<html>
  <head></head>
  <body>
      <svg width="800" height="800">
           <react
               width = "50"
               height = "50"
               style="fill:red;stroke-width:1px;stroke:rgb(0,0,0)"
           />
          <line
              x1="100"
              y1="100"
              x2="250"
              y2="75"
              style="stroke:blue;stroke-width:1;"
           />
          <line
              x1="250"
              y1="75"
              x2="300"
              y2="100"
              style="stroke:blue;stroke-width:1;"
           />
          <circle
            cx="200"
            cy="200"
            r="50"
            stroke="green"
            stroke-width = "2"
         />
     </svg>
  </body>
</html>

3、webgl 绘制一个点

<!DOCTYPE html>
<html>
  <head></head>
  <body>
      <canvas id="canvas" width="200" height="200"></canvas>
      <script>
        
          window.onload=function (){
              //顶点着色器
              var VSHADER_SOURCE = 
              "void main() {"+
              "gl_Position = vec4(0.0,0.0,0.0,1.0);"+
              "gl_PointSize = 10.0; "+
              "} ";
              //片元着色器
              var FSHADER_SOURCE = 
                    "void main() {"+
                    "gl_FragColor = vec4(1.0,0.0,0.0,1.0);"+
                    "}";
              const canvas = document.getElementById('canvas');
              const gl= canvas.getContext('webgl');//获取到Canvas对象
              if(!gl){
                  console.log("Failed");
                  return
              }
              //编译着色器
              var vertShader = gl.createShader(gl.VERTEX_SHADER);
              gl.shaderSource(vertShader,VSHADER_SOURCE);
              gl.compileShader(vertShader);
              var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
              gl.shaderSource(fragShader,FSHADER_SOURCE);
              gl.compileShader(fragShader);

              //合并程序
              var shaderProgram = gl.createProgram();
              gl.attachShader(shaderProgram,vertShader);
              gl.attachShader(shaderProgram,fragShader);
              gl.linkProgram(shaderProgram);
              gl.useProgram(shaderProgram);
        
              //绘制一个点
              gl.drawArrays(gl.POINTS,0,1);
          }
      </script>
  </body>
</html>

4、zrender

zrender是echarts的底层渲染引擎。提供Canvas,SVG,VML等多种渲染方式

<!DOCTYPE html>
<html>
  <head>
      <script src="https://cdn.jsdelivr.net/npm/zrender@4.3.0/dist/zrender.js"></script>
  </head>
  <body>
      <div id="container" style="width:800px;height:800px"></div>
      <script>
        const zr = zrender.init(document.getElementById('container'));
        //绘制矩形
        const react = new zrender.Rect({
            shape:{
                x:0,
                y:0,
                width:50,
                height:50
            },
            style:{
              fill:'red',
              lineWidth:0
            }
        })
        //绘制线
        const line = new zrender.Polyline({
            shape:{
                points:[
                  [100,100],
                  [250,75],
                  [300,100]
                ]
            },
            style:{
                stroke:'blue',
                lineWidth:1
            }
        })
        //绘制圆
        const circle = new zrender.Circle({
            shape:{
                cx:200,
                cy:200,
                 r:50
            },
            style:{
                fill:'red',
                stroke:'green',
                lineWidth:2
            }
        })
        //绘制点
        const point= new zrender.Polyline({
            shape:{
                points:[
                  [300,300],
                  [301,301]
                ]
            },
            style:{
                stroke:'blue',
                lineWidth:1
            }
        })
        zr.add(react)
        zr.add(line)
        zr.add(circle)
        zr.add(point)
      </script>
  </body>
</html>

zrender使用总结:

1 引入zrender库
2 编写div容器
3 初始化zrender对象
4 初始化zrender绘图对象
5 调用zrender add方法绘图
zrender官网 https://ecomfe.github.io/zrender-doc/public/

zrender案例库 https://ecomfe.github.io/zrender-doc/public/examples

案例源码库 https://www.github.com/ecomfe/zrender-doc/blob/master/public/examples

5、D3(Data-Driven Documents) 是一个JavaScript图形库,基于Canvas,Svg和HTML。
案例库

https://www.observablehq.com/@d3/gallery

学习地址:https://www.zhuanlan.zhihu.com/p/38001672

数据绑定:

<!DOCTYPE html>
<html>
  <head>
      <script src="https://d3js.org/d3.v5.js"></script>
  </head>
  <body>
      <p>Vue</p>
      <p>React</p>
      <p>Angular</p>
      <button id="dutam">dutam</button>
      <button id="data">data</button>
      <script>
          const body = d3.select('body')
          const p = body.selectAll('p');
          function doDatum(){
              const str = 'Framework';
              p.datum(str);
              p.text(function(d,i){
                  return `${d}-${i}`
              })
          }
          function doData(){
              const dataset = ['Vue','React','Agular'];
              p.data(dataset)
                .text(function(d,i){
                    return `${d}-${i}`
                })
          }
          document.getElementById('datum').addEventListener('click',function(e){
              doDatum();
          });
          document.getElementById('data').addEventListener('click',function(e){
              doData();
          })
      </script>
  </body>
</html>

思维导图

https://www.github.com/reclay/vue-d3-tree-example

6、three.js的用法

<!DOCTYPE html>
<html>
  <head>
      <script src="https://cdn.jsdelivr.net/npm/three@0.116.1/build/three.js"></script>
  </head>
  <body>
     
      <script>
          var camera,scene,renderer;
          var geometry,material,mesh;

          init();
          animate();
          function init(){
            camera = new THREE.PerspectiveCamera(70,window.innerWidth/window.innerHeight)  ;
            camera.position.z = 1;
            scene = new THREE.Scene();
            geometry = new THREE.BoxGeometry(0.2,0.2,0.2);
            material = new THREE.MeshNormalMaterial();
            mesh = new THREE.Mesh(geometry,material);
            scene.add(mesh);
            renderer = new THREE.WebGLRenderer({antialias:true});
            renderer.setSize(window.innerWidth,window.innerHeight);
            document.body.appendChild(renderer.domElement);
          }
          function animate(){
             requestAnimationFrame(animate);
             mesh.rotation.x += 0.01;
             mesh.rotation.y += 0.02;
             renderer.render(scene,camera)
          }
      </script>
  </body>
</html>

threejs学习库:

https://www.github.com/mrdoob/three.js

静态文档地址:

https://www.github.com/mrdoob/three.js/tree/dev/docs
7、Highcharts

是一个用纯JavaScript编写的一个图标库,能够很简单便捷的在web网站或是web应用程序添加有交互性的图标,并且免费提供给个人学习、个人网站和非商业用途使用。Highcharts系列包含HighchartsJS,HighstockJS,HighmapsJS工三款软件,均为纯JavaScript编写的HTML5图表库。
能够很简单的在Web网站或是Web应用程序添加有交互性的图标,Hightcharts支持的图标类型有直线图,曲线图,区域图,柱状图,饼状图,散装点图,仪表图,气泡图,瀑布流图等多达20种图表,其中很多图表可以集成在同一个图形中形成混合图。

学习链接:https://www.youbaobao.xyz/datav-docs/guide/guide/highcharts.html

<!DOCTYPE html>
<html>
  <head>
      <script src="https://cdn.highcharts.com.cn/highcharts/highcharts.js"></script>
      <style>
        #container{
          width:800px;
          height:400px;
        }
     </style>
  </head>
  <body>
      <div id="container"></div>
      <script>
            var chart = Highcharts.chart('container',{
                  title:{
                      text:'2010 ~ 2016 年太阳能行业就业人员发展情况'
                  },
                  subtitle:{
                    text:'数据来源:thesolarfoundation.com'
                  },
                  yAxis:{
                    title:{
                       text:'就业人数'
                    }
                  },
                  legend:{
                      layout:'vertical',
                      align:'righ',
                      verticalAlign:'middle'
                  },
                  plotOptions:{
                      series:{
                            label:{
                                connectorAllowed:false
                            },
                            pointStart:2010
                      }
                  },
                  series:[{
                      name:'安装 实施人员',
                      data:[43934,52503,57177,69658,97031,119931,137133,154175]
                  },{
                      name:'工人',
                      data:[24916, 24064, 29742, 29851, 32490, 30282, 38121, 40434]
                  }, {
                      name: '销售',
                      data: [11744, 17722, 16005, 19771, 20185, 24377, 32147, 39387]
                }, {
                      name: '项目开发',
                      data: [null, null, 7988, 12169, 15112, 22452, 34400, 34227]
                }, {
                      name: '其他',
                      data: [12908, 5948, 8105, 11248, 8989, 11816, 18274, 18111]
                }]
            })
      </script>
  </body>
</html>

8、Highstock

Highstock是用纯JavaScript编写的股票图表控件,可以开发股票走势或大数据量的时间轴图表。它包含多个高级导航组件:预设置数据时间范围,日期选择器、滚动条、平移、缩放功能。

案例:平安银行股价图

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://code.highcharts.com.cn/highstock/highstock.js"></script>
    <style>
      #container {
        width: 800px;
        height: 400px;
      }
    </style>
  </head>
  <body>
    <div id="container"></div>
    <script>
      Highcharts.setOptions({
        lang: {
          rangeSelectorZoom: ''
        }
      });
      fetch('https://data.jianshukeji.com/stock/history/000001')
        .then(data => data.json())
        .then(data => {
          if(data.code !== 1) {
                  alert('读取股票数据失败!');
                    return false;
          }
          data = data.data;
          var ohlc = [],
            volume = [],
            dataLength = data.length,
          // set the allowed units for data grouping
          groupingUnits = [[
            'week',                         // unit name
            [1]                             // allowed multiples
          ], [
            'month',
            [1, 2, 3, 4, 6]
          ]],
          i = 0;
          for (i; i < dataLength; i += 1) {
            ohlc.push([
              data[i][0], // the date
              data[i][1], // open
              data[i][2], // high
              data[i][3], // low
              data[i][4] // close
            ]);
            volume.push([
              data[i][0], // the date
              data[i][5] // the volume
            ]);
          }
          // create the chart
          var chart = Highcharts.stockChart('container', {
            rangeSelector: {
              selected: 1,
              inputDateFormat: '%Y-%m-%d'
            },
            title: {
              text: '平安银行历史股价'
            },
            xAxis: {
              dateTimeLabelFormats: {
                millisecond: '%H:%M:%S.%L',
                second: '%H:%M:%S',
                minute: '%H:%M',
                hour: '%H:%M',
                day: '%m-%d',
                week: '%m-%d',
                month: '%y-%m',
                year: '%Y'
              }
            },
            tooltip: {
              split: false,
              shared: true,
            },
            yAxis: [{
              labels: {
                align: 'right',
                x: -3
              },
              title: {
                text: '股价'
              },
              height: '65%',
              resize: {
                enabled: true
              },
              lineWidth: 2
            }, {
              labels: {
                align: 'right',
                x: -3
              },
              title: {
                text: '成交量'
              },
              top: '65%',
              height: '35%',
              offset: 0,
              lineWidth: 2
            }],
            series: [{
              type: 'candlestick',
              name: '平安银行',
              color: 'green',
              lineColor: 'green',
              upColor: 'red',
              upLineColor: 'red',
              tooltip: {
              },
              navigatorOptions: {
                color: Highcharts.getOptions().colors[0]
              },
              data: ohlc,
              dataGrouping: {
                units: groupingUnits
              },
              id: 'sz'
            }, {
              type: 'column',
              data: volume,
              yAxis: 1,
              dataGrouping: {
                units: groupingUnits
              }
            }]
          })
        })
    </script>
  </body>
</html>

highcharts官网

9、antv-g2

antv官网

AntV是蚂蚁金服全新一代数据可视化解决方案,致力于提供一套简单方便、专业可靠、拥有无限可能的数据可视化服务。

G2折线图案例

<!DOCTYPE html>
<html>
  <head>
      <meta charset="utf-8">
      <script src="https://unpkg.com/@antv/g2plot@latest/dist/g2plot.js"></script>
  </head>
  <body>
      <div id="g2-chart"></div>
      <script>
              const data = [
              { year: '1991', value: 3 },
              { year: '1992', value: 4 },
              { year: '1993', value: 3.5 },
              { year: '1994', value: 5 },
              { year: '1995', value: 4.9 },
              { year: '1996', value: 6 },
              { year: '1997', value: 7 },
              { year: '1998', value: 9 },
              { year: '1999', value: 13 },
              ];
             const chartDom = document.getElementById('g2-chart');
             const line = new G2Plot.Line(chartDom,{
                title:{
                  visible:true,
                  text:'g2折线图示例'
                },
                description:{
                   visible:true,
                   text:'折线图示例,这是一个副标题'
                },
                data,
                xField:'year',
                yField:'value',
                point:{
                    visible:true,
                    size:5,
                    color:'#fff',
                    style:{
                        stroke:'#fe740c',
                        lineWidth:2,
                        fillOpacity:0.6
                    }
                },
                 label:{
                    visible:true
                },
                color:'#f2740c'
             });
             line.render();
      </script>
  </body>
</html>

10、 antv-g6

<!DOCTYPE html>
<html>
  <head>
      <meta charset="utf-8">
      <script src="https://gw.alipayobjects.com/os/lib/antv/g6/3.4.7/dist/g6.min.js"></script>
  </head>
  <body>
      <div id="g6-chart"></div>
      <script>
          const data = {
            nodes:[{
              id:'node1',
              x:100,
              y:200,
              label:'起始点',
              size:60,
              labelCfg:{
                position:'top',
                style:{
                  fontSize:16,
                  file:'#f00'
                }
              },
              style:{
                fill:'#ff0000',
                stroke:'#888',
                lineWidth:10
              }
            },{
              id:'node2',
              x:300,
              y:200,
              label:'目标点1',
              size:80
            },{
              id:'node3',
              x:500,
              y:200,
              label:'目标点2',
              size:100
            }],
            edges:[{
              source:'node1',
              target:'node2',
              label:'连接线1'
            },{
              source:'node2',
              target:'node3',
              label:'连接线2'
            }]
          };
          const graph = new G6.Graph({
            container:'g6-chart',
            width:800,
            height:500
          })
          graph.data(data);
          graph.render();
      </script>
  </body>
</html>

11、antv-l7
全球电站分布案例:

<!DOCTYPE html>
<html>
  <head>
      <meta charset="utf-8">
      <script src="https://unpkg.com/@antv/l7"></script>
      <style>
        html,body{
          padding:0;
          margin:0;
          width:100%;
          height:100%;
        }
      </style>
  </head>
  <body>
      <div id="l7-map"></div>
      <script>
          const scene = new L7.Scene({
            id:'l7-map',
            map:new L7.GaodeMap({
              style:'dark',
              center:[120.19382669582967,30.258134],
              pitch:0,
              zoom:6,
              token:'e8653883477c1e38d190463340771816'
            })
          })
          scene.on('loaded',function(){
            fetch('https://gw.alipayobjects.com/os/basement_prod/337ddbb7-aa3f-4679-ab60-d64359241955.json').then(res=>res.json()).then(data=>{
              const pointLayer = new L7.PointLayer({})
              .source(data)
              .shape('circle')
              .size('capacity',[0,16])
              .color('capacity',['#34B6B7',
              '#4AC5AF',
              '#5FD3A6',
              '#7BE39E',
              '#A1EDB8',
              '#CEF8D6'])
              .active(true)
              .style({
                opacity:0.5,
                strokeWidth:0
              })
              .animate(false)
              ;
              scene.addLayer(pointLayer);
            })
          })
      </script>
  </body>
</html>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容