动态添加网络拓扑节点、svg下载

一、效果
这里写图片描述

二、代码

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>拓扑图</title>
    <style>

        .link {

        }

        .node {
          cursor: move;
        }

        .node.fixed {
          fill: #000;
        }

        .dialog {
            display: none;
            position: absolute;
            top: 0;
            padding: 1em;
            font-size: 14px;
            border-radius: 0.5em;
            box-shadow: 0 0 20px #ccc;
            background: #fff;
            z-index: 10;
        }

    </style>
</head>
<body>
    <div><button id="download">下载</button></div>
</body>
</html>

js
“d3版本”: “^3.5.17”


import 'd3'
import $ from 'jquery'

let graph = {
    "nodes": [
        {"x": 469, "y": 410},
        {"x": 493, "y": 364},
        {"x": 442, "y": 365},
        {"x": 467, "y": 314},
        {"x": 477, "y": 248},
        {"x": 425, "y": 207},
        {"x": 402, "y": 155},
        {"x": 369, "y": 196},
        {"x": 350, "y": 148},
        {"x": 539, "y": 222},
        {"x": 594, "y": 235},
        {"x": 582, "y": 185},
        {"x": 633, "y": 200}
    ],
    "links": [
        {"source":  0, "target":  1},
        {"source":  1, "target":  2},
        {"source":  2, "target":  0},
        {"source":  1, "target":  3},
        {"source":  3, "target":  2},
        {"source":  3, "target":  4},
        {"source":  4, "target":  5},
        {"source":  5, "target":  6},
        {"source":  5, "target":  7},
        {"source":  6, "target":  7},
        {"source":  6, "target":  8},
        {"source":  7, "target":  8},
        {"source":  9, "target":  4},
        {"source":  9, "target": 11},
        {"source":  9, "target": 10},
        {"source": 10, "target": 11},
        {"source": 11, "target": 12},
        {"source": 12, "target": 10}
    ]
};

document.oncontextmenu = function(){
  return false;
}

let width = 960,
    height = 500,
    dialog = Dialog();

let force = d3.layout.force()
    .size([width, height])
    .nodes(graph.nodes)
    .links(graph.links)
    .charge(-700)
    .linkDistance(40)
    .on("tick", tick);

let drag = force.drag()
    .on("dragstart", dragstart);

let svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

let link = svg.selectAll(".link"),
    node = svg.selectAll(".node");

let nodes = force.nodes(),
    links = force.links();

restart(12);

let button = document.getElementById("download");
button.onclick = downLoad;

function tick() {
  link.attr("x1", function(d) { return d.source.x; })
      .attr("y1", function(d) { return d.source.y; })
      .attr("x2", function(d) { return d.target.x; })
      .attr("y2", function(d) { return d.target.y; });

  node.attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; });
}

function dblclick(d) {
  //d3.select(this).classed("fixed", d.fixed = false);
}

function dragstart(d) {
  //d3.select(this).classed("fixed", d.fixed = true);
}

//单例对话框
function Dialog(){
    let dialog = null;
    return (function(){
        if(dialog == null){
            let bodyEle = document.querySelector("body"),
            dialogHtml = `<div>
                <label>创建节点大小:</label>
                <input type="number" value="12" />
            </div>
            <input name="enter" type="button" value="确定" />
            <input name="cancel" type="button" value="取消" />
            `;
            dialog = document.createElement("div");
            dialog.className = "dialog";
            dialog.innerHTML = dialogHtml;
            bodyEle.appendChild(dialog);
        }

        return dialog;
    })();  
}

function mouseRtClick(d,i){
    if(d3.event.button){
        dialog.style.left = d3.event.x+20+'px';
        dialog.style.top = d3.event.y+20+'px';
        dialog.style.display = 'block';
        dialog.querySelector("input[name='enter']").onclick = function(e){
            let node = {x: parseFloat(this.parentNode.style.left)-20, y: parseFloat(this.parentNode.style.top)-20};
            nodes.push(node);
            // add links to any nearby nodes
            links.push({source: node, target: d});
            restart(dialog.querySelector("input[type='number']").value);
            this.parentNode.style.display = 'none';
        };

        dialog.querySelector("input[name='cancel']").onclick = function(e){
            this.parentNode.style.display = 'none';
        };
    }
}


function downLoad(){
    $("svg")
        .attr("version", 1.1)  
        .attr("xmlns", "http://www.w3.org/2000/svg");

    let svg = document.querySelector("svg"),
        svgXml = svg.outerHTML,
        image = new Image(),
        canvas,
        context,
        a;

    //给图片对象写入base64编码的svg流
    image.src = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(svgXml)));
    image.onload = function(){
        //准备空画布
        canvas = document.createElement('canvas');
        canvas.width = svg.width.animVal.value;//svg宽度
        canvas.height = svg.height.animVal.value;//svg高度
        context = canvas.getContext('2d');  //取得画布的2d绘图上下文

        //设置图片颜色,避免下载图片为透明的背景
        context.fillStyle   = "#fff";
        context.fillOpacity = "0";
        context.fillRect(0,0,canvas.width,canvas.height);

        context.drawImage(image, 0, 0);
        a = document.createElement('a');
        a.href = canvas.toDataURL('image/png');  //将画布内的信息导出为png图片数据
        a.download = "网络拓扑.png";  //设定下载名称
        a.click(); //点击触发下载
    }


}

function restart(r) {

    link = link.data(links);
    link.enter().insert("line",".node")//使line放到circle前面
        .attr("class", "link")
        .style("stroke","#000")
        .style("stroke-width",'1.5');

    node = node.data(nodes);
    node.enter().append("circle")
        .attr("class", "node")
        .attr("r", r)
        .style("fill",'#ccc')
        .style("stroke","#000")
        .style("stroke-width",'1.5')
        .on("dblclick", dblclick)
        .on("mousedown",mouseRtClick)
        .call(drag);

    force.start();

}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wl_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值