如何使用HTML和JavaScript创建带方向箭头与交互提示的有向网络图?
解决方案:实现带箭头的双向有向网络图及边悬停提示
针对你提出的两个核心需求,我们可以直接在现有AnyChart代码基础上修改实现,同时也提供一个完全免费开源的替代方案(D3.js)供你参考,满足更高的自定义需求。
一、基于AnyChart的快速修改实现
关键优化点
- 添加方向箭头:启用边的箭头配置,明确展示节点连接方向
- 分离双向边:设置平行布局,避免双向边合并,确保悬停时各自的提示信息独立触发
- 优化Tooltip展示:处理数组类型的
info,让内容更易读
修改后的完整代码
HTML 部分
<div id="container"></div> <script src="https://cdn.anychart.com/releases/8.10.0/js/anychart-core.min.js?hcode=a0c21fc77e1449cc86299c5faa067dc4"></script> <script src="https://cdn.anychart.com/releases/8.10.0/js/anychart-graph.min.js?hcode=a0c21fc77e1449cc86299c5faa067dc4"></script> <script src="https://cdn.anychart.com/releases/8.10.0/js/anychart-exports.min.js?hcode=a0c21fc77e1449cc86299c5faa067dc4"></script> <script src="https://cdn.anychart.com/releases/8.10.0/js/anychart-ui.min.js?hcode=a0c21fc77e1449cc86299c5faa067dc4"></script>
CSS 部分
html, body, #container { width: 100%; height: 100%; margin: 0; padding: 0; }
JavaScript 部分
anychart.onDocumentReady(function () { // 创建数据 var data = { nodes: [ {id: "A"}, {id: "B"}, {id: "C"} ], edges: [ {from: "A", to: "B", info: ["foo_one", "foo_two"]}, {from: "B", to: "A", info: "foo_three"}, {from: "C", to: "A", info: "foo_four"} ] }; // 初始化图表 var chart = anychart.graph(data); // 核心修改1:启用边的方向箭头并调整大小 chart.edges().arrow().enabled(true); chart.edges().arrow().size(8); // 核心修改2:设置平行布局,分离双向边并调整间距 chart.edges().layout().type('parallel'); chart.edges().layout().distance(15); // 关闭滚轮缩放(原代码这里写反了,修正为false) chart.interactivity().zoomOnMouseWheel(false); // 配置节点标签 chart.nodes().labels().enabled(true); chart.nodes().labels().format("{%id}"); chart.nodes().labels().fontSize(12); chart.nodes().labels().fontWeight(600); // 配置节点Tooltip chart.nodes().tooltip().useHtml(true); chart.nodes().tooltip().format("<span style='font-weight:bold'>{%id}</span>"); // 核心修改3:优化边的Tooltip,处理数组类型的info chart.edges().tooltip().useHtml(true); chart.edges().tooltip().format(function() { const info = this.info; return Array.isArray(info) ? info.join('<br>') : info; }); // 设置图表标题 chart.title("Network Graph: ABC (带方向箭头与独立双向边)"); // 绑定容器并渲染 chart.container("container"); chart.draw(); });
修改说明
- 方向箭头:通过
chart.edges().arrow().enabled(true)开启箭头,size()调整箭头大小,让连接方向一目了然 - 双向边分离:
layout().type('parallel')让双向边平行展示,distance()控制间距,确保悬停时能精准触发对应边的提示 - Tooltip优化:自定义格式化函数将数组内容转为换行展示,提升可读性
- 修正了原代码中滚轮缩放的逻辑错误(
zoomOnMouseWheel设为false才是禁止滚轮缩放)
二、免费开源替代方案:D3.js
如果需要更高的自定义自由度,D3.js是完全免费开源的可视化库,适合构建高度定制的网络图。以下是简化实现示例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>D3.js 有向网络图</title> <script src="https://d3js.org/d3.v7.min.js"></script> <style> html, body { height: 100%; margin: 0; } #container { width: 100%; height: 100%; } .link { stroke: #999; stroke-opacity: 0.6; stroke-width: 2; } .node circle { fill: #69b3a2; stroke: #fff; stroke-width: 2px; } .node text { font-size: 12px; font-weight: bold; } .arrowhead { fill: #999; } .tooltip { position: absolute; background: #fff; padding: 8px; border: 1px solid #ccc; border-radius: 4px; pointer-events: none; font-size: 12px; } </style> </head> <body> <div id="container"></div> <script> const width = document.getElementById('container').clientWidth; const height = document.getElementById('container').clientHeight; // 数据定义 const data = { nodes: [ {id: "A"}, {id: "B"}, {id: "C"} ], links: [ {source: "A", target: "B", info: ["foo_one", "foo_two"]}, {source: "B", target: "A", info: "foo_three"}, {source: "C", target: "A", info: "foo_four"} ] }; // 创建SVG容器 const svg = d3.select("#container") .append("svg") .attr("width", width) .attr("height", height); // 力导向布局配置 const simulation = d3.forceSimulation(data.nodes) .force("link", d3.forceLink(data.links).id(d => d.id).distance(150)) .force("charge", d3.forceManyBody().strength(-400)) .force("center", d3.forceCenter(width / 2, height / 2)); // 路径生成器(处理双向边偏移) const linkPath = d3.linkHorizontal() .x(d => d.x) .y(d => d.y); // 绘制边 const links = svg.append("g") .selectAll("path") .data(data.links) .join("path") .attr("class", "link") .attr("d", d => linkPath({ source: {x: d.source.x, y: d.source.y + (d.source.id > d.target.id ? 10 : -10)}, target: {x: d.target.x, y: d.target.y + (d.source.id > d.target.id ? 10 : -10)} })) .attr("marker-end", "url(#arrowhead)"); // 添加箭头标记 svg.append("defs").append("marker") .attr("id", "arrowhead") .attr("viewBox", "0 -5 10 10") .attr("refX", 15) .attr("refY", 0) .attr("markerWidth", 6) .attr("markerHeight", 6) .attr("orient", "auto") .append("path") .attr("d", "M0,-5L10,0L0,5"); // 绘制节点 const nodes = svg.append("g") .selectAll("circle") .data(data.nodes) .join("circle") .attr("r", 15) .attr("class", "node") .call(d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended)); // 绘制节点标签 const labels = svg.append("g") .selectAll("text") .data(data.nodes) .join("text") .text(d => d.id) .attr("x", d => d.x + 20) .attr("y", d => d.y + 5); // 创建Tooltip const tooltip = d3.select("body").append("div") .attr("class", "tooltip") .style("opacity", 0); // 边的悬停事件 links.on("mouseover", (event, d) => { tooltip.transition().duration(200).style("opacity", .9); const info = Array.isArray(d.info) ? d.info.join("<br>") : d.info; tooltip.html(info) .style("left", (event.pageX + 10) + "px") .style("top", (event.pageY - 28) + "px"); }) .on("mouseout", () => { tooltip.transition().duration(500).style("opacity", 0); }); // 更新布局 simulation.on("tick", () => { links.attr("d", d => linkPath({ source: {x: d.source.x, y: d.source.y + (d.source.id > d.target.id ? 10 : -10)}, target: {x: d.target.x, y: d.target.y + (d.source.id > d.target.id ? 10 : -10)} })); nodes.attr("cx", d => d.x).attr("cy", d => d.y); labels.attr("x", d => d.x + 20).attr("y", d => d.y + 5); }); // 拖拽函数 function dragstarted(event, d) { if (!event.active) simulation.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) simulation.alphaTarget(0); d.fx = null; d.fy = null; } </script> </body> </html>
D3.js方案优势
- 完全开源免费,无商用限制
- 高度自定义,可灵活调整边的样式、箭头、间距等细节
- 原生实现悬停Tooltip,支持复杂数据格式展示
- 自带力导向布局,节点支持拖拽交互
内容的提问来源于stack exchange,提问作者Pm740
相关产品推荐
相关产品推荐

