d3 拖动:无法创建属性“fx”在数字“0”上
首先感谢您提出问题。
我的问题是我的拖动处理程序在 d3 v6/v7 上无法按预期工作(两者都尝试过)。现在我没有在函数中接收事件,所以我无法将新的 x,y 值分配给节点。
这是我的节点结构:
{
"id": string,
"color": string,
"distance": number,
"size": number,
"type": string,
"visibility": string,
"data": any[]
},
这是我的行结构:
{
"source": string,
"target": string,
"distance": number,
"size": number,
"color": string,
"visibility": string,
"type": string
}
这是我的代码:
import { Component, OnInit } from '@angular/core';
import { select, selectAll } from 'd3-selection';
import { transition } from 'd3-transition';
import { forceSimulation, forceLink, forceManyBody, forceCenter } from 'd3-force';
import { drag } from 'd3-drag';
const d3 = { select, selectAll, transition, forceSimulation, forceLink, forceManyBody, forceCenter, drag };
import { S4pService } from 'src/app/services/s4p.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-mapit',
templateUrl: './mapit.component.html',
styleUrls: []
})
export class MapitComponent implements OnInit {
svg_width = 1252;
svg_height = 601;
radius = 10;
NODES: any;
LINKS: any;
constructor(private s4p: S4pService, private router: Router){}
ngOnInit(): void {
this.s4p.getNetworkData(this.router.url).subscribe((data: any) => {
this.NODES = data.nodes;
this.LINKS = data.links;
this.createGraph();
});
}
createGraph(){
const svg = d3.select('#container');
const svg_container = document.getElementById('map-container')!;
this.svg_width = svg_container.offsetWidth;
this.svg_height = svg_container.offsetHeight;
const centerX = this.svg_width/2;
const centerY = this.svg_height/2;
const simulation = d3.forceSimulation(this.NODES)
.force('charge', d3.forceManyBody().strength(-20))
.force('link', d3.forceLink(this.LINKS).id((d:any) => d.id ).distance(((link: any) => link.distance) as any))
.force('center', d3.forceCenter(centerX, centerY));
const dragInteraction: any = d3.drag().on('drag', (event: any, node: any) => {
console.log(event); // --> console.log() added to question below
console.log(node); // --> console.log() added to question below
node.fx = event.x;
node.fy = event.y;
simulation.alpha(1);
simulation.restart();
});
const lines = svg
.selectAll('line')
.data(this.LINKS)
.enter()
.append('line')
.attr('stroke', ((link: any) => link.color || 'black') as any)
.attr('visibility', (link: any) => link.source.type === 'Main' && link.target.type === 'Main' ? 'visible !important' : link.visibility )
.attr('class', (link: any) => `line-${ link.source.id }`)
.attr('source', (link: any) => link.source.id )
.attr('target', (link: any) => link.target.id )
.attr('type', 'line')
const circles = svg
.selectAll('circle')
.data(this.NODES)
.enter()
.append('circle')
.attr('fill', ((node: any) => node.color || 'gray') as any)
.attr('r', this.radius - .75 /*((node: any) => node.size) as any*/)
.attr('class', (node: any) => `node-${ node.id }`)
.attr('id', (node: any) => `node-${ node.id }`)
.attr('visibility', (node: any) => node.visibility ? node.visibility : 'hidden')
.attr('parents', (node: any) => node.parents ? node.parents : '')
.call(dragInteraction)
.on("mouseover", mouseover)
.on("mouseout", (node:any) => mouseout(node))
.on("contextmenu", (node:any) => alert(JSON.stringify(node.srcElement.__data__)))
.on("click", (n:any) => displayChildNodes(n));
const text = svg
.selectAll('text')
.data(this.NODES)
.enter()
.append('text')
.attr('text-anchor', ('middle') as any)
.attr('alignment-baseline', ('middle') as any)
.attr('class', (node: any) => `text-${ node.id }`)
.attr('id', (node: any) => `text-${ node.id }`)
.attr('visibility', (node: any) => node.visibility ? node.visibility : 'hidden')
.attr('type', 'text')
.style('pointer-events', ('none') as any)
.text( (node: any) => node.id );
simulation.on('tick', () => {
let width = this.svg_width;
let height = this.svg_height;
circles
.attr("cx", function(d: any) { return d.x = Math.max(10, Math.min(width - 10, d.x)); })
.attr("cy", function(d: any) { return d.y = Math.max(10, Math.min(height - 10, d.y)); });
text
.attr('x', ((node: any) => node.x) as any)
.attr('y', ((node: any) => node.y) as any);
lines
.attr('x1', ((link: any) => link.source.x) as any)
.attr('y1', ((link: any) => link.source.y) as any)
.attr('x2', ((link: any) => link.target.x) as any)
.attr('y2', ((link: any) => link.target.y) as any)
});
function mouseover(d: any) {
lines
.attr("stroke","black")
.attr("stroke-width",2)
d3.selectAll('line')
.transition()
.duration(500)
.style("opacity", function(o: any) {
return o.source.id === d.id || o.target.id === d.id ? 1 : .1;
});
circles
.transition()
.duration(500)
.attr('r', (node:any) => {
if(node.id === d.id) return node.size + 15
return node.size;
});
}
function mouseout(d: any) {
lines
.attr('stroke', ((line: any) => line.color) as any );
lines
.transition()
.duration(500)
.style("opacity", 1);
circles
.attr('fill', ((node: any) => node.color || 'gray') as any)
.attr('r', 10) /*((node: any) => node.size) as any)*/
circles
.transition()
.duration(500)
}
function displayChildNodes(node: any){
const node_lines: any = document.getElementsByClassName(`line-${ node.id }`);
for(let line of node_lines){
const childNode = document.getElementById(`node-${ line.getAttribute('target')}`)!;
const childNodeText = document.getElementById(`text-${ line.getAttribute('target')}`)!;
childNode.getAttribute('visibility') === 'visible' ? childNode.setAttribute('visibility', 'hidden') : childNode.setAttribute('visibility', 'visible');
childNodeText.getAttribute('visibility') === 'visible' ? childNodeText.setAttribute('visibility', 'hidden') : childNodeText.setAttribute('visibility', 'visible');
line.getAttribute('visibility') === 'visible' ? line.setAttribute('visibility', 'hidden') : line.setAttribute('visibility', 'visible');
let other_parents = childNode.getAttribute('parents')!.split('*');
other_parents = [...new Set(other_parents)];
let myindex = other_parents.indexOf('undefined');
other_parents.splice(myindex, 1);
myindex = other_parents.indexOf(line.source);
other_parents.splice(myindex, 1);
for(let parent_id of other_parents){
const parent_lines = document.getElementsByClassName(`line-${ parent_id }`)!;
for(let i=0; i<parent_lines.length; i++){
const ids = childNode.getAttribute('id')!.split('-');
if(parent_lines[i].getAttribute('target') === ids[1] && parent_lines[i].getAttribute('visibility') === 'visible'){
childNode.setAttribute('visibility', 'visible');
childNodeText.setAttribute('visibility', 'visible')
}
}
}
}
}
}
}
这是拖动处理程序处的 console.log():
对于事件(没有收到事件,而是我正在接收节点对象):
{
"id": "myid",
"color": "#CBE4F9",
"distance": 5,
"size": 50,
"type": "Main",
"visibility": "visible",
"index": 0,
"x": 709.282818224574,
"y": 98.26132198282302,
"vy": -0.0306124925872071,
"vx": -0.02388584994074383
}
对于节点(没有相反,我收到了一个与节点数组中节点的索引位置相对应的数字):
0
在 console.logs 之后,我收到错误:
有人遇到过类似的问题吗?欢迎任何帮助。
谢谢指教。
first of all thanks for entering the question.
My issue is my drag handler is not working as expected on d3 v6/v7 (tried both). Right now I'm not receiving the event in the function, so I can't assign the new x,y values to the node.
Here is my node structure:
{
"id": string,
"color": string,
"distance": number,
"size": number,
"type": string,
"visibility": string,
"data": any[]
},
Here is my line structure:
{
"source": string,
"target": string,
"distance": number,
"size": number,
"color": string,
"visibility": string,
"type": string
}
Here is my code:
import { Component, OnInit } from '@angular/core';
import { select, selectAll } from 'd3-selection';
import { transition } from 'd3-transition';
import { forceSimulation, forceLink, forceManyBody, forceCenter } from 'd3-force';
import { drag } from 'd3-drag';
const d3 = { select, selectAll, transition, forceSimulation, forceLink, forceManyBody, forceCenter, drag };
import { S4pService } from 'src/app/services/s4p.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-mapit',
templateUrl: './mapit.component.html',
styleUrls: []
})
export class MapitComponent implements OnInit {
svg_width = 1252;
svg_height = 601;
radius = 10;
NODES: any;
LINKS: any;
constructor(private s4p: S4pService, private router: Router){}
ngOnInit(): void {
this.s4p.getNetworkData(this.router.url).subscribe((data: any) => {
this.NODES = data.nodes;
this.LINKS = data.links;
this.createGraph();
});
}
createGraph(){
const svg = d3.select('#container');
const svg_container = document.getElementById('map-container')!;
this.svg_width = svg_container.offsetWidth;
this.svg_height = svg_container.offsetHeight;
const centerX = this.svg_width/2;
const centerY = this.svg_height/2;
const simulation = d3.forceSimulation(this.NODES)
.force('charge', d3.forceManyBody().strength(-20))
.force('link', d3.forceLink(this.LINKS).id((d:any) => d.id ).distance(((link: any) => link.distance) as any))
.force('center', d3.forceCenter(centerX, centerY));
const dragInteraction: any = d3.drag().on('drag', (event: any, node: any) => {
console.log(event); // --> console.log() added to question below
console.log(node); // --> console.log() added to question below
node.fx = event.x;
node.fy = event.y;
simulation.alpha(1);
simulation.restart();
});
const lines = svg
.selectAll('line')
.data(this.LINKS)
.enter()
.append('line')
.attr('stroke', ((link: any) => link.color || 'black') as any)
.attr('visibility', (link: any) => link.source.type === 'Main' && link.target.type === 'Main' ? 'visible !important' : link.visibility )
.attr('class', (link: any) => `line-${ link.source.id }`)
.attr('source', (link: any) => link.source.id )
.attr('target', (link: any) => link.target.id )
.attr('type', 'line')
const circles = svg
.selectAll('circle')
.data(this.NODES)
.enter()
.append('circle')
.attr('fill', ((node: any) => node.color || 'gray') as any)
.attr('r', this.radius - .75 /*((node: any) => node.size) as any*/)
.attr('class', (node: any) => `node-${ node.id }`)
.attr('id', (node: any) => `node-${ node.id }`)
.attr('visibility', (node: any) => node.visibility ? node.visibility : 'hidden')
.attr('parents', (node: any) => node.parents ? node.parents : '')
.call(dragInteraction)
.on("mouseover", mouseover)
.on("mouseout", (node:any) => mouseout(node))
.on("contextmenu", (node:any) => alert(JSON.stringify(node.srcElement.__data__)))
.on("click", (n:any) => displayChildNodes(n));
const text = svg
.selectAll('text')
.data(this.NODES)
.enter()
.append('text')
.attr('text-anchor', ('middle') as any)
.attr('alignment-baseline', ('middle') as any)
.attr('class', (node: any) => `text-${ node.id }`)
.attr('id', (node: any) => `text-${ node.id }`)
.attr('visibility', (node: any) => node.visibility ? node.visibility : 'hidden')
.attr('type', 'text')
.style('pointer-events', ('none') as any)
.text( (node: any) => node.id );
simulation.on('tick', () => {
let width = this.svg_width;
let height = this.svg_height;
circles
.attr("cx", function(d: any) { return d.x = Math.max(10, Math.min(width - 10, d.x)); })
.attr("cy", function(d: any) { return d.y = Math.max(10, Math.min(height - 10, d.y)); });
text
.attr('x', ((node: any) => node.x) as any)
.attr('y', ((node: any) => node.y) as any);
lines
.attr('x1', ((link: any) => link.source.x) as any)
.attr('y1', ((link: any) => link.source.y) as any)
.attr('x2', ((link: any) => link.target.x) as any)
.attr('y2', ((link: any) => link.target.y) as any)
});
function mouseover(d: any) {
lines
.attr("stroke","black")
.attr("stroke-width",2)
d3.selectAll('line')
.transition()
.duration(500)
.style("opacity", function(o: any) {
return o.source.id === d.id || o.target.id === d.id ? 1 : .1;
});
circles
.transition()
.duration(500)
.attr('r', (node:any) => {
if(node.id === d.id) return node.size + 15
return node.size;
});
}
function mouseout(d: any) {
lines
.attr('stroke', ((line: any) => line.color) as any );
lines
.transition()
.duration(500)
.style("opacity", 1);
circles
.attr('fill', ((node: any) => node.color || 'gray') as any)
.attr('r', 10) /*((node: any) => node.size) as any)*/
circles
.transition()
.duration(500)
}
function displayChildNodes(node: any){
const node_lines: any = document.getElementsByClassName(`line-${ node.id }`);
for(let line of node_lines){
const childNode = document.getElementById(`node-${ line.getAttribute('target')}`)!;
const childNodeText = document.getElementById(`text-${ line.getAttribute('target')}`)!;
childNode.getAttribute('visibility') === 'visible' ? childNode.setAttribute('visibility', 'hidden') : childNode.setAttribute('visibility', 'visible');
childNodeText.getAttribute('visibility') === 'visible' ? childNodeText.setAttribute('visibility', 'hidden') : childNodeText.setAttribute('visibility', 'visible');
line.getAttribute('visibility') === 'visible' ? line.setAttribute('visibility', 'hidden') : line.setAttribute('visibility', 'visible');
let other_parents = childNode.getAttribute('parents')!.split('*');
other_parents = [...new Set(other_parents)];
let myindex = other_parents.indexOf('undefined');
other_parents.splice(myindex, 1);
myindex = other_parents.indexOf(line.source);
other_parents.splice(myindex, 1);
for(let parent_id of other_parents){
const parent_lines = document.getElementsByClassName(`line-${ parent_id }`)!;
for(let i=0; i<parent_lines.length; i++){
const ids = childNode.getAttribute('id')!.split('-');
if(parent_lines[i].getAttribute('target') === ids[1] && parent_lines[i].getAttribute('visibility') === 'visible'){
childNode.setAttribute('visibility', 'visible');
childNodeText.setAttribute('visibility', 'visible')
}
}
}
}
}
}
}
Here the console.log()s at drag handler:
For event (no event received, instead I'm receiving the node object):
{
"id": "myid",
"color": "#CBE4F9",
"distance": 5,
"size": 50,
"type": "Main",
"visibility": "visible",
"index": 0,
"x": 709.282818224574,
"y": 98.26132198282302,
"vy": -0.0306124925872071,
"vx": -0.02388584994074383
}
For node (no node received, instead, I'm receiving a number which corresponds to the index position of the node in nodes array):
0
Just after the console.logs I receive the error:
Has anyone been facing a similar issue? Any help is welcomed.
Thanks in advice.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论