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>