環境,數據,函數部分請參考 上一篇博客d3-V5 力引導佈局實例圖,這裏僅繪圖部分代碼
// 新建一個力導向圖
this.forceSimulation = d3.forceSimulation()
.force('link', d3.forceLink().id(d => d.id))
.force('charge', d3.forceManyBody().strength(() => -50))
.force('center', d3.forceCenter());
const marge = { top: 80, bottom: 60, left: 50, right: 60 };
const svg = d3.select('#containerKG').append('svg').attr('width', this.width - 20)
.attr('height', this.height - 20);
d3.forceX([100]);
const width = svg.attr('width');
const height = svg.attr('height');
let g = svg.append('g')
.attr('transform', `translate(${marge.top},${marge.left})`);
svg.call(
d3.zoom()
.scaleExtent([0.1, 4])
.on('zoom', () => { g.attr('transform', d3.event.transform); }),
);
let j = 0;
let k = 0;
const legendGropp = [];
for (let i = 0; i < legend.length; i++) {
const obj = {
group: legend[i].group,
states: true,
};
legendGropp.push(obj);
}
const nodeOri = node;
const linkOri = fliterlink;
const sv = svg.selectAll('rect')
.data(legendGropp)
.enter()
.append('rect')
.attr('x', (d, i) => {
if (d.group.length > 2) {
j = j + d.group.length - 2;
k++;
return width / 3 + (k - 1) * 12 * j + i * 70;
}
return width / 3 + i * 70 + k * 12 * j;
})
.attr('rx', 4)
.attr('ry', 4)
.attr('y', 40)
.attr('class', 'legend')
.attr('width', 25)
.attr('height', 15)
.on('click', (dlegend) => {
for (let i = 0; i < legendGropp.length; i++) {
if (legendGropp[i].group === dlegend.group) {
legendGropp[i].states = !legendGropp[i].states;
}
}
sv.attr('fill', (dd) => {
if (dd.states === false) {
return '#ccc';
}
return colorScale(dd.group);
});
const nodeClick = [];
const checkSetFalse = new Set();
for (let i = 0; i < legendGropp.length; i++) {
if (legendGropp[i].states === true) {
for (let m = 0; m < nodeOri.length; m++) {
if (nodeOri[m].group === legendGropp[i].group) {
nodeClick.push(nodeOri[m]);
}
}
} else {
checkSetFalse.add(legendGropp[i].group);
}
}
const LinksObj = [];
for (let p = 0; p < linkOri.length; p++) {
if (checkSetFalse.has(linkOri[p].source.group)
|| checkSetFalse.has(linkOri[p].target.group)) {
continue;
}
LinksObj.push(linkOri[p]);
}
node = nodeClick;
fliterlink = LinksObj;
d3.selectAll('svg > g').remove();
d3.forceX([100]);
this.forceSimulation = d3.forceSimulation()
.force('link', d3.forceLink().id(d => d.id))
.force('charge', d3.forceManyBody().strength(() => -50))
.force('center', d3.forceCenter());
g = svg.append('g')
.attr('transform', `translate(${marge.top},${marge.left})`);
svg.call(
d3.zoom()
.scaleExtent([0.1, 4])
.on('zoom', () => { g.attr('transform', d3.event.transform); }),
);
this.forceSimulation.nodes(node)
.on('tick', this.ticked);// 這個函數很重要,後面給出具體實現和說明
// 生成邊數據
this.forceSimulation.force('link')
.links(fliterlink)
.distance(() => 200);
// 設置圖形的中心位置
this.forceSimulation.force('center')
.x(width / 2)
.y(height / 2);
// 在瀏覽器的控制檯輸出
// 有了節點和邊的數據後,我們開始繪製
// 繪製邊
this.links = g.append('g')
.selectAll('line')
.data(fliterlink)
.enter()
.append('line')
.attr('stroke', '#ccc')
.attr('stroke-width', 1);
// 繪製節點
// 老規矩,先爲節點和節點上的文字分組
this.gs = g.selectAll('.circleText')
.data(node)
.enter()
.append('g')
.attr('transform', (d) => {
const cirX = d.x;
const cirY = d.y;
return `translate(${cirX},${cirY})`;
})
.call(d3.drag()
.on('start', this.started)
.on('drag', this.dragged)
.on('end', this.ended));
// 繪製節點
this.gs.append('circle')
.attr('r', 15)
.attr('class', 'circleR')
.attr('fill', d => colorScale(d.group))
.on('mouseover', function () {
d3.select(this)
.attr('r', 18)
.attr('fill', d => colorScale(d.group))
.attr('stroke', '#D3D3D3')
.attr('stroke-width', 1)
.attr('opacity', '0.8');
})
.on('mouseout', function () {
d3.select(this)
.transition()
.duration(300)
.attr('fill', d => colorScale(d.group))
.attr('r', 15)
.attr('stroke', 'none')
.attr('opacity', '1');
});
// this.gs.append('title')
// .text('my color is')
// .style('fill', 'red');
// 文字
this.gs.append('text')
.attr('x', -15)
.attr('y', -28)
.attr('dy', 10)
.attr('fill', d => colorScale(d.group))
.text(d => d.id);
})
.attr('fill', d => colorScale(d.group));
let h = 0;
let l = 0;
svg.selectAll('.text')
.data(legend)
.enter()
.append('text')
.attr('class', 'Text')
.attr('x', (d, i) => {
if (d.group.length > 2) {
l++;
h += d.group.length - 2;
return width / 3 + 25 + i * 70 + (l - 1) * 12 * h;
}
return width / 3 + 25 + i * 70 + l * 12 * h;
})
.attr('y', 52)
.attr('transform', 'translate(5)')
.text(d => d.group);
this.forceSimulation.nodes(node)
.on('tick', this.ticked);// 這個函數很重要,後面給出具體實現和說明
// 生成邊數據
this.forceSimulation.force('link')
.links(fliterlink)
.distance(() => 200);
// 設置圖形的中心位置
this.forceSimulation.force('center')
.x(width / 2)
.y(height / 2);
// 在瀏覽器的控制檯輸出
// 有了節點和邊的數據後,我們開始繪製
// 繪製邊
this.links = g.append('g')
.selectAll('line')
.data(fliterlink)
.enter()
.append('line')
.attr('stroke', '#ccc')
.attr('stroke-width', 1);
// 繪製節點
// 老規矩,先爲節點和節點上的文字分組
this.gs = g.selectAll('.circleText')
.data(node)
.enter()
.append('g')
.attr('transform', (d) => {
const cirX = d.x;
const cirY = d.y;
return `translate(${cirX},${cirY})`;
})
.call(d3.drag()
.on('start', this.started)
.on('drag', this.dragged)
.on('end', this.ended));
// 繪製節點
this.gs.append('circle')
.attr('r', 15)
.attr('class', 'circleR')
.attr('fill', d => colorScale(d.group))
.on('mouseover', function () {
d3.select(this)
.attr('r', 18)
.attr('fill', d => colorScale(d.group))
.attr('stroke', '#D3D3D3')
.attr('stroke-width', 1)
.attr('opacity', '0.8');
})
.on('mouseout', function () {
d3.select(this)
.transition()
.duration(300)
.attr('fill', d => colorScale(d.group))
.attr('r', 15)
.attr('stroke', 'none')
.attr('opacity', '1');
});
// this.gs.append('title')
// .text('my color is')
// .style('fill', 'red');
// 文字
this.gs.append('text')
.attr('x', -15)
.attr('y', -28)
.attr('dy', 10)
.attr('fill', d => colorScale(d.group))
.text(d => d.id);
}