【D3.js实战】Force Simulation 力导图制作

2024-03-04 06:30

本文主要是介绍【D3.js实战】Force Simulation 力导图制作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • Force Simulation 力导图实战 @ D3.js
      • 数据结构
      • 思路分析
      • 画布初始化,全局变量定义
      • 数据读取
      • 力模拟
      • 数据绑定 datajoin
        • 结点与结点名称
        • 链接
      • tick函数编写
      • drag 拖曳交互设计
        • ```d3.drag()```函数
        • dragstarted 拖曳开始
        • dragged 拖曳过程
        • dragended 拖曳结束
        • 调用方式
    • 完整代码
    • 效果
      • 初始
      • 拖曳

Force Simulation 力导图实战 @ D3.js

数据结构

做力导图使用到的数据为json数据,一般采用json数据来表达图结构。本次实验选用的json数据数据结构如下(图片中为经过了力模拟后的):

  • links至少要由target, source组成
    • 这连个属性标记了起始node和终止node,其值通在这里插入图片描述
      常为node的id。
  • node由groupid构成
    • group表示node属于哪一类(方便着色)
    • id是node的唯一标识符

思路分析

  • 画布初始化,全局变量定义
  • 数据读取
  • 力模拟
  • 数据绑定 datajoin
    • 结点node(
    • 连边link
  • simulation.on("tick",tick),tick函数编写
  • drag 拖曳交互设计

画布初始化,全局变量定义

这步比较基础,直接贴上代码

const svg = d3.select('svg')
const height = +(svg.attr('height'))
const width = +(svg.attr('width'))
const margin = {top:150,left:50,right:50,bottom:0
}const innerHeight = height-margin.top-margin.bottom;
const innerWidth = width - margin.left - margin.right;
var color = d3.scaleOrdinal(d3.schemeCategory10);
let link;
let nodes;
let simulation;
const render_init = function(){svg.append("text").attr('class', 'title').attr('font-size','2em').attr('x',margin.left).attr('y', margin.top/2).attr('transform',`translate(0,-40)` ).attr('font-weight', 'bold').attr('fill','blue' ).html("Force  Simulation")svg.append("g").attr('id', 'maingroup').attr('x', margin.left).attr('y', margin.top).attr('transform', `translate(${margin.left},${margin.top})`).attr('width',innerWidth ).attr('height',innerHeight )
}

数据读取

通常读取进来的数据要进行预处理,但是图数据有些不同。通常我们拿到的数据都是不符合d3力导图数据结构要求的。一般用python等语言写的脚本文件进行数据预处理整理成符合要求的数据结构。

d3.json('./data/miserables.json').then(data => {render_init();//画布初始化//force simulation 力模拟//data join 数据绑定//drag  交互事件
})

力模拟

有两点需要注意的地方:

  • forceLink这里要处理一下,绑定node.id。不然会按照node的索引来进行,这样设置tick的时候会非常不方便
  • 仅进行力模拟结点的位置不会进行实时更新,必须要有simulation.on(tick)才会把力模拟的结果反映到图元上
simulation = d3.forceSimulation().nodes(data.nodes).force("link",d3.forceLink(data.links).id(d => d.id)).force("manyBody",d3.forceManyBody()).force("center",d3.forceCenter(innerWidth/2,innerHeight/2))  .on("tick",tick)

数据绑定 datajoin

这里我们需要绑定的三个元素为: 结点,结点名称,链接

结点与结点名称

一个传统的方法是circletext分开绑定,但是这里采用一个更加高效的办法:就是创建一个 , 每个g标签代表一个结点,其中包含 circletext两个图元

  • 创建结点group
nodes = group.append('g').attr("class", "nodegroup")
//先创建一个group,其中包含所有结点(这步可有可无).selectAll('.node').data(data.nodes).join('g').attr('class','node' )
  • 结点绑定(circle)

    var circle = nodes.append('circle')
    .attr('r', 5)
    .attr('fill', d => color(d.group))
    
  • 结点名称绑定

    var label = nodes.append('text')
    .attr('x', 6)
    .attr('y', 3 )
    .html(d => d.id )
    .attr('font-size', '12px')
    
链接

这里有个坑,line标签是不能设置fill的,应该通过设置stroke属性来设置line的颜色。

//注意设置stroke
link = group.append('g').attr('class','linkgroup').selectAll('line').data(data.links).enter().append('line').attr('stroke-width', d => Math.sqrt(d.value)).attr('stroke', 'green').attr('stroke-opacity',0.6 )

tick函数编写

在tick函数中,我们要更新node的位置link的起点和终点

注意:node指包含了circle和text的那个group,这里只需要更新那个group的位置, 就会一起更新。这就是上面提出为什么要把 放在一个 中。

const tick = function(){link.attr('x1', d => d.source.x).attr('y1', d  => d.source.y).attr('x2', d => d.target.x).attr('y2', d => d.target.y)nodes.attr('transform', d => `translate(${d.x},${d.y})`)
}

drag 拖曳交互设计

现在给图加一些交互效果:让结点能够被鼠标选中并拖拽

d3.drag()函数

drag函数有三个需要配置的,分别是拖曳开始,拖曳过程和拖曳结束

const dragFunc = d3.drag()
.on('start',dragstarted)
.on('drag',dragged)
.on('end',dragended)
dragstarted 拖曳开始
  • alphaTarget:衰减系数,对节点位置移动过程的模拟,数值越高移动越快,数值范围[0,1]
  • restart()重新启动仿真的内部定时器并且返回仿真。与 simulation*.alphaTargetsimulation*.alpha结合使用,这个方法可以在交互期间再次激活仿真,比如拖拽节点或者在使用 simulation.stop临时暂停仿真后使用。
function dragstarted(d) {if (!d3.event.active) simulation.alphaTarget(0.3).restart();d.fx = d.x;d.fy = d.y;
}
dragged 拖曳过程

fx,fy:结点的最终位置

const dragged = function(d){d.fx = d3.event.x;d.fy = d3.event.y;
}
dragended 拖曳结束

最后将fx和fy设置成null,表示拖曳结束后让结点回到力模拟的位置,而不是停留在拖曳的位置

const dragended = function(d){if (!d3.event.active) {simulation.alphaTarget(0);}//让它回到原来的位置d.fx = null;d.fy = null;
}
调用方式
dragFunc(nodes)//param:拖曳对象

完整代码

<!DOCTYPE html>
<html>
<head><title>force</title><script src="./js/d3.min.js"></script></head>
<body><svg width="1200" height = "650"></svg>
<script >const svg = d3.select('svg')const height = +(svg.attr('height'))const width = +(svg.attr('width'))const margin = {top:150,left:50,right:50,bottom:0}const innerHeight = height-margin.top-margin.bottom;const innerWidth = width - margin.left - margin.right;var color = d3.scaleOrdinal(d3.schemeCategory10);let link;let nodes;let simulation;const render_init = function(){svg.append("text").attr('class', 'title').attr('font-size','2em').attr('x',margin.left).attr('y', margin.top/2).attr('transform',`translate(0,-40)` ).attr('font-weight', 'bold').attr('fill','blue' ).html("Force  Simulation")svg.append("g").attr('id', 'maingroup').attr('x', margin.left).attr('y', margin.top).attr('transform', `translate(${margin.left},${margin.top})`).attr('width',innerWidth ).attr('height',innerHeight )}const tick = function(){link.attr('x1', d => d.source.x).attr('y1', d  => d.source.y).attr('x2', d => d.target.x).attr('y2', d => d.target.y)nodes.attr('transform', d => `translate(${d.x},${d.y})`)}const dragstarted = function(d){if(!d3.event.active){ 设置衰减系数,对节点位置移动过程的模拟,数值越高移动越快,数值范围[0,1]simulation.alphaTarget(0.3).restart();d.fx = d.x;d.fy = d.y;}}const dragged = function(d){d.fx = d3.event.x;d.fy = d3.event.y;}const dragended = function(d){if (!d3.event.active) {simulation.alphaTarget(0);}//让它回到原来的位置d.fx = null;d.fy = null;}d3.json('./data/miserables.json').then(data => {console.log(data);render_init();const group = d3.select('#maingroup')simulation = d3.forceSimulation().nodes(data.nodes).force("link",d3.forceLink(data.links).id(d => d.id)).force("manyBody",d3.forceManyBody()).force("center",d3.forceCenter(innerWidth/2,innerHeight/2))  .on("tick",tick)//注意设置strokelink = group.append('g').attr('class','linkgroup').selectAll('line').data(data.links).enter().append('line').attr('stroke-width', d => Math.sqrt(d.value)).attr('stroke', 'green').attr('stroke-opacity',0.6 )nodes = group.append('g').attr("class", "nodegroup").selectAll('.node').data(data.nodes).join('g').attr('class','node' )var circle = nodes.append('circle').attr('r', 5).attr('fill', d => color(d.group))var label = nodes.append('text').attr('x', 6).attr('y', 3 ).html(d => d.id ).attr('font-size', '12px')const dragFunc = d3.drag().on('start',dragstarted).on('drag',dragged).on('end',dragended)dragFunc(nodes)//拖曳对象})
</script>
</body>
</html>

效果

初始

在这里插入图片描述

拖曳

在这里插入图片描述

这篇关于【D3.js实战】Force Simulation 力导图制作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/772277

相关文章

Python列表去重的4种核心方法与实战指南详解

《Python列表去重的4种核心方法与实战指南详解》在Python开发中,处理列表数据时经常需要去除重复元素,本文将详细介绍4种最实用的列表去重方法,有需要的小伙伴可以根据自己的需要进行选择... 目录方法1:集合(set)去重法(最快速)方法2:顺序遍历法(保持顺序)方法3:副本删除法(原地修改)方法4:

在Spring Boot中浅尝内存泄漏的实战记录

《在SpringBoot中浅尝内存泄漏的实战记录》本文给大家分享在SpringBoot中浅尝内存泄漏的实战记录,结合实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录使用静态集合持有对象引用,阻止GC回收关键点:可执行代码:验证:1,运行程序(启动时添加JVM参数限制堆大小):2,访问 htt

JS+HTML实现在线图片水印添加工具

《JS+HTML实现在线图片水印添加工具》在社交媒体和内容创作日益频繁的今天,如何保护原创内容、展示品牌身份成了一个不得不面对的问题,本文将实现一个完全基于HTML+CSS构建的现代化图片水印在线工具... 目录概述功能亮点使用方法技术解析延伸思考运行效果项目源码下载总结概述在社交媒体和内容创作日益频繁的

Node.js 数据库 CRUD 项目示例详解(完美解决方案)

《Node.js数据库CRUD项目示例详解(完美解决方案)》:本文主要介绍Node.js数据库CRUD项目示例详解(完美解决方案),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考... 目录项目结构1. 初始化项目2. 配置数据库连接 (config/db.js)3. 创建模型 (models/

使用Node.js制作图片上传服务的详细教程

《使用Node.js制作图片上传服务的详细教程》在现代Web应用开发中,图片上传是一项常见且重要的功能,借助Node.js强大的生态系统,我们可以轻松搭建高效的图片上传服务,本文将深入探讨如何使用No... 目录准备工作搭建 Express 服务器配置 multer 进行图片上传处理图片上传请求完整代码示例

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

MyBatis 动态 SQL 优化之标签的实战与技巧(常见用法)

《MyBatis动态SQL优化之标签的实战与技巧(常见用法)》本文通过详细的示例和实际应用场景,介绍了如何有效利用这些标签来优化MyBatis配置,提升开发效率,确保SQL的高效执行和安全性,感... 目录动态SQL详解一、动态SQL的核心概念1.1 什么是动态SQL?1.2 动态SQL的优点1.3 动态S

Pandas使用SQLite3实战

《Pandas使用SQLite3实战》本文主要介绍了Pandas使用SQLite3实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录1 环境准备2 从 SQLite3VlfrWQzgt 读取数据到 DataFrame基础用法:读