• d3js 各种力的仿真


    d3js 各种力的仿真

    d3js 是一种可以自由组合图形的库

    我的理解,简单图形,比如柱形图,饼状图,条形图,要求不高可以直接用 echarts,自由度需要高些的使用 d3js,需要频繁交互的使用 pixi

    偶然发现 d3js 可以进行各种力的模拟,学习后进行分析,希望大家能点个赞,让我有更多动力。点赞过十,下星期分享一篇 d3js 入门教程

    效果如下

    演示1

    演示2

    1. 创建数据

    首先创建两组 JSON 数据,这里我使用的是网上的数据。一组数据记录了每个点的信息,另一组数据记录了连接信息。

    // 结点信息的文件 node_data.json
    [
        {"x": 30.5, "y": 100.7, "r": 4},
        ...
    ]
        
    // 连接信息的文件 edge_data.json
    [
        {"source": 0, "target": 98},
        ...
    ]
    

    2. 代码结构

    力的模拟分为四个步骤

    1. 加载数据
    2. 创建仿真变量
    3. 绑定数据并画出图形
    4. 在帧回调函数中更新图形
    // step 1
    const nodes = await d3.json("/force/node_data.json");
    
    // step 2
    const sim = d3.forceSimulation(nodes)
    	.force('力的名字', ...)
        .on('tick', tick_function);
    
    // step3
    const node = svg.selectAll("circle")
                .data(nodes)
                .enter()
                .append('circle');
    
    // step4
    tick_function(){
        // 更新图形
    }
    

    3. 位置x和y的力

    特定位置x,y有中拉力,将其他图形拉向自己,比如让特定位置为画框中心

    const sim = d3.forceSimulation(nodes)
        // 指定位置的拉力
        .force('x', d3.forceX(width/2))
        .force('y', d3.forceY(height/2));
    

    位置力1

    可以设置力的大小,力如果越大,那么拉力越强,越小越弱

    const sim = d3.forceSimulation(nodes)
        // 指定位置的拉力
        .force('x', d3.forceX(width/2).strength(0.06))
        .force('y', d3.forceY(height/2).strength(0.06))
    

    位置力2

    4. 碰撞力

    如果不想让所有图形重合,那么就需要使用碰撞力,碰撞力以坐标为圆心,设置碰撞半径,碰撞半径内不会发生重合。比如让位置力和碰撞力结合

    const sim = d3.forceSimulation(nodes)
        // 指定位置的拉力
        .force('x', d3.forceX(width/2).strength(0.06))
        .force('y', d3.forceY(height/2).strength(0.06))
        // 碰撞力, radius(碰撞半径)
        .force('collide', d3.forceCollide().radius(10))
    

    碰撞力

    我们会发现有一些图形会有重合部分,这是因为我们设置了固定半径,有些图形的半径要大于固定半径,因此我们需要动态设置半径

    const sim = d3.forceSimulation(nodes)
        // 指定位置的拉力
        .force('x', d3.forceX(width/2).strength(0.06))
        .force('y', d3.forceY(height/2).strength(0.06))
        // 碰撞力, radius(碰撞半径)
        .force('collide', d3.forceCollide().radius(d=>d.r+1))
    

    碰撞力1

    5. 原子力

    这里是我自己的叫法,实际上这个力叫 many-body force,表示每个图形自身存在对其他图形的力,这个力如果是正数,代表了吸引力,如果这个力是负数,代表了排斥力

    const sim = d3.forceSimulation(nodes)
        // 原子力,正数吸引力
        .force('charge', d3.forceManyBody().strength(7))
    

    元素力

    看下排除力

    const sim = d3.forceSimulation(nodes)
        // 原子力,正数吸引力
        .force('charge', d3.forceManyBody().strength(-7))
    

    元素力1

    当原子力为吸引力时,所有图形聚合起来了,不好看,加上碰撞力再看看效果

    const sim = d3.forceSimulation(nodes)
        // 碰撞力, radius(碰撞半径)
        .force('collide', d3.forceCollide().radius(d=>d.r+1))
        // 原子力,正数吸引力
        .force('charge', d3.forceManyBody().strength(7))
    

    元素力2

    看起来效果和位置力类似,但是核心内容完全不同,原子力代表每个图形自身都有对其他图形的力,而位置力是一个点对其他图形的吸引力

    6. 链接力

    表示连接线产生的力,类似于弹簧,远了就拉回来,近了就推出去

    const sim = d3.forceSimulation(nodes)
        // 链接力
        .force('link', d3.forceLink(edges))
    

    链接力

    还是给链接力加个碰撞力,再加上刚刚了解的原子力看看效果

    const sim = d3.forceSimulation(nodes)
        .force('collide', d3.forceCollide().radius(d=>d.r+1))
        // 原子力
        .force('charge', d3.forceManyBody().strength(-7))
        // 链接力
        .force('link', d3.forceLink(edges))
    

    链接力1

    有点鲜花盛开的感觉是不

    7. 径向力

    我们可以定义一个圆,这个圆上的每一点都会有个从圆心指向该点的力,会让所有图形集中再圆上

    const sim = d3.forceSimulation(nodes)
    	// 径向力
        .force('radial', d3.forceRadial(240, width/2, height/2))
    

    径向力

    径向力让所有图形在一个圆上并且图形直接由重合,不太美观,加其他力试试

    const sim = d3.forceSimulation(nodes)
       	// 碰撞力
        .force('collide', d3.forceCollide().radius(d=>d.r+1))
        // 原子力
        .force('charge', d3.forceManyBody().strength(-7))
        // 径向力
        // .force('radial', d3.forceRadial(240, width/2, height/2))
        .force('radial', d3.forceRadial(d => d.r+200, width/2, height/2))
    

    径向力1

    将径向力半径改为动态生成,加入碰撞力和原子力后是不是就好看多了

    8. 中心力

    中心力也是定义了一个圆,这个力有让图形移动到该圆内的趋势,这个力要与其他力配合使用

    const sim = d3.forceSimulation(nodes)
        // 碰撞力
        .force('collide', d3.forceCollide().radius(d=>d.r+1))
        // 原子力
        .force('charge', d3.forceManyBody().strength(7))
        // 中心力,规划
        .force('center', d3.forceCenter(centerForce.x, centerForce.y))
    

    中心力

    9. 代码解析

    <!DOCTYPE html>
    <html lang="zh">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <script src="/lib/d3.v7.min.js"></script>
    <script type="module">
        const width=1000, height = 500;
        const svg = d3.select("body")
            .append("svg").attr('width', width).attr('height', height);
        
        // 选取颜色色系
        const color = d3.scaleOrdinal(d3.schemeTableau10)
    
        async function load(){
            // 加载数据
            const nodes = await d3.json("/force/node_data.json");
            const edges = await d3.json("/force/edge_data.json");
            
            // 绑定数据并形成圆
            const node = svg.selectAll("circle")
                .data(nodes)
                .enter()
                .append('circle')
                .attr("cx", d=>d.x)
                .attr("cy", d=>d.y)
                .attr("r", d=>d.r)
                .attr("fill", (d, i)=>color(i));
            
    		// 绑定数据并形成线
            const lines = svg.selectAll('lines')
                .data(edges)
                .enter()
                .append('line')
                .attr('x1',d=>nodes[d.source].x)
                .attr('x2',d=>nodes[d.target].x)
                .attr('y1',d=>nodes[d.source].y)
                .attr('y2',d=>nodes[d.target].y)
                .attr('stroke', "#5d5d66")
                .attr('stroke-width', 2)
    
            const centerForce = {
                x: 700,
                y: 250
            }
    
            // 创建中心力的圆
            svg.append('circle')
                .attr('cx', centerForce.x)
                .attr('cy', centerForce.y)
                .attr('r', 100)
                .attr('stroke', '#6b6f80')
                .attr('stroke-width', 3)
                .attr('fill', "#00000000")
    
            // 力的模拟
            const sim = d3.forceSimulation(nodes)
                // 指定位置的拉力
                // .force('x', d3.forceX(width/2))
                // .force('y', d3.forceY(height/2))
                // .force('x', d3.forceX(width/2).strength(0.06))
                // .force('y', d3.forceY(height/2).strength(0.06))
                // 碰撞力
                // .force('collide', d3.forceCollide().radius(10))
                .force('collide', d3.forceCollide().radius(d=>d.r+1))
                // 原子力
                .force('charge', d3.forceManyBody().strength(7))
                // 链接力
                // .force('link', d3.forceLink(edges))
                // 径向力
                // .force('radial', d3.forceRadial(240, width/2, height/2))
                // .force('radial', d3.forceRadial(d => d.r+200, width/2, height/2))
                // 中心力,规划
                .force('center', d3.forceCenter(centerForce.x, centerForce.y))
            // .stop()
            //  .tick(100);
    
    
            sim.on("tick", () => {
                // sim 直接改变了
                node.attr("cx", function(d){ return d.x;})
                    .attr("cy", function(d){ return d.y;});
    
    
                lines.attr('x1',d=>d.source.x)
                    .attr('y1',d=>d.source.y)
                    .attr('x2',d=>d.target.x)
                    .attr('y2',d=>d.target.y);
            })
        }
        load()
    </script>
    </body>
    </html>
    

    如果觉得不错的话,给个赞,有赞才能有动力呀,过十赞下星期写 d3js 入门教程

    希望读者在看完后能提出意见, 点个赞, 鼓励一下, 我们一起进步. 加油 !!
  • 相关阅读:
    JVM调优总结(四)-垃圾回收面临的问题
    JVM调优总结(三)-基本垃圾回收算法
    JVM调优总结(二)-一些概念
    Java8 Lambda表达式教程
    Java8 Lambda表达式教程
    Java8 Lambda表达式教程
    JVM调优总结(一)-- 一些概念
    Hibernate 3中如何获得库表所有字段的名称
    easyUI-datagrid带有工具栏和分页器的数据网格
    easyui-tabs
  • 原文地址:https://www.cnblogs.com/xiaxiangx/p/15730973.html
Copyright © 2020-2023  润新知