GTD数据分析及可视化项目的第一张图表,项目总体介绍见这篇文章。

最终效果

实现

数据集


目标是做出世界、地区、国家三级所有选项的图表。原数据集中有地区(region)和国家(country)的编码,增加编码“1”,代表全世界。在数据表中使用这一编码,按年份统计袭击次数,死亡人数,受伤人数。在项目中用json文件记录层级关系及编码和文本的对应关系。

二级联动菜单

这个不细说了,网上一查教程很多。思路是通过v-model分别将一级、二级select框绑定到form.type和form.detailedType数据上。一级select框的@change事件绑定到一个函数getType上,函数根据一级select框的值判定类型,然后更新detailedType内容,v-model自动实现二级select框的视图更新。二级select框绑定到函数onGenerate上,根据选择的值(即世界、地区或国家的编码)+指标值(袭击次数,死亡或受伤人数)生成折线图。另外,指标值的@change也要绑定到onGenerate函数。

html部分:

     <div id="selectSection"><select name="type" id="type" @change="getType($event)" v-model="form.type" ><option v-for="(item,index) in types" :key="index" :value="item.value" :label="item.name"></option></select><select name="detailedType" id="detailedType" @change="onGenerate()" v-model="form.detailedType" ><option v-for="(item,index) in detailedTypes" :key="index" :value="item.value" :label="item.name"></option></select><select name="category" id="category" @change="onGenerate()" ><option value="attacks">攻击次数</option><option value="killed">死亡人数</option><option value="wounded">受伤人数</option></select></div>

js部分:

     // 声明及初始化data() {return {// type代表一级地区选择,detailedType代表二级,category代表指标types: '',detailedTypes: '',form: {type: '',detailedType: '',category: ''}};},created: function() {this.types = typeSelect.bodythis.form.type = this.types[0].valuethis.detailedTypes = this.types[0].childrenthis.form.detailedType = this.detailedTypes[0].value},
// 省略...// 更新二级菜单getType: function(event) {let type = event.target.valueif (type == 'world') {this.detailedTypes = this.types[0].children} else if (type == 'region') {this.detailedTypes = this.types[1].children} else if (type == 'country') {this.detailedTypes = this.types[2].children}},// 生成图表,type为世界、地区或国家的编码,category为袭击次数,死亡,受伤人数指标onGenerate: function() {let type = d3.select(this.$el).select('#detailedType').node().valuecategory = d3.select(this.$el).select('#category').node().valuethis.update(procData(type, category))}

数据处理

拿到种类编码type和指标值category就可以从数据集中筛选出需要的子集了。完整数据集保存在data数组中,需要做的就是筛选出data中type值与我们的参数type相等的行,再从列中选出年份和我们的参数category。实现如下:

 function procData(type, category) {var filteredData = data.filter(d => d.type == type)result = filteredData.map(d => ({year: d.year,value: d. [category] // 中括号代表使用category变量}))return result}

折线图绘制

终于到重点了。首先,在html中留一个供注入的div。

     <div id="line-chart-graph"></div>

在初始化函数中,用d3.select找到该节点,添加一个svg节点作为图形根节点,通过attr为该节点添加属性,再添加一个g节点。

// 变量已在前面声明
// ...// 该函数需要用户负责在网页加载时调用init: function() {data = this.getLineChartData() // 获取数据到datamargin = {top: 100,right: 150,bottom: 30,left: 50},width = totalWidth - margin.left - margin.right,height = totalHeight - margin.top - margin.bottom;svg = d3.select("#line-chart-graph").append("svg").attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom).append("g").attr("transform","translate(" + margin.left + "," + margin.top + ")");// ...

然后画坐标轴。

             x = d3.scaleLinear().range([0, width]);xAxis = d3.axisBottom().scale(x).ticks((2018 - 1970) / 5).tickFormat(d3.format("d"));svg.append("g").attr("transform", "translate(0," + height + ")").attr("class", "myXaxis")y = d3.scaleLinear().range([height, 0]);yAxis = d3.axisLeft().scale(y);svg.append("g").attr("class", "myYaxis")

画(更新)折线在update函数完成。初始状态,统计类型为世界(编码为1),指标为袭击次数。

             this.update(procData(1, 'attacks'))

重点来了,update函数的写法。 x轴域固定为1970-2018,y轴域为0到data中最大值。坐标轴的动态更新:

         update: function(data) {x.domain([1970, 2018]);svg.selectAll(".myXaxis").transition().duration(2000).call(xAxis);y.domain([0, d3.max(data, d => d.value)]);svg.selectAll(".myYaxis").transition().duration(2000).call(yAxis);// ...

折线的更新,可以感受到d3数据绑定的思路和简便:

             var u = svg.selectAll(".lineTest").data([data], d => d.year);u.enter().append("path").attr("class", "lineTest").merge(u).transition().duration(2000).attr("d", d3.line().x(d => x(d.year)).y(d => y(d.value))).attr("fill", "none").attr("stroke", "url(#line-gradient)").attr("stroke-width", 3.5)

折线根据y值进行梯度染色:

             // 注意这段放在上面那段之前const max = d3.max(data, d => +d.value);var color = d3.scaleSequential(y.domain(), d3.interpolateTurbo)svg.append("linearGradient").attr("id", "line-gradient").attr("gradientUnits", "userSpaceOnUse").attr("x1", 0).attr("y1", y(0)).attr("x2", 0).attr("y2", y(max)).selectAll("stop").data(d3.ticks(0, 1, 10)).join("stop").attr("offset", d => d).attr("stop-color", color.interpolator());

十字线绘制

鼠标悬浮显示数据的效果会明显增强图表的可交互性。下面来实现这一效果。为了捕获图表区域内的鼠标事件,我们在图表上覆盖一个同样大小的rect,并将鼠标事件绑定到自定义的函数。在init函数中:

             svg.append('rect').style("fill", "none").style("pointer-events", "all").attr('width', width).attr('height', height).on('mouseover', mouseover).on('mousemove', mousemove).on('mouseout', mouseout);

完成这三个函数之前,先来想想我们要画哪些东西:一个距离鼠标位置最近的数据点(圆),聚焦在数据点的一条横线+一条竖线,年份文本+指标文本。把它们的定义和属性填好:

             focus = svg.append('g').append('circle').attr("stroke", "black").attr("fill", "black").attr('r', 4).style("opacity", 0)line1 = svg.append('line').attr("stroke", "black").attr("stroke-width", 1).style("opacity", 0)line2 = svg.append('line').attr("stroke", "black").attr("stroke-width", 1).style("opacity", 0)focusText = svg.append('g').append('text')text1 = focusText.append('tspan').attr('id', 't1').style("opacity", 0).attr("text-anchor", "left").attr("alignment-baseline", "middle")text2 = focusText.append('tspan').attr('id', 't2').style("opacity", 0).attr("text-anchor", "left").attr("alignment-baseline", "middle")

那么mouseover和mouseout函数就很好写了,就是让这些元素出现和隐藏。

             let mouseover = function() {focus.style("opacity", 0.8)line1.style("opacity", 0.8)line2.style("opacity", 0.8)focusText.style("opacity", 0.9)text1.style("opacity", 0.9)text2.style("opacity", 0.9)}let mouseout = function() {focus.style("opacity", 0)line1.style("opacity", 0)line2.style("opacity", 0)focusText.style("opacity", 0)}

mousemove函数中,用bisect找到鼠标悬浮位置最近的数据点。有了数据点后,就可以将其映射到x,y坐标,将点、线和文字画在正确的位置,同时文本显示数据点的值。

             let mousemove = function(e) {var x0 = x.invert(d3.pointer(e)[0]);var i = bisect(result, x0, 1); // bisect定义:var bisect = d3.bisector(d => d.year).left;let selectedData = result[i]focus.attr("cx", x(selectedData.year)).attr("cy", y(selectedData.value))line1.attr("x1", 0).attr("x2", svgrect.width).attr("y1", y(selectedData.value)).attr("y2", y(selectedData.value))line2.attr("x1", x(selectedData.year)).attr("x2", x(selectedData.year)).attr("y1", 0).attr("y2", svgrect.height)focusText.attr("cx", x(selectedData.year) + 20).attr("cy", y(selectedData.value) - 50)text1.text('年份: ' + selectedData.year).attr("x", x(selectedData.year) + 20).attr("dy", y(selectedData.value) - 50)text2.text(categoryMap(category) + ': ' + selectedData.value).attr("x", x(selectedData.year) + 15).attr("dy", "1.5em")}

源码

见项目总体介绍底部项目链接。本图源码为src/components/LineChart.vue文件。

Vue + D3 动态可视化图实现之一:折线图相关推荐

  1. 用户数据销售额分析动态大屏看板+大屏数据可视化图表组件(折线图+圆柱图+散点图+饼图+漏斗图+雷达图+水位图)+智能web端高保真大数据动态可视化大屏看板+中国动态地图+智慧电商实时动态数据大屏看板

    作品内容:用户数据销售额分析动态大屏看板+大屏数据可视化图表组件(折线图+圆柱图+散点图+饼图+漏斗图+雷达图+水位图)+web端高保真大数据动态可视化大屏看板+中国动态地图+电商实时动态数据大屏看板 ...

  2. Python使用matplotlib函数subplot可视化多个不同颜色的折线图、在折线图上为每个数据点添加日期数据标签

    Python使用matplotlib函数subplot可视化多个不同颜色的折线图.在折线图上为每个数据点添加日期数据标签 目录

  3. Python使用matplotlib函数subplot可视化多个不同颜色的折线图、在折线图上为每个数据点添加数值标签

    Python使用matplotlib函数subplot可视化多个不同颜色的折线图.在折线图上为每个数据点添加数值标签 目录

  4. highcharts 动态生成x轴和折线图

    highchart 动态生成x轴和折线图 <!DOCTYPE HTML> <html><head><meta charset="utf-8" ...

  5. ECharts动态加载数据绘制折线图

    Echarts动态加载数据绘制折线图 ECharts 引入ECharts 步骤 连接数据接口,动态加载图表 动态加载数据,整体代码 折线图绘制 总结 绘制多个图表的方法 ECharts 纯Javasc ...

  6. 数据可视化图表之面积折线图

    相信不少制作报表大屏的人都会遇到一个问题:我要制作每个月收入和支出趋势的报表,是要用折线图还是面积图?到底用什么图表来展示比较好?其实每个图表都有自己的特点,都有适合展示的数据类型,只要你熟悉了解每个 ...

  7. Echarts动态加载多条折线图

    背景:动态加载多条折线图,折线图条数不确定 页面效果: 页面代码 //气象数据function serchQx(beginTime, endTime, str, parameter) {$(" ...

  8. vue - vue中使用echart实现柱状图和折线图

    vue中使用echart实现柱状图和折线图,所用到的数据会放到最后面,在costRate.js里面: 1,先看效果图 一些重要注释我都写到代码里面了:第一个图柱状图,第二个是折线图 2,代码实现 &l ...

  9. python大量数据折线图-Python数据可视化练习:各种折线图的用法

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 以下文章来源于python数据分析之禅 ,作者鸟哥 折线图是排列在工作表的列或行 ...

最新文章

  1. 【网络基础】02、IP地址
  2. yolov3算法优点缺点_优点缺点
  3. [react] React15和16别支持IE几以上?
  4. NumPy 简介及安装
  5. 数据库中int类型存在空数据开发过程中model和dal层处理方法
  6. iozone联机测试
  7. java矩阵连乘动态规划_动态规划之矩阵连乘
  8. python遍历列表中所有元素_python如何遍历列表所有元素?
  9. IT公司聘用应届生的标准是什么?到底看中应届生的什么?
  10. 【渝粤教育】电大中专计算机网络基础_1作业 题库
  11. word文档生成系列:doc和docx插入多图
  12. Touch panel DTS 分析(MSM8994平台,Atmel 芯片)
  13. 印章、拿金币、数字游戏
  14. C语言之贪吃蛇游戏源码
  15. 在b站上跟着沐神学习深度学习
  16. 本人从事Java十余年~是时候收徒弟~包教包会~深藏功与名~
  17. 渠道二维码服务号实现渠道二维码的关注与统计怎么弄?
  18. 数据仓库建模(三):事实表的设计
  19. Maple中使用注意事项
  20. python自动切换链接_python+selenium自动化(四)之selenium切换窗口

热门文章

  1. 智慧景区“数字孪生“三维可视化运营管理平台-景区“元宇宙”的数字
  2. 天池比赛-金融风控贷款违约预测
  3. 数据分析应用在传统运营后变身数据化运营
  4. 人工智能芯片龙头之一gti概念股_AI芯片相关股票有哪些?AI芯片概念股票龙头一览...
  5. 怎样才能使你的Mac桌面干净整洁?
  6. 计算机网络设备接地规范,网络机房防雷接地的四种方式及静电要求
  7. 单身女生看过来:你为什么没有男朋友的20个原因
  8. java中任何变量都可以被赋值为null,关于异常处理:为什么“throw null”没有在Java中创建编译错误?...
  9. 计算机数学与高中数学衔接,高中数学的断层与衔接研究论文
  10. 武汉市企业研究开发中心备案