代码:
<template>
<h2><a href="https://d3js.org" target="_blank" >巴黎圣母院人物关系图</a></h2>
<div id="force-container"></div>
</template>
<script>
import { defineComponent } from 'vue';
//import axios from "axios";
import * as d3 from "d3";
var color=d3.schemeCategory10;
var nodes=[{name:"埃斯梅拉达",intro:"埃斯梅拉达是雨果笔下集真、善、美于一体的完美的艺术形象。她在小时候被吉普赛人从妓女母亲的呵护下偷走,流浪街头以卖艺为生,虽然饱尝人世的艰辛与苦难,但是却始终保持着一颗善良纯真、乐于助人的心。她是美丽善良的,当乞丐国王要绞死甘果瓦时,她承诺要与甘果瓦结婚救下了他的命;当卡西莫多接受刑法口渴难耐时,只有她站出来以德报怨为他送水。她是勇敢执着的,当克洛德威胁她,只要接受他的爱就能够获得自由时,她斩钉截铁地拒绝了;当菲比斯不顾她的安危死活时,却依然痴心执着的爱着他。"},
{name:"卡西莫多",intro:"卡西莫多是一个有着几何形的脸,四面体的鼻子,马蹄形的嘴,参差不齐的牙齿,独眼,耳聋,驼背,难听而忠厚的声音的畸形儿。他幼时被父母遗弃在巴黎圣母院门口,后被克洛德收养,成为敲钟人。为报恩,他对克洛德言听计从,绑架了埃斯梅拉达。但在遇到埃斯梅拉达之后,他真诚善良、忠实勇敢的本性被复活了。他奋不顾身地去救处于危险中的埃斯梅拉达,并且不图任何回报。"},
{name:"克洛德",intro:"克洛德从小接受了良好的宗教教育,是一个勤奋好学、积极向上、知识渊博的青年,他收养丑陋的卡西莫多、照顾年少的弟弟,是有些许善良的。在成为巴黎圣母院的副主教遇到美丽的埃斯梅拉达后,克洛德真实的人性开始表现出来,内心强烈的占有欲迫使他去跟踪、绑架、强抢埃斯梅拉达,这并不是真正意义上的爱情,只是人性中的贪婪和欲望而已。他对埃斯梅拉达的占有欲已经超越了教会思想的束缚,不顾任何人的想法采用极端的做法只能造成悲剧的结局。"},
{name:"甘果瓦",intro:"流浪诗人,曾因误闯乞丐王国要被绞死,行刑前被埃斯梅拉达以名义婚姻的形式救下。长时间的流浪使他更清楚地认识这个时代和社会,在爱情和物质面前更倾向于选择实际的物质。"},
{name:"菲比斯",intro:"菲比斯表面上是一个英俊潇洒、美好的皇家卫队队长,深受女孩子的欢迎和喜爱,但是内在却是一个口蜜腹剑、风流成性的伪君子。一方面,菲比斯并不爱他的表妹,却因为表妹的丰厚嫁妆和贵族地位,而同意和表妹结婚,这是一个将金钱、地位摆在第一位的人;另一方面,他虚伪地爱着埃斯梅拉达,因其美丽的外表才一时兴起去追求。单纯的埃斯梅拉达却死心塌地爱上了这个男子,当克洛德因嫉妒袭击菲比斯时,他侥幸逃脱一劫,埃斯梅拉达却冤死在了绞刑架上。"},
{name:"若望",intro:"巴黎圣母院副主教克洛德之弟,一个游戏人生的小混混,后不满哥哥的所作所为,加入“圣母院”暴动队伍,死于伽西莫多之手。"},
{name:"小百合",intro:"菲比斯的原未婚妻,最终和甘果瓦成婚。"},
{name:"居第尔",intro:"埃斯梅拉达的母亲。她在监狱里认出了自己的女儿,在得知分散15年后刚刚找回的骨肉马上就要被吊死了后发狂一样地保护女儿,最后死在女儿的绞架下。"}];
var links=[{source:0,target:1,relation:"爱慕"},
{source:0,target:2,relation:"由爱生恨"},
{source:0,target:3,relation:"名义夫妻"},
{source:0,target:4,relation:"爱慕"},
{source:1,target:2,relation:"养父子"},
{source:1,target:4,relation:"被逮捕&逮捕"},
{source:2,target:3,relation:"师生"},
{source:2,target:4,relation:"情敌"},
{source:2,target:5,relation:"兄弟"},
{source:4,target:6,relation:"订婚"},
{source:0,target:7,relation:"母女"},
{source:2,target:7,relation:"上下级"},
{source:3,target:6,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.98;
const width=800;
const height=400;
var svg=d3.select("#force-container")
.append("svg")
.attr("width",w)
.attr("height",h*0.98);
var forceSimulation = d3.forceSimulation()
.force("link",d3.forceLink())
.force("charge",d3.forceManyBody().strength(-800))
.force("center",d3.forceCenter(w/2,h/2));
forceSimulation.nodes(nodes)
.on("tick");
forceSimulation.force("link")
.links(links)
.distance(180);
var link=svg.selectAll(".link")
.data(links)
.enter()
.append("line")
.attr("class","link")
.style("stroke-width",1)
.style("stroke",(d,i)=>color[i%10])
.style("opacity",0.6);
var node=svg.selectAll(".node")
.data(nodes)
.enter()
.append("circle")
.attr("r",16)
.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",10);
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);
var circles=svg.selectAll("forceCircle")
.data(nodes)
.enter()
.append("circle")
.attr("class","forceCircle")
.attr("r",radius)
.style("stroke","DarkGray")
.style("stroke-width","1.0px")
.attr("fill", function(d, i){
//创建圆形图片
var defs = svg.append("defs").attr("id", "imgdefs");
var catpattern = defs.append("pattern")
.attr("id", "catpattern" + i)
.attr("height", 1)
.attr("width", 1);
catpattern.append("image")
.attr("x", - (img_w / 2 - radius+5.8))
.attr("y", - (img_h / 2 - radius+3.5))
.attr("width", img_w+11)
.attr("height", img_h+6)
.attr("xlink:href","image/"+d.image);
return "url(#catpattern" + i + ")";
})
.on("mouseover",function(d,i){ //加入提示框
tooltip.html("角色简介:"+d.intro)
.style("left",(d3.event.pageX)+"px")
.style("top",(d3.event.pageY+20)+"px")
.style("opacity",1.0);
})
}
}
})
</script>