Vue+D3 绘制条形图和力导向图

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

Vue+D3 绘制条形图和力导向图


本篇blog在于把已经编写好的D3代码从一般HTML文件迁移到Vue框架中,关于D3的技术不予讨论


准备:D3代码

条形图

<html>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <head>
        <title>
            直方图
        </title>
    </head>
    <body>
        <h2 style="text-align: center;" >中国2022年各类商品进口金额占比</h2>
        <h3 style="text-align: center;">数据来源:国家统计局2020年对外贸易数据</h3>
        <script src="d3.min.js"></script>
        <script>
        var w=window.innerWidth
        ||document.documentElement.clientWidth
        ||document.body.clientWidth;    
        var h=window.innerHeight
        ||document.documentElement.clientHeight
        ||document.body.clientHeight;
        w=w*0.98;
        h=h*0.98;
            var svg=d3.select("body")
                      .append("svg")
                      .attr("width",w)
                      .attr("height",h);
            /*svg.append("rect")
               .attr("x",0)
               .attr("y",0)
               .attr("width",100)
               .attr("height",400)
            */
            w=w*0.8;
            h=h*0.8; 
            var size = 6;
            var dataset = new Array(size);
            var colors = new Array(size);
            for(var i=0;i<=size;i++){
                dataset[i] = Math.random()*h;
                colors[i] = "FF"+i%10+"00";
            }
            var population=[949052.98,682100.83,178452.85,203726,73186,239257];
            var rate = new Array(size);
            var sum = 0;
            for(var i = 0;i < size;i++){
                sum = sum + population[i];
            }
            for(var i=0; i < size;i++){
                rate[i] = (population[i]/sum)*100;
                console.log(rate[i]);
            }
            svg.append("line")
               .attr("x1",w*0.1-10)
               .attr("y1",h)
               .attr("x2",w*0.1-10)
               .attr("y2",0)
               .attr("stroke","black");
            svg.append("line")
               .attr("x1",w*0.1-10)
               .attr("y1",h)
               .attr("x2",w*1.17)
               .attr("y2",h)
               .attr("stroke","black");
            svg.append("text")
                .attr("x",w*0.1-10)
                .attr("y",h*0.02)
                .text("金额占比")
            svg.append("text")
                .attr("x",w*1.17)
                .attr("y",h)
                .text("种类")
            svg.selectAll("rect")
               .data(rate)
               .enter()
               .append("rect")
               .attr("x",function(d,i){
                   return w*0.1 + w/size*i;
               })
               .attr("y",function(d){
                   return h-d*10;
                })
               .attr("width",0.8*w/size)
               .attr("height",function(d){
                   return d*10;
               })
               .attr("fill",function(d,i){
                   return "#FF"+(i%10)+"000";
               });
               svg.selectAll(".dashline")
               .data(rate)
               .enter()
               .append("line")
               .attr("class","dashline")
               .attr("x1",w*0.1-10)
               .attr("x2",w*1.17)
               .attr("y1",function(d){
                   return h-d*10;
               })
               .attr("y2",function(d){
                   return h-d*10;
               })
               .attr("stroke","black")
               .attr("stroke-dasharray","5,5");
            
            svg.selectAll(".data")
                .data(rate)
                .enter()
                .append("text")
                .text(function(d){
                    console.log(Math.trunc(d*1000)/1000);
                    return Math.trunc(d*1000)/1000 + "%";
                })
                .attr("class","data")
                .attr("x",function(d,i){
                    return w*0.1 + w/size*i;
                })
                .attr("y",function(d){
                    return h-d*10;
                })
                .attr("fill","black");
            var regions=["机电产品","高新技术产品","原油","铁铜矿砂,精矿,铜材","汽车及其零件","其他"];
            svg.selectAll(".Mytext")
                .data(rate)
                .enter()
                .append("text")
                .attr("class","Mytext")
                .text(function(d,i){
                    return regions[i];
                })
                .attr("x",function(d,i){
                    return w*0.1 + w/size*i;
                })
                .attr("y",h+14)
                .attr("fill","black");
        </script>
    </body>


</html>

力导向图

<html>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <head>
        <title>
            力导向图
        </title>
    </head>
    <body bgcolor="#00a7d0">
        <a href="https://www.bilibili.com/video/BV1Xq4y1T7ne?spm_id_from=333.337.search-card.all.click" style="text-align: center;">https://www.bilibili.com/video/BV1Xq4y1T7ne?spm_id_from=333.337.search-card.all.click</a>
        <h1 style="text-align: center;">古典音乐体裁力导向图</h1>
        <script src="d3.min.js"></script>
        <script>
            var w=window.innerWidth
            ||document.documentElement.clientWidth
            ||document.body.clientWidth;    
            var h=window.innerHeight
            ||document.documentElement.clientHeight
            ||document.body.clientHeight;
            w=w*0.98;
            h=h*0.98;
            var svg=d3.select("body")
                      .append("svg")
                      .attr("width",w)
                      .attr("height",h);
            var color = d3.scale.category20();
            var force = d3.layout.force()
                          .charge(-600)
                          .linkDistance(200)
                          .size([w,h]);
            d3.json("node.json",function(error,graph){
                console.log(graph)
                force.nodes(graph.nodes)
                     .links(graph.links)
                     .start();
                var link = svg.selectAll()
                              .data(graph.links)
                              .enter()
                              .append("line")
                              .attr("class","link")
                              .style("stroke-width",4)
                              .style("stroke","pink");
                var node = svg.selectAll()
                              .data(graph.nodes)
                              .enter()
                              .append("circle")
                              .attr("class","node")
                              .attr("r",function(d){
                                  return 15;
                              })
                              .attr("cx",200)
                              .attr("cy",100)
                              .attr("fill",function(d,i){
                                    return color(i);
                              })
                              .on("dblclick",function(d){
                                    window.location.href = "https://baike.baidu.com/item/"+d.name;
                              })
                              .call(force.drag);
                
                var text = svg.selectAll(".forcetext")
                              .data(graph.nodes)
                              .enter()
                              .append("text")
                              .attr("class","forcetext")
                              .attr("x","100")
                              .attr("y","200")
                              .text(function(d){
                                  return d.name
                              })
                

                force.on("tick",function(){
                    node.attr("cx",function(d){
                        return d.x;
                    })
                        .attr("cy",function(d){
                            return d.y;
                        })
                    link.attr("x1",function(d){
                        return d.source.x;
                        })
                        .attr("y1",function(d){
                            return d.source.y; 
                        })
                        .attr("x2",function(d){
                            return d.target.x;
                        })
                        .attr("y2",function(d){
                            return d.target.y;
                        })
                    text.attr("x",function(d){
                            return d.x;
                        })
                        .attr("y",function(d){
                            return d.y
                        });
                })
            })
        </script>
    </body>
    
</html>

力导向图所需要的json文件node.json

{"nodes":
	[
		{"name":"音乐体裁","group":0},
		{"name":"人声","group":1},
		{"name":"器乐","group":2},
		{"name":"歌剧","group":3},
		{"name":"艺术歌曲","group":4},
		{"name":"费加罗的婚礼","group":5},
		{"name":"尼伯龙根的指环","group":6},
		{"name":"魔王","group":7},
		{"name":"交响乐","group":8},
		{"name":"室内乐","group":9},
		{"name":"交响曲","group":10},
		{"name":"协奏曲","group":11},
		{"name":"乐队组曲","group":12},
		{"name":"序曲","group":13},
		{"name":"交响诗","group":14},
		{"name":"四乐章","group":15},
		{"name":"三乐章","group":16},
		{"name":"单乐章","group":17},
		{"name":"华彩","group":18},
		{"name":"独奏","group":19},
		{"name":"重奏","group":20},
		{"name":"三重奏","group":21},
		{"name":"四重奏","group":22},
		{"name":"钢琴","group":23},
		{"name":"小提琴","group":24},
		{"name":"大提琴","group":25}
	],
"links":
	[
		{"source":0,"target":1,"value":1},
		{"source":0,"target":2,"value":1},
		{"source":1,"target":3,"value":1},
		{"source":1,"target":4,"value":1},
		{"source":3,"target":5,"value":1},
		{"source":3,"target":6,"value":1},
		{"source":4,"target":7,"value":1},
		{"source":2,"target":8,"value":1},
		{"source":2,"target":9,"value":1},
		{"source":8,"target":10,"value":1},
		{"source":8,"target":11,"value":1},
		{"source":8,"target":12,"value":1},
		{"source":8,"target":13,"value":1},
		{"source":8,"target":14,"value":1},
		{"source":10,"target":15,"value":1},
		{"source":11,"target":16,"value":1},
		{"source":13,"target":17,"value":1},
		{"source":14,"target":17,"value":1},
		{"source":11,"target":18,"value":1},
		{"source":11,"target":18,"value":1},
		{"source":9,"target":19,"value":1},
		{"source":9,"target":20,"value":1},
		{"source":20,"target":21,"value":1},
		{"source":20,"target":22,"value":1},
		{"source":21,"target":23,"value":1},
		{"source":21,"target":24,"value":1},
		{"source":21,"target":25,"value":1},
		{"source":22,"target":24,"value":1},
		{"source":22,"target":25,"value":1}
	]
}

在已经创建的vue项目框架中
注意:
1-运行安装依赖包:npm install
2-如果是从标准的空工程文件开始,需要安装d3和axios
npm install d3
npm install axios
3-运行程序:npm run dev

在main.js中`

import { createApp } from 'vue'
import App from './App.vue'
import './index.css'

createApp(App).mount('#app')

定义CSS文件index.css

#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

1.条形图

在APP.vue中定义模板和组件名称及其文件路劲

<template>
  <BarChart />
</template>

<script>
import BarChart from './components/BarChart.vue';
export default {
  name: 'App',
  components: {
    BarChart
  }
}
</script>

定义组件BarChart.vue

<template>
    <h2><a href="https://d3js.org" target="_blank" >D3直方图@VUE3</a></h2>
    <div id="bar-chart-container"></div>
</template>

<script>
import { defineComponent } from 'vue';
//import axios from "axios";
import * as d3 from "d3";

var data=new Array(10);
for (var i=0;i<10;i++)
{
    data[i]=Math.floor(Math.random()*255);           
    console.log(data[i]);
}
var color=d3.schemeCategory10;

export default defineComponent({
    mounted() {      
        this.drawBarChart(data);      
    },
    methods:{
        drawBarChart(data){
        var w=window.innerWidth
        ||document.documentElement.clientWidth
        ||document.body.clientWidth;    
        var h=window.innerHeight
        ||document.documentElement.clientHeight
        ||document.body.clientHeight;
        w=w*0.98;
        h=h*0.98;
            var svg=d3.select("body")
                      .append("svg")
                      .attr("width",w)
                      .attr("height",h);
            /*svg.append("rect")
               .attr("x",0)
               .attr("y",0)
               .attr("width",100)
               .attr("height",400)
            */
            w=w*0.8;
            h=h*0.8; 
            var size = 6;
            var dataset = new Array(size);
            var colors = new Array(size);
            for(var i=0;i<=size;i++){
                dataset[i] = Math.random()*h;
                colors[i] = "FF"+i%10+"00";
            }
            var population=[949052.98,682100.83,178452.85,203726,73186,239257];
            var rate = new Array(size);
            var sum = 0;
            for(var i = 0;i < size;i++){
                sum = sum + population[i];
            }
            for(var i=0; i < size;i++){
                rate[i] = (population[i]/sum)*100;
                console.log(rate[i]);
            }
            svg.append("line")
               .attr("x1",w*0.1-10)
               .attr("y1",h)
               .attr("x2",w*0.1-10)
               .attr("y2",0)
               .attr("stroke","black");
            svg.append("line")
               .attr("x1",w*0.1-10)
               .attr("y1",h)
               .attr("x2",w*1.17)
               .attr("y2",h)
               .attr("stroke","black");
            svg.append("text")
                .attr("x",w*0.1-10)
                .attr("y",h*0.02)
                .text("金额占比")
            svg.append("text")
                .attr("x",w*1.17)
                .attr("y",h)
                .text("种类")
            svg.selectAll("rect")
               .data(rate)
               .enter()
               .append("rect")
               .attr("x",function(d,i){
                   return w*0.1 + w/size*i;
               })
               .attr("y",function(d){
                   return h-d*10;
                })
               .attr("width",0.8*w/size)
               .attr("height",function(d){
                   return d*10;
               })
               .attr("fill",function(d,i){
                   return "#FF"+(i%10)+"000";
               });
               svg.selectAll(".dashline")
               .data(rate)
               .enter()
               .append("line")
               .attr("class","dashline")
               .attr("x1",w*0.1-10)
               .attr("x2",w*1.17)
               .attr("y1",function(d){
                   return h-d*10;
               })
               .attr("y2",function(d){
                   return h-d*10;
               })
               .attr("stroke","black")
               .attr("stroke-dasharray","5,5");
            
            svg.selectAll(".data")
                .data(rate)
                .enter()
                .append("text")
                .text(function(d){
                    console.log(Math.trunc(d*1000)/1000);
                    return Math.trunc(d*1000)/1000 + "%";
                })
                .attr("class","data")
                .attr("x",function(d,i){
                    return w*0.1 + w/size*i;
                })
                .attr("y",function(d){
                    return h-d*10;
                })
                .attr("fill","black");
            var regions=["机电产品","高新技术产品","原油","铁铜矿砂,精矿,铜材","汽车及其零件","其他"];
            svg.selectAll(".Mytext")
                .data(rate)
                .enter()
                .append("text")
                .attr("class","Mytext")
                .text(function(d,i){
                    return regions[i];
                })
                .attr("x",function(d,i){
                    return w*0.1 + w/size*i;
                })
                .attr("y",h+14)
                .attr("fill","black");
        }
    }
})
</script>

在此注意在mouted定义drawBarChart方法,在method中定义这个方法,此时只用将对应的HTML文件的js部分代码复制到所定义的方法中(在这里的data负责传参,也可以不传参,在方法中直接定义数据,使用视情况而定)

2.力导向图

将上述项目文件夹进行复制,得到一个项目模板,同时注意将上文提到的node.json文件移到项目的根目录下
此时APP.vue为

<template>
  <HelloD3 msg="D3.JS@Vue3"/>
</template>

<script>
import HelloD3 from './components/D3Hist.vue';

export default {
  name: 'App',
  components: {
    	HelloD3
  }
}
</script>

定义组件D3Hist.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
  </div>
<head>
        <title>
            力导向图
        </title>
    </head>
        <a href="https://www.bilibili.com/video/BV1Xq4y1T7ne?spm_id_from=333.337.search-card.all.click" style="text-align: center;">https://www.bilibili.com/video/BV1Xq4y1T7ne?spm_id_from=333.337.search-card.all.click</a>
        <h1 style="text-align: center;">古典音乐体裁力导向图</h1>
</template>

<script>
    import axios from "axios";
    import { defineComponent } from 'vue';
    //import axios from "axios";
    import * as d3 from "d3";
    var data;
    export default defineComponent({
        //挂载后执行
        mounted(){
            axios.get("./node.json").then(res => {
                console.log(res.data);
                this.drawBarChart(res.data);
            });
        },
        methods:{
          drawBarChart(data){
            var w=window.innerWidth
            ||document.documentElement.clientWidth
            ||document.body.clientWidth;    
            var h=window.innerHeight
            ||document.documentElement.clientHeight
            ||document.body.clientHeight;
            w=w*0.98;
            h=h*0.98;
            var svg=d3.select("body")
                      .append("svg")
                      .attr("width",w)
                      .attr("height",h);
            var color = d3.schemeCategory10;
            var forceSimulation = d3.forceSimulation()
                          .force("link",d3.forceLink())
		      .force("charge",d3.forceManyBody().strength(-100))
                          .force("center",d3.forceCenter(w/2,h/2));
	  forceSimulation.nodes(data.nodes)
			         .on("tick");
	  forceSimulation.force("link")
			         .links(data.links)
			         .distance(100);
	  function drag(){
		function dragstarted(event,d){
			if(!event.active) forceSimulation.alphaTarget(0.3).restart();
			d.fx = d.x;
			d.fy = d.y;
	}
		function dragged(event,d){
			d.fx = event.x;
			d.fy = event.y;
	}
		function dragended(event,d){
			if(!event.active) forceSimulation.alphaTarget(0);
			d.fx = null;
			d.fy = null;
	}
		return d3.drag()
			     .on("start",dragstarted)
			     .on("drag",dragged)
			     .on("end",dragended);
	} 
            var link = svg.selectAll()
                              .data(data.links)
                              .enter()
                              .append("line")
                              .attr("class","link")
                              .style("stroke-width",4)
                              .style("stroke","pink");
                var node = svg.selectAll()
                              .data(data.nodes)
                              .enter()
                              .append("circle")
                              .attr("class","node")
                              .attr("r",function(d){
                                  return 15;
                              })
                              .attr("cx",200)
                              .attr("cy",100)
                              .attr("fill",function(d,i){
                                    return color[i%10];
                              })
                              .on("dblclick",function(d){
                                    window.location.href = "https://baike.baidu.com/item/"+d.name;
                              })
                              .call(drag());
                
                var text = svg.selectAll(".forcetext")
                              .data(data.nodes)
                              .enter()
                              .append("text")
                              .attr("class","forcetext")
                              .attr("x","100")
                              .attr("y","200")
                              .text(function(d){
                                  return d.name
                              })
                forceSimulation.on("tick",function(){
                    node.attr("cx",function(d){
                        return d.x;
                    })
                        .attr("cy",function(d){
                            return d.y;
                        })
                    link.attr("x1",function(d){
                        return d.source.x;
                        })
                        .attr("y1",function(d){
                            return d.source.y; 
                        })
                        .attr("x2",function(d){
                            return d.target.x;
                        })
                        .attr("y2",function(d){
                            return d.target.y;
                        });
                    text.attr("x",function(d){
                            return d.x;
                        })
                        .attr("y",function(d){
                            return d.y
                        });
                })
          }
        }
    })
</script>



<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

这里需要注意这里通过axios发出获取json文件的请求,将json数据作为数据传入到所定义的方法,同时将d3的代码移入其中(注意源代码是d3的v3版本vue中使用的是v7版本,已经经过了改造,所以如果直接从之前的代码复制会报错,而我所展示的vue的代码可以直接使用),这样就完成对d3代码的复制迁移

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue.js是一个基于JavaScript的开源框架,用于构建用户界面。而D3.js是一个用于在网页上创建可交互数据可视化的JavaScript库。结合Vue.js和D3.js可以很方便地绘制流程。 首先,我们需要在Vue.js项目中安装D3.js,可以通过npm安装。在终端中运行以下命令: npm install d3 安装完成后,在Vue.js组件中引入D3.js库: import * as d3 from 'd3'; 接下来,我们可以在Vue.js组件的生命周期函数中创建一个D3.js绘制流程的函数。比如,在mounted钩子函数中: mounted() { this.drawFlowChart(); } 然后,在drawFlowChart函数中,我们可以使用D3.js提供的方法和API来绘制流程。这个过程可以分为以下几步: 1. 创建svg元素,用于承载流程绘制: const svg = d3.select('body') .append('svg') .attr('width', '100%') .attr('height', '100%'); 2. 定义绘制流程所需的数据,比如节点和连接线的位置和样式信息: const nodes = [ { id: 1, name: 'Node 1' }, { id: 2, name: 'Node 2' }, { id: 3, name: 'Node 3' }, // ... ]; const links = [ { source: 1, target: 2 }, { source: 2, target: 3 }, // ... ]; 3. 创建节点和连接线的元素,并设置其位置和样式: const node = svg.selectAll('.node') .data(nodes) .enter() .append('g') .attr('class', 'node') .attr('transform', d => `translate(${d.x}, ${d.y})`); node.append('rect') .attr('width', 100) .attr('height', 50); const link = svg.selectAll('.link') .data(links) .enter() .append('line') .attr('class', 'link') .attr('x1', d => d.source.x) .attr('y1', d => d.source.y) .attr('x2', d => d.target.x) .attr('y2', d => d.target.y); 4. 可以根据需要添加交互功能,比如节点的点击事件、鼠标悬停效果等等。 至此,我们使用Vue.js和D3.js成功绘制了一个流程。通过Vue.js的数据绑定和D3.js的数据驱动视的特性,可以很方便地更新流程的数据和样式,实现交互和动态效果。同时,D3.js也提供了丰富的API可以用于进行复杂的数据可视化操作,比如节点之间的动画过渡效果、缩放和拖拽等等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值