如何用R/JavaScript实现可折叠层级网络图并生成离线HTML
层级网络点击展开/双击折叠的离线可视化实现(R语言)
需求概述
- 初始仅展示
Boss节点 - 单击节点展开其直接子节点
- 双击节点折叠其已展开的子节点
- 生成可离线打开的独立HTML文件,无需依赖Shiny应用
完整实现代码
library(igraph) library(visNetwork) # 构建TeamA数据 from_a <- c("Boss", "TeamA", "TeamA", "TeamA", "SubteamA1", "SubteamA1", "SubteamA1", "SubteamA2", "SubteamA2", "SubteamA2", "SubteamA3", "SubteamA3", "SubteamA3") to_a <- c("TeamA", "SubteamA1", "SubteamA2", "SubteamA3", "employee1", "employee2", "employee3", "employee4", "employee5", "employee6", "employee7", "employee8", "employee9") df_a <- data.frame(from = from_a, to = to_a) # 构建TeamB数据 from_b <- c("Boss", "TeamB", "TeamB", "TeamB", "SubteamB1", "SubteamB1", "SubteamB1", "SubteamB2", "SubteamB2", "SubteamB2", "SubteamB3", "SubteamB3", "SubteamB3") to_b <- c("TeamB", "SubteamB1", "SubteamB2", "SubteamB3", "employee10", "employee11", "employee12", "employee13", "employee14", "employee15", "employee16", "employee17", "employee18") df_b <- data.frame(from = from_b, to = to_b) # 合并数据 final_df <- rbind(df_a, df_b) # 转换为有向igraph对象,便于识别父子关系 g <- graph_from_data_frame(final_df, directed = TRUE) nodes <- data.frame(id = V(g)$name, label = V(g)$name) edges <- as.data.frame(get.edgelist(g)) colnames(edges) <- c("from", "to") # 给节点添加层级属性,初始隐藏非Boss节点 nodes$level <- sapply(nodes$id, function(x) { shortest.paths(g, v = "Boss", to = x)[1] }) nodes$hidden <- nodes$id != "Boss" # 初始仅显示Boss # 自定义交互逻辑:点击展开子节点,双击折叠子节点 network <- visNetwork(nodes, edges) %>% visHierarchicalLayout(direction = "LR", levelSeparation = 200) %>% visInteraction(navigation = c("zoom", "drag")) %>% visEvents( click = "function(nodes) { if (nodes.nodes.length > 0) { var nodeId = nodes.nodes[0]; // 筛选当前节点的直接子节点 var children = edges.filter(function(e) { return e.from === nodeId; }).map(function(e) { return e.to; }); // 显示子节点 children.forEach(function(child) { network.body.data.nodes.update({id: child, hidden: false}); }); } return false; }", doubleClick = "function(nodes) { if (nodes.nodes.length > 0) { var nodeId = nodes.nodes[0]; // 筛选当前节点的直接子节点 var children = edges.filter(function(e) { return e.from === nodeId; }).map(function(e) { return e.to; }); // 隐藏子节点 children.forEach(function(child) { network.body.data.nodes.update({id: child, hidden: true}); }); } return false; }" ) # 导出为离线HTML文件 visSave(network, file = "hierarchical_network.html")
代码说明
- 数据整理:修正原代码中重复赋值的问题,分别创建两个团队的边数据后合并,转换为有向igraph对象以明确父子节点关系。
- 节点预处理:
- 计算每个节点与
Boss的最短路径作为层级标记 - 设置初始隐藏非
Boss节点,实现仅展示根节点的初始状态
- 计算每个节点与
- 交互逻辑:
- 通过
visEvents绑定点击和双击事件,用JavaScript筛选当前节点的直接子节点 - 点击时将子节点的
hidden属性设为false实现展开,双击时设为true实现折叠
- 通过
- 布局与导出:使用横向层级布局优化展示效果,通过
visSave直接生成可离线打开的HTML文件,无需额外依赖。
内容的提问来源于stack exchange,提问作者stats_noob
相关产品推荐
相关产品推荐

