Bootstrap

Vue 力导图d3js 实现

PowerBI 自定义组件推荐用D3JS 实现,实现一个PowerBI 的力导图组件,为调试方便,先用Vue 实现一个,然后再移植到PowerBI 中,话不多说,上效果:
在这里插入图片描述
体验入口

上代码是最好的老师:

<template>
<div>
    <div id="force-container"></div>
</div>

</template>

<script>
import { defineComponent } from 'vue';
import * as d3 from "d3";
var color=d3.schemeCategory10;

var nodes = [
				{"name":"爱情公寓"},
				{"name":"曾小贤"},
				{"name":"胡一菲"},
				{"name":"吕子乔"},
				{"name":"陈美嘉"},
				{"name":"关谷神奇"},
				{"name":"唐悠悠"},
				{"name":"陆展博"},
				{"name":"林宛瑜"},
				{"name":"张伟"},
				{"name":"诸葛大力"},
				{"name":"秦羽墨"},
				{"name":"诺澜"},
				{"name":"Lisa榕"},
				{"name":"杜俊"},
				{"name":"赵海棠"},
				{"name":"咖喱酱"}
			];
var links = [
				{"source":1,"target":0,"relation":"租户"},
				{"source":2,"target":0,"relation":"租户"},
				{"source":3,"target":0,"relation":"租户"},
				{"source":4,"target":0,"relation":"租户"},
				{"source":5,"target":0,"relation":"租户"},
				{"source":6,"target":0,"relation":"租户"},
				{"source":7,"target":0,"relation":"租户"},
				{"source":8,"target":0,"relation":"租户"},
				{"source":9,"target":0,"relation":"租户"},
				{"source":10,"target":0,"relation":"租户"},
				{"source":11,"target":0,"relation":"租户"},
				{"source":15,"target":0,"relation":"租户"},
				{"source":16,"target":0,"relation":"租户"},
				{"source":1,"target":2,"relation":"夫妻"},
				{"source":1,"target":13,"relation":"上下级"},
				{"source":1,"target":12,"relation":"同事&喜欢"},
				{"source":2,"target":7,"relation":"姐弟"},
				{"source":2,"target":11,"relation":"同学"},
				{"source":2,"target":12,"relation":"情敌"},
				{"source":3,"target":4,"relation":"夫妻"},
				{"source":3,"target":6,"relation":"小姨妈/大外甥"},
				{"source":3,"target":13,"relation":"暗恋"},
				{"source":4,"target":6,"relation":"闺蜜"},
				{"source":5,"target":6,"relation":"夫妻"},
				{"source":5,"target":14,"relation":"师兄弟"},
				{"source":7,"target":8,"relation":"情侣"},
				{"source":9,"target":10,"relation":"情侣"},
				{"source":9,"target":15,"relation":"情敌"},
				{"source":9,"target":16,"relation":"助理"},
				{"source":10,"target":15,"relation":"同学"},
				{"source":10,"target":2,"relation":"师生"},
				{"source":15,"target":10,"relation":"追求"},
				{"source":15,"target":2,"relation":"师生"},
				{"source":13,"target":12,"relation":"同学"}
			];

export default defineComponent({
    mounted() {      
        this.drawBarChart(nodes,links);      
    },
    methods:{
        drawBarChart(nodes,links){
            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.9;
            var svg=d3.select("#force-container")
                        .append("svg")
                        .attr("width",w)
                        .attr("height",h*1.1);
            var forceSimulation = d3.forceSimulation()
                                    .force("link",d3.forceLink())
                                    .force("charge",d3.forceManyBody().strength(-600))
                                    //.force("collide", d3.forceCollide(100).strength(0.2).iterations(5))
                                    .force("center",d3.forceCenter(w/2,h/2));
            forceSimulation.nodes(nodes)
                           .on("tick");
            forceSimulation.force("link")
                           .links(links)
                           .distance(200);
            var link=svg.selectAll(".link")
                        .data(links)
                        .enter()
                        .append("line")
                        .attr("class","link")
                        .style("stroke-width",2)
                        .style("stroke",(d,i)=>color[i%10])
					    .style("opacity",0.6);
            var node=svg.selectAll(".node")
                        .data(nodes)
                        .enter()
                        .append("circle")
                        .attr("r",20)
                        .style("fill",(d,i)=>color[i%10])
                        .call(drag()); 
            forceSimulation.on("tick",()=>{
                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);
                node.attr("cx",d=>d.x)
                    .attr("cy",d=>d.y);
                edges_text.attr("x",d=>(d.source.x + d.target.x) / 2 )
					.attr("y",d=>(d.source.y + d.target.y) / 2 );
                texts.attr("x",d=>d.x)
					 .attr("y",d=>d.y);
            });
            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 edges_text = svg.selectAll(".linetext")
							.data(links)
							.enter()
							.append("text")
							.attr("class","linetext")
							.text(d=> d.relation)
							.style("stroke",(d,i)=>color[i%10])
							.style("font-size",14);	                
            var texts=svg.selectAll(".forceText")
			 .data(nodes)
			 .enter()
			 .append("text")
			 .attr("class","forceText")
			 .style("stroke",(d,i)=>color[i%10])
			 .style("font-size","12px")
			 .attr("text-anchor","middle")
			 .attr("dy",30)
			 .text(d=>d.name);
            
        }
    }
})
</script>


;