canvas力导布局

老规矩,先上效果图

<html><head><style>* {margin: 0;padding: 0;}canvas {display: block;width: 100%;height: 100%;background: #000;}</style>
</head><body><canvas id="network"></canvas>
</body>
<script>class TaskQueue {constructor() {this.taskList = []this.hasTaskDone = falsethis.status = 'do' // do or stopthis.requestAnimationFrame = nullthis.requestAnimationFrameDrawBind = this.requestAnimationFrameDraw.bind(this)}addTask(func) {this.taskList.push(func)if (this.requestAnimationFrame === null) {this.addRequestAnimationFrame()this.do()}}do() {this.status = 'do'new Promise(res => {this.taskList[0] && this.taskList[0]()this.taskList.shift()this.hasTaskDone = trueres()}).then(() => {if (this.status === 'do' && this.taskList.length) {this.do()}})}stop() {this.status = 'stop'}requestAnimationFrameDraw() {this.stop()if (this.hasTaskDone && this.reDraw) {this.hasTaskDone = falsethis.reDraw()}if (this.taskList.length) {this.addRequestAnimationFrame()this.do()} else {this.clearRequestAnimationFrame()}}addRequestAnimationFrame() {this.requestAnimationFrame = window.requestAnimationFrame(this.requestAnimationFrameDrawBind)}clearRequestAnimationFrame() {window.cancelAnimationFrame(this.requestAnimationFrame)this.requestAnimationFrame = null}removeEvent() {this.stop()this.clearRequestAnimationFrame()}}class Layout extends TaskQueue {constructor(opt) {super(opt)this.qIndex = opt.layout.qIndexthis.fStableL = opt.layout.fStableLthis.fIndex = opt.layout.fIndexthis.count = opt.layout.count || opt.nodes.length * Math.ceil(opt.nodes.length / 5)this.countForce = 0}doLayout() {this.countForce++if (this.countForce >= this.count) {return}// 计算开始this.forceComputed(this.arc, this.line)setTimeout(() => {this.addTask(() => {this.doLayout();})})}forceComputed(nodes) {nodes.forEach(item => {item.translateX = 0item.translateY = 0})nodes.forEach((curNode, index) => {// 库仑力计算for (let i = index + 1; i < nodes.length; i++) {let otherNode = nodes[i]if (otherNode) {this.computedXYByQ(curNode, otherNode)}}// 弹簧力计算if (curNode.fromArcs?.length) {curNode.fromArcs.forEach(id => {let fromNode = nodes.filter(node => {return node.id === id})[0]if (fromNode) {this.computedXYByK(curNode, fromNode)}})}// 中心拉力if (curNode.fromArcs?.length) {this.computedXYByK(curNode, {xy: {x: this.canvas.width / 2,y: this.canvas.height / 2}})}})// let maxTranslate = 1// nodes.forEach(item => {//     if(item.translateX && Math.abs(item.translateX) > maxTranslate){//         maxTranslate = Math.abs(item.translateX)//     }//     if(item.translateY && Math.abs(item.translateY) > maxTranslate){//         maxTranslate = Math.abs(item.translateY)//     }// })// nodes.forEach(item => {//     if(item.translateX){//         item.x += item.translateX / maxTranslate//     }//     if(item.translateY){//         item.y += item.translateY / maxTranslate//     }// })nodes.forEach(item => {if (item.translateX) {item.xy.x += item.translateX}if (item.translateY) {item.xy.y += item.translateY}})}computedXYByQ(node1, node2) {let x1 = node1.xy.xlet y1 = node1.xy.ylet x2 = node2.xy.xlet y2 = node2.xy.ylet xl = x2 - x1let yl = y2 - y1let angle = Math.PIif (!xl) {if (y2 > y1) {angle = -Math.PI / 2} else {angle = Math.PI / 2}} else if (!yl) {if (x2 > x1) {angle = 0} else {angle = Math.PI}} else {angle = Math.atan(yl / xl)}let r = Math.sqrt(Math.pow(xl, 2) + Math.pow(yl, 2))if (r < 1) {r = 1}// 库仑力 r越大,库仑力越小let node1Q = (node1.fromNodes?.length || 0) + (node1.toNodes?.length || 0) + 1let node2Q = (node2.fromNodes?.length || 0) + (node2.toNodes?.length || 0) + 1let f = this.qIndex * node1Q * node2Q / Math.pow(r, 2)let fx = f * Math.cos(angle)let fy = f * Math.sin(angle)node1.translateX = node1.translateXnode1.translateY = node1.translateYnode2.translateX = node2.translateXnode2.translateY = node2.translateY// node1.translateX -= fx// node2.translateX += fx// node1.translateY -= fy// node2.translateY += fyif (x2 > x1) {if (fx > 0) {node1.translateX -= fxnode2.translateX += fx} else {node1.translateX += fxnode2.translateX -= fx}} else {if (fx > 0) {node1.translateX += fxnode2.translateX -= fx} else {node1.translateX -= fxnode2.translateX += fx}}if (y2 > y1) {if (fy > 0) {node1.translateY -= fynode2.translateY += fy} else {node1.translateY += fynode2.translateY -= fy}} else {if (fy > 0) {node1.translateY += fynode2.translateY -= fy} else {node1.translateY -= fynode2.translateY += fy}}}computedXYByK(node1, node2) {let x1 = node1.xy.xlet y1 = node1.xy.ylet x2 = node2.xy.xlet y2 = node2.xy.ylet xl = x2 - x1let yl = y2 - y1let angle = Math.PIif (!xl) {if (y2 > y1) {angle = -Math.PI / 2} else {angle = Math.PI / 2}} else if (!yl) {if (x2 > x1) {angle = 0} else {angle = Math.PI}} else {angle = Math.atan(yl / xl)}let r = Math.sqrt(Math.pow(xl, 2) + Math.pow(yl, 2))if (r > this.fStableL * 2) {r = this.fStableL * 2} else if (r < 1) {r = 1}// 弹簧力let f = this.fIndex * (r - this.fStableL)let fx = f * Math.cos(angle)let fy = f * Math.sin(angle)node1.translateX = node1.translateXnode1.translateY = node1.translateYnode2.translateX = node2.translateXnode2.translateY = node2.translateYif (f > 0) {// 拉力if (x2 > x1) {if (fx > 0) {node1.translateX += fxnode2.translateX -= fx} else {node1.translateX -= fxnode2.translateX += fx}} else {if (fx > 0) {node1.translateX -= fxnode2.translateX += fx} else {node1.translateX += fxnode2.translateX -= fx}}if (y2 > y1) {if (fy > 0) {node1.translateY += fynode2.translateY -= fy} else {node1.translateY -= fynode2.translateY += fy}} else {if (fy > 0) {node1.translateY -= fynode2.translateY += fy} else {node1.translateY += fynode2.translateY -= fy}}} else {// 弹力if (x2 > x1) {if (fx > 0) {node1.translateX -= fxnode2.translateX += fx} else {node1.translateX += fxnode2.translateX -= fx}} else {if (fx > 0) {node1.translateX += fxnode2.translateX -= fx} else {node1.translateX -= fxnode2.translateX += fx}}if (y2 > y1) {if (fy > 0) {node1.translateY -= fynode2.translateY += fy} else {node1.translateY += fynode2.translateY -= fy}} else {if (fy > 0) {node1.translateY += fynode2.translateY -= fy} else {node1.translateY -= fynode2.translateY += fy}}}}}class View extends Layout {constructor(opt) {super(opt)this.canvas = opt.canvasthis.dpr = window.devicePixelRatio || 1this.nodes = opt.nodesthis.paths = opt.pathsthis.circleStyle = opt.circleStylethis.lineStyle = opt.lineStylethis.line = []this.arc = []this.init()}init() {if (!this.canvas) {return}if (this.canvas.width !== Math.floor(this.canvas.offsetWidth * this.dpr) || this.canvas.height !== Math.floor(this.canvas.offsetHeight * this.dpr)) {this.canvas.width = Math.floor(this.canvas.offsetWidth * this.dpr)this.canvas.height = Math.floor(this.canvas.offsetHeight * this.dpr)}this.ctx = this.canvas.getContext('2d')this.addData(this.nodes, this.paths)}addData(nodes, paths) {if (nodes && nodes.length) {this.addArc(nodes)}if (paths && paths.length) {this.addLine(paths)}super.countForce = 0super.doLayout()}addArc(nodes) {// 数据多时可以考虑将初始化随机坐标范围与数据量做等比函数nodes.forEach(node => {this.arc.push({id: node.id,fromArcs: [],toArcs: [],xy: {x: this.rand(0, this.canvas.width),y: this.rand(0, this.canvas.height)}})})}addLine(paths) {paths.forEach(path => {let fromArc = this.arc.filter(node => {return node.id === path.from})[0]let toArc = this.arc.filter(node => {return node.id === path.to})[0]fromArc.toArcs.push(toArc.id)toArc.fromArcs.push(fromArc.id)if (fromArc && toArc) {this.line.push({id: path.id,from: path.from,to: path.to,fromArc,toArc})}})}reDraw() {this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)this.draw()}draw() {this.line.forEach(item => {this.drawLine(item)})this.arc.forEach(item => {this.drawArc(item)})}drawLine(data) {this.ctx.save()this.ctx.translate(this.canvas.width / 2, this.canvas.height / 2)this.ctx.scale(this.scaleC, this.scaleC)this.ctx.translate(-this.canvas.width / 2, -this.canvas.height / 2)this.ctx.beginPath()this.ctx.lineWidth = this.lineStyle.widththis.ctx.strokeStyle = this.lineStyle.colorthis.ctx.moveTo(data.fromArc.xy.x, data.fromArc.xy.y)this.ctx.lineTo(data.toArc.xy.x, data.toArc.xy.y)this.ctx.stroke()this.ctx.closePath()this.ctx.restore()}drawArc(data) {this.ctx.save()this.ctx.translate(this.canvas.width / 2, this.canvas.height / 2)this.ctx.scale(this.scaleC, this.scaleC)this.ctx.translate(-this.canvas.width / 2, -this.canvas.height / 2)this.ctx.beginPath()this.ctx.fillStyle = this.circleStyle.backgroundthis.ctx.arc(data.xy.x, data.xy.y, this.circleStyle.r, 0, 2 * Math.PI)this.ctx.fill()this.ctx.closePath()this.ctx.restore()}rand = (n, m) => {var c = m - n + 1return Math.floor(Math.random() * c + n)}}// 测试数据let data = {"nodes": [{"id": "36"},{"id": "50"},{"id": "20077"},{"id": "1090"},{"id": "1078"},{"id": "10007"},{"id": "20039"},{"id": "1074"},{"id": "20058"},{"id": "1062"},{"id": "10001"},{"id": "20076"},{"id": "1089"},{"id": "20038"},{"id": "1068"},{"id": "20057"},{"id": "1081"},{"id": "20070"},{"id": "1034"},{"id": "1077"},{"id": "10002"},{"id": "10003"},{"id": "20069"},{"id": "1002"},{"id": "47"},{"id": "10010"},{"id": "14"},{"id": "42"},{"id": "94"},{"id": "16"},{"id": "41"},{"id": "64"},{"id": "20002"},{"id": "73"},{"id": "1001"},{"id": "10009"},{"id": "10008"},{"id": "10006"},{"id": "10005"},{"id": "10004"},{"id": "33"},{"id": "10"},{"id": "18"},{"id": "70"},{"id": "98"},{"id": "20"},{"id": "24"},{"id": "20001"}],"paths": [{"id": "606","from": "50","to": "36"},{"id": "346","from": "20077","to": "1090"},{"id": "343","from": "1078","to": "10007"},{"id": "382","from": "20039","to": "1074"},{"id": "419","from": "20058","to": "1062"},{"id": "344","from": "1078","to": "10001"},{"id": "356","from": "20076","to": "1089"},{"id": "439","from": "20038","to": "1068"},{"id": "417","from": "20057","to": "1081"},{"id": "358","from": "20070","to": "1078"},{"id": "438","from": "20038","to": "1034"},{"id": "248","from": "1077","to": "10002"},{"id": "249","from": "1077","to": "10003"},{"id": "364","from": "20069","to": "1077"},{"id": "4797","from": "1002","to": "10003"},{"id": "4787","from": "1002","to": "10002"},{"id": "223","from": "1002","to": "10003"},{"id": "222","from": "1002","to": "10002"},{"id": "2659","from": "1002","to": "47"},{"id": "4777","from": "1002","to": "10001"},{"id": "4867","from": "1002","to": "10010"},{"id": "1466","from": "14","to": "1002"},{"id": "1437","from": "42","to": "1002"},{"id": "1414","from": "94","to": "1002"},{"id": "1411","from": "16","to": "1002"},{"id": "1395","from": "16","to": "1002"},{"id": "1382","from": "41","to": "1002"},{"id": "1377","from": "64","to": "1002"},{"id": "436","from": "20002","to": "1002"},{"id": "2658","from": "73","to": "1002"},{"id": "4856","from": "1001","to": "10009"},{"id": "4846","from": "1001","to": "10008"},{"id": "4836","from": "1001","to": "10007"},{"id": "4826","from": "1001","to": "10006"},{"id": "4816","from": "1001","to": "10005"},{"id": "4806","from": "1001","to": "10004"},{"id": "4796","from": "1001","to": "10003"},{"id": "4786","from": "1001","to": "10002"},{"id": "4776","from": "1001","to": "10001"},{"id": "221","from": "1001","to": "10001"},{"id": "4866","from": "1001","to": "10010"},{"id": "1469","from": "33","to": "1001"},{"id": "1459","from": "10","to": "1001"},{"id": "1448","from": "18","to": "1001"},{"id": "1406","from": "70","to": "1001"},{"id": "1396","from": "47","to": "1001"},{"id": "1369","from": "98","to": "1001"},{"id": "1365","from": "20","to": "1001"},{"id": "1363","from": "24","to": "1001"},{"id": "406","from": "20001","to": "1001"}]}// canvas domconst canvas = document.getElementById('network');new View({canvas,nodes: data.nodes,paths: data.paths,circleStyle: {r: 10,background: '#FFFFFF'},lineStyle: {width: 1,color: '#FFFFFF'},layout: {qIndex: 2000, // 库仑力系数,值越大,库仑力越大fStableL: 80,fIndex: 0.1, // 拉力系数,数值越大,力越大}})
</script></html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/155538.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何避免 IDEA 每次重启都index

如何避免 IDEA 每次重启都index 在 IntelliJ IDEA 中&#xff0c;可以通过以下几个步骤来避免每次重启时索引&#xff1a; 打开 File -> Settings 菜单。在左侧的菜单栏中选择 “Appearance & Behavior” -> “System Settings” -> “Synchronization”。 在右…

力扣第501题 二叉树的众数 c++ (暴力 加 双指针优化)

题目 501. 二叉搜索树中的众数 简单 相关标签 树 深度优先搜索 二叉搜索树 二叉树 给你一个含重复值的二叉搜索树&#xff08;BST&#xff09;的根节点 root &#xff0c;找出并返回 BST 中的所有 众数&#xff08;即&#xff0c;出现频率最高的元素&#xff09;。 …

【Redis】Set集合相关的命令

目录 命令SADDSMEMBERSSISMEMBERSCARDSPOPSMOVESREMSINTERSINTERSTORESUNIONSUNIONSTORESDIFFSDIFFSTORE 命令 SADD 将⼀个或者多个元素添加到set中。注意&#xff0c;重复的元素⽆法添加到set中。 SADD key member [member ...]SMEMBERS 获取⼀个set中的所有元素&#xff0…

js事件循环详解

事件循环简介 JavaScript的事件循环是一种处理异步事件和回调函数的机制&#xff0c;它是在浏览器或Node.js环境中运行&#xff0c;用于管理任务队列和调用栈&#xff0c;以及在适当的时候执行回调函数。 事件循环的基本原理是&#xff0c;JavaScript引擎在空闲时等待事件的到…

苹果ios开发者ipa文件包内测人数签名真机数量满了应该怎么做?

苹果ios开发者ipa文件包内测人数签名真机数量满了应该怎么做&#xff1f; 有人总是问我开发者的设备满了怎么做才可以让设备增加&#xff1f;或者我要怎么做才能让员工的设备都可以安装&#xff0c;那么首先我们要做到的就是要知道我们的开发者都是拥有多少内测设备&#xff1f…

jupyter notebook如何实现代码提示功能?

jupyter notebook在数据分析中使用非常方便&#xff0c;但是没有代码提示功能&#xff0c;让人感觉有一点点遗憾&#xff1f;如何实现代码提示功能呢&#xff1f;以下实现亲测有效。 本人python版本是3.8. 首先关闭jupyter notebook&#xff0c;安装相关的库。 一、需要提前…

MongoDB——centOS7环境Mongodb权限管理(图解版)

目录 一、MongDB权限概述1.1、MongDB权限概述1.2、MongDB权限列表 二、Mongodb权限管理示例2.1、创建账号2.1.1、创建管理员用户2.1.2、开启认证2.1.3、创建普通账号 一、MongDB权限概述 1.1、MongDB权限概述 mongodb是没有默认管理员账号&#xff0c;所以要先添加管理员账号…

【Python 零基础入门】 函数

【Python 零基础入门】第五课 函数 【Python 零基础入门】第五课 函数函数在生活中的类比函数为什么要使用函数函数的格式无参函数含参函数 参数形参实参 变量作用域局部变量全局变量 递归函数基本的递归斐波那契数列 Lambda 表达式高阶函数map 函数filter 函数reduce 函数结合…

Node历史版本下载及配置npm镜像

https://nodejs.org/en/download/releases 点击对应版本Release,选择合适的包&#xff0c;进行下载安装。 配置国内镜像 npm config set registry https://registry.npmmirror.com/

Practical Memory Leak Detection using Guarded Value-Flow Analysis 论文阅读

本文于 2007 年投稿于 ACM-SIGPLAN 会议1。 概述 指针在代码编写过程中可能出现以下两种问题&#xff1a; 存在一条执行路径&#xff0c;指针未成功释放&#xff08;内存泄漏&#xff09;&#xff0c;如下面代码中注释部分所表明的&#xff1a; int foo() {int *p malloc(4 …

上海亚商投顾:沪指冲高回落 医药、芯片股全天领涨

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指昨日小幅反弹&#xff0c;创业板指盘中涨超1.6%&#xff0c;午后涨幅有所收窄。医药医疗股全线走强&#…

LLM - 旋转位置编码 RoPE 代码详解

目录 一.引言 二.RoPE 理论 1.RoPE 矩阵形式 2.RoPE 图例形式 3.RoPE 实践分析 三.RoPE 代码分析 1.源码获取 2.源码分析 3.rotary_emb 3.1 __init__ 3.2 forward 4.apply_rotary_pos_emb 4.1 rotate_half 4.2 apply_rotary_pos_emb 四.RoPE 代码实现 1.Q/K/V …

飞桨大模型套件:一站式体验,性能极致,生态兼容

在Wave Summit 2023深度学习开发者大会上&#xff0c;来自百度的资深研发工程师贺思俊和王冠中带来的分享主题是&#xff1a;飞桨大模型套件&#xff0c;一站式体验&#xff0c;性能极致&#xff0c;生态兼容。 大语言模型套件PaddleNLP 众所周知PaddleNLP并不是一个全新的模型…

腾讯云轻量2核4G5M可容纳多少人访问?

腾讯云2核4G5M服务器支持多少人在线访问&#xff1f;卡不卡&#xff1f;腾讯云轻量2核4G5M带宽服务器支持多少人在线访问&#xff1f;5M带宽下载速度峰值可达640KB/秒&#xff0c;阿腾云以搭建网站为例&#xff0c;假设优化后平均大小为60KB&#xff0c;则5M带宽可支撑10个用户…

ad5665r STM32 GD32 IIC驱动设计

本文涉及文档工程代码&#xff0c;下载地址如下 ad5665rSTM32GD32IIC驱动设计,驱动程序在AD公司提供例程上修改得到,IO模拟的方式进行IIC通信资源-CSDN文库 硬件设计 MCU采用STM32或者GD32,GD32基本上和STM32一样,针对ad566r的IIC时序操作是完全相同的. 原理图设计如下 与MC…

matlab绘制尖角colorbar

Matlab代码 cmap [69 117 180116 173 203171 217 233254 224 144253 174 77244 109 67215 48 39165 0 38]/255; %画图的部分代码 figure set(gcf,outerposition,get(0,screensize)) ax axes(Position,[0.2 0.2 0.6 0.6]); % pos需要自己设置位置 h colorbar; % colormap(ax…

bash上下键选择选项demo脚本

效果如下&#xff1a; 废话不多说&#xff0c;上代码&#xff1a; #!/bin/bashoptions("111" "222" "333" "444") # 选项列表 options_index0 # 默认选中第一个选项 options_len${#options[]}echo "请用上下方向键进行选择&am…

Windows11下清理Docker Desktop与wsl的C盘空间占用

一、清理Docker Desktop的磁盘占用 //【查看docker 占用的空间】 docker system dfTYPE 列出了docker 使用磁盘的 4 种类型&#xff1a; Images&#xff1a;所有镜像占用的空间&#xff0c;包括拉取下来的镜像&#xff0c;和本地构建的。Containers&#xff1a;运行的容器占用…

华为云云耀云服务器L实例评测 | 实例使用教学之综合导览

华为云云耀云服务器L实例评测 &#xff5c; 实例使用教学之综合导览 实例使用教学实例场景体验实例性能评测实例评测使用介绍华为云云耀云服务器 华为云云耀云服务器 &#xff08;目前已经全新升级为 华为云云耀云服务器L实例&#xff09; 华为云云耀云服务器是什么华为云云耀云…

k8s 集群部署 kubesphere

一、最小化部署 kubesphere 1、在已有的 Kubernetes 集群上部署 KubeSphere&#xff0c;下载 YAML 文件: wget https://github.com/kubesphere/ks-installer/releases/download/v3.4.0/kubesphere-installer.yaml wget https://github.com/kubesphere/ks-installer/releases/…