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>