You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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();
});

修改说明

  1. 方向箭头:通过chart.edges().arrow().enabled(true)开启箭头,size()调整箭头大小,让连接方向一目了然
  2. 双向边分离:layout().type('parallel')让双向边平行展示,distance()控制间距,确保悬停时能精准触发对应边的提示
  3. Tooltip优化:自定义格式化函数将数组内容转为换行展示,提升可读性
  4. 修正了原代码中滚轮缩放的逻辑错误(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 10:47:50