首页 > 解决方案 > 我需要哪些概念和技术来实现这种图

问题描述

我想画出某些功能块如何相互交互的动态视觉效果。我很难定义我需要什么样的图表,以及最适合从什么(如果有的话)d3 布局函数开始。我什至不确定我的示例是否属于图表的定义。

我试图实现的粗略概念

总体思路是将一组函数及其输入和输出可视化。它以一组输入开始,以一组输出结束。在这之间有几个函数,每个函数都接受输入并生成 1 个或多个输出。每个输出都可以作为一个或多个功能的输入。所以每条边/线代表一个输出被转移到一个函数作为输入(并且是单向的)

我不是在寻找代码中的答案,而是对我需要从哪些概念开始的洞察力。该图像可能有问题,无法从字面上实现,但我无法指出具体是什么。

标签: d3.jsgraph-theory

解决方案


const nodes = [
	{
		id: 1,
		title: 'Function A',
		x: 100,
		y: 25,
		points: [
			{
				id: 11,
				dx: 50,
				dy: 0
			},
			{
				id: 12,
				dx: 0,
				dy: 20
			}
		]
	},
	{
		id: 2,
		title: 'Function B',
		x: 300,
		y: 100,
		points: [
			{
				id: 21,
				dx: -50,
				dy: 0
			},
			{
				id: 22,
				dx: 0,
				dy: 20
			}
		]
	},
	{
		id: 3,
		title: 'Function C',
		x: 170,
		y: 160,
		points: [
			{
				id: 31,
				dx: 0,
				dy: -20
			},
			{
				id: 32,
				dx: 50,
				dy: 0
			}
		]
	}
];

const links = [
	{source: 11, target:21},
	{source: 12, target:31},
	{source: 22, target:32}
];

var selectedNode = null;

const renderNodes = svg => {
	const allNodes = svg.selectAll('.node').data(nodes, node => node.id);
	const addedNodes = allNodes.enter()
		.append('g')
		.style('cursor', 'pointer')
		.attr('transform', d => `translate(${d.x},${d.y})`)
		.on('click', d => {
			selectedNode = d.id;
			renderAll();
		});
		
	addedNodes.append('rect')
		.attr('width', 100)
		.attr('height', 40)
		.attr('x', -50)
		.attr('y', -20)
		.attr('rx', 5);

	addedNodes.append('text')
		.text(d => d.title)
		.style('fill', 'white')
		.attr('y', 6)
		.attr('text-anchor', 'middle');
		
	addedNodes.merge(allNodes)
		.select('rect')
		.style('fill', d => (d.id === selectedNode) ? 'blue' : 'black');
		
	allNodes.exit().remove();	
};

const renderConnectionPoints = (svg, points) => {
	const allPoints = svg.selectAll('.point').data(points, point => point.id);
	const addedPoints = allPoints.enter()
		.append('g')
		.style('cursor', 'pointer')
		.attr('transform', d => `translate(${d.x},${d.y})`);
		
	addedPoints.append('circle')
		.attr('r', 4)
		.style('fill', 'white');

	addedPoints.merge(allPoints)
		.select('circle')
		.style('stroke', d => (d.parentId === selectedNode) ? 'blue' : 'black');
		
	allPoints.exit().remove();	
}

const renderLinks = (svg, _links) => {
	const linkPath = d => `M ${d.source.x}, ${d.source.y} 
		C ${(d.source.x + d.target.x) / 2}, ${d.source.y}
		${(d.source.x + d.target.x) / 2}, ${d.target.y}
		${d.target.x}, ${d.target.y}`;

	const allLinks = svg.selectAll('.link').data(_links, link => link.id);
	const addedLinks = allLinks.enter()
		.append('path')
		.style('fill', 'none')
		.attr('d', linkPath);
		
	addedLinks.merge(allLinks)
		.style('stroke', d => (d.source.parentId === selectedNode || 
		d.target.parentId === selectedNode) ? 'blue' : 'lightgray');
		
	allLinks.exit().remove();	
	
}

const getPoints = () => nodes.reduce((all, node) => {
		node.points.forEach(point => all.push({
			id: point.id, x: node.x + point.dx, y: node.y + point.dy, parentId: node.id}));
		return all;
	}, []);

const getLinks = points => links.map(link => {
		const source = points.find(point => point.id === link.source);
		const target = points.find(point => point.id === link.target);
		return {source, target};
	});  

const renderAll = () => {
	const svg = d3.select('svg');
	const points = getPoints();
	const _links = getLinks(points);
	renderNodes(svg);
	renderLinks(svg, _links);
	renderConnectionPoints(svg, points);
} 

renderAll();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width='400' height='180'>
	
</svg>

Hi, Take a look at the snippet. The data model is very simple: nodes, their connection points and links Just provide your data and use the snippet's code to render it. Good luck and feel free to ask any question :)


推荐阅读