console
const devices = new Array(5).fill("").map((d,i)=>`device${i+1}`),
n = devices.length,
interval = 100,
rectwidth = 30,
radius=25,
linkRaduis=7;
function dragstarted(d) {
const g = d3.select(d3.select(this).node().parentNode);
g.raise().attr("stroke", "black");
}
function dragged(d) {
const g = d3.select(d3.select(this).node().parentNode);
d.x = d3.event.x;
d.y = d3.event.y;
g.select('rect').attr("x", d.x-rectwidth/2).attr("y", d.y - rectwidth/2);
g.select('text').attr("x", d.x ).attr("y", d.y+rectwidth);
g.select('.outerCircle').attr("cx", d.x ).attr("cy", d.y);
g.select('.dragCircle').attr("cx", d.x ).attr("cy", d.type === "HSM" ?d.y+radius : d.y-radius);
d3.selectAll("path").each(function(link){
const linkEle = d3.select(this);
if(link.source.name === d.name ||link.target.name === d.name){
linkEle.attr("d",d=>{
const source = d.source,
target = d.target;
if(source.type === "HSM"){
return `M${source.x} ${source.y+radius+linkRaduis} L${target.x} ${target.y - radius - linkRaduis}`
}else if(source.type !== "HSM" && target.type !== "HSM") {
return `M${source.x} ${source.y - radius -linkRaduis} L${target.x} ${target.y - radius - linkRaduis}`
}else{
return `M${source.x} ${source.y - radius -linkRaduis} L${target.x} ${target.y + radius + linkRaduis}`
}
});
}
})
}
function dragended(d) {
const g = d3.select(d3.select(this).node().parentNode);
g.raise().attr("stroke", null);
}
function dragstartedEdge(d) {
const container = d3.select(".container");
const linksContainer = container.select(".links")
linksContainer.append("path")
.classed("add",true)
.style("fill","none")
.style("stroke","#000");
}
function draggedEdge(d) {
const addPath = d3.select(".add");
const circle = d3.select(this);
const cx = circle.attr("cx"),cy = circle.attr("cy");
if(d.type === "HSM"){
addPath.attr("d",`M${+cx} ${+cy+linkRaduis} L${d3.event.x} ${d3.event.y + radius + linkRaduis }`);
}else{
addPath.attr("d",`M${+cx} ${+cy-linkRaduis} L${d3.event.x} ${d3.event.y - radius - linkRaduis }`);
}
}
function dragendedEdge(d) {
const {x,y} = d3.event;
let cy;
const cx = d3.event.x;
if(d.type === "HSM"){
cy = d3.event.y + radius + linkRaduis;
}else{
cy = d3.event.y - radius - linkRaduis;
}
const targets = d3.selectAll(".dragCircle");
targets.each(function(tar){
if(d.name !== tar.name){
const t_circle = d3.select(this);
const t_cx = +t_circle.attr("cx"),t_cy = +t_circle.attr("cy");
const dis = Math.abs(Math.sqrt(Math.pow(cx-t_cx,2)+Math.pow(cy-t_cy,2)));
if(dis< (linkRaduis+3)){
const container = d3.select(".container");
const linksContainer = container.select(".links")
linksContainer.append("path")
.datum({
source:d,
target:tar
})
.attr("d",d=>{
const source = d.source,
target = d.target;
if(source.type === "HSM"){
return `M${source.x} ${source.y+radius+linkRaduis} L${target.x} ${target.y - radius - linkRaduis}`
}else if(source.type !== "HSM" && target.type !== "HSM") {
return `M${source.x} ${source.y - radius -linkRaduis} L${target.x} ${target.y - radius - linkRaduis}`
}else{
return `M${source.x} ${source.y - radius -linkRaduis} L${target.x} ${target.y + radius + linkRaduis}`
}
})
.call(link=>giveLinkAttr(link))
}
}
})
d3.select(".add").remove();
}
const dragNode = d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
function container(){
console.log(this)
return this;
}
const dragEdge = d3.drag()
.on("start", dragstartedEdge)
.on("drag", draggedEdge)
.on("end", dragendedEdge)
const generateData = _=>{
const root = {name:"HSM",x:0,y:0,type:"HSM"};
let nodes,links;
if(devices.length%2 ===0){
nodes = devices.map((d,i)=>({
name:d,
x: (i-n/2)*interval + interval/2,
y:100
}))
}else{
nodes = devices.map((d,i)=>({
name:d,
x: (i-(n-1)/2)*interval,
y:150
}))
}
links = nodes.map(d=>({
source:root,
target:d
}))
return {nodes:[root,...nodes],links}
}
const giveLinkAttr = link=>{
link.style("fill","none")
.classed("link",true)
.style("stroke-width","1.5px")
.on("mouseover",d=>{
const line = d3.select(d3.event.target);
line.style("stroke-width","3px");
})
.on("mouseout",d=>{
const line = d3.select(d3.event.target);
line.style("stroke-width","1.5px");
})
.on("click",d=>{
d3.event.preventDefault();
d3.event.stopPropagation();
const line = d3.select(d3.event.target);
line.classed("selected",true);
})
}
const init = _ =>{
const width = 750,height=800;
const svg = d3.select(svgContainer).attr("width",width).attr("height",height);
const container = svg.append("g")
.classed("container",true)
.attr("transform",`translate(${width/2},50)`)
container.append("g").classed("links",true)
}
const draw = _=>{
const container = d3.select(".container");
const linksContainer = container.select(".links")
const {nodes,links} = generateData();
linksContainer.selectAll(".link")
.data(links)
.enter()
.append("path")
.attr("d",d=>{
const source = d.source,
target = d.target;
return `M${source.x} ${source.y+radius+linkRaduis}L${target.x} ${target.y - radius - linkRaduis}`
})
.call(link=>giveLinkAttr(link))
container.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.classed("node",true)
.call(g=>g.append("circle")
.attr("r",linkRaduis)
.attr("cx",d=> d.x)
.attr("cy",d=> d.type === "HSM" ? d.y+radius : d.y-radius)
.style("fill","#09c")
.classed("dragCircle",true)
.call(dragEdge)
.style("cursor","pointer")
.on("mouseover",d=>{
d3.select(d3.event.target).attr("r",linkRaduis+3)
})
.on("mouseout",d=>{
d3.select(d3.event.target).attr("r",linkRaduis)
}))
.call(g=>g.append("circle")
.attr("r",radius)
.attr("cx",d=>d.x)
.attr("cy",d=>d.y)
.style("fill","#fff")
.style("stroke","#000")
.classed("outerCircle",true)
.call(dragNode)
.style("cursor","pointer"))
.call(g=>g.append("rect")
.attr("width",rectwidth)
.attr("height",rectwidth)
.attr("x",d=>d.x-rectwidth/2)
.attr("y",d=>d.y-rectwidth/2)
.style("fill","lightpink")
.style("pointer-events","none")
.style("cursor","pointer"))
.call(g=>g.append("text")
.attr("x",d=>d.x)
.attr("y",d=>d.y+rectwidth)
.style("dominant-baseline","text-before-edge")
.text(d=>d.name)
);
}
const giveEvent = _=>{
d3.select("body").on("click",_=>{
d3.selectAll(".selected").classed("selected",false);
})
d3.select(".delete").on("click",_=>{
d3.selectAll("path.selected").remove();
})
}
init();
draw();
giveEvent();
<button class="delete">删除连线</button>
<svg id="svgContainer" width="750" height="800" />
#svg{
background: #ccc;
}
.link{
stroke:#000;
}
path.selected{
stroke:#09c;
}