在节点中与画布绘图集成

时间:2015-07-12 16:01:29

标签: d3.js

我尝试绘制图表/网络,其中节点内容应填充第三方库。到目前为止,我能够使用容器()为d3绘制图形,以便稍后填充每个节点。我的问题是,当提到绘图时,容器似乎还不存在。添加onload事件也不起作用,但使用onclick事件表明所有内容都已到位,并且很可能在实际创建DOM元素之前开始绘制。

在开始分配内容之前,确保创建d3生成的DOM元素的最佳做法是什么?我使用的第三方JS库只需要div id来绘制内容。

从Neo4j返回的节点和关系数组中,我构建了一个带有d3的图形,如下所示。要在节点中显示的图像作为Base64字符串存储在neo4j数据库中。

        var width = 500, height = 500;

        var div = d3.select("body").append("div")   
            .attr("class", "tooltip")
            .style("opacity", 0);

        var col = obj.columns;
        var data = obj.data;
        var nodes = obj.data[0][0].nodes;
        var nodeMap = {};
        nodes.forEach(function(x) { nodeMap[x.label] = x; });

        var edges = obj.data[0][0].edges;

        var links = edges.map(function(x) {
           return { source: nodeMap[x.source], target: nodeMap[x.target], value: x.value };
        });

        var svg = d3.select("#graph").append("svg")
            .attr("width", width)
            .attr("height", height)
            .attr("pointer-events", "all")
            .append('g')
            .call(d3.behavior.zoom().on("zoom", redraw))
            .append('g');

        var force = d3.layout.force()
            .gravity(.3)
            .distance(150)
            .charge(-4000)
            .size([width, height]);

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

        force
            .nodes(nodes)
            .links(links)
            .friction(0.8)
            .start();

        var link = svg.selectAll(".link")
            .data(links)
            .enter().append("line")
            .attr("class", "link");

        var node = svg.selectAll(".node")
            .data(nodes)
            .enter().append("svg:g")
            .attr("class", "node")
            .on("dblclick", dblclick)
            .call(force.drag);

        node.append("circle")
            .attr("r", 50);

        node.append("image")
            // display structure in nodes
            .attr("xlink:href", function(d){
                  if (d.imgB64) {
                    return 'data:image/png;base64, ' + d.imgB64 ;
                  }
                })
            .attr("x", -40)
            .attr("y", -40)
            .attr("width", 80)
            .attr("height", 80);

        force.on("tick", function() {
            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("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
        });

该代码工作正常。因此,我尝试做的是用第三方库生成的画布图替换Base64图像。

enter image description here

此库的工作原理如下:

var myCanvas = newCanvasViewer(id, data);

该函数知道如何从给定数据生成图像并将结果放入DOM元素(如果元素已经存在于DOM中),或者直接在DOM中生成新的canvas元素,但不幸的是擦掉任何其他元素现有的DOM元素。

所以,我更改了d3以上的代码,用以下代码替换了node.append(' image')块:

node.append("canvas")
    .attr("id", function(d) {
          var cnv = newCanvasViewer(d.name, d.data);
          return d.name;
    });

这显然不起作用,画布对象显示但不在节点中,可能是因为DOM元素在调用newCanvasViewer时还不存在。此外,d3图被覆盖。 设置调用newCanvasViewer的onclick函数时,图形会在点击时显示在节点内。

node.append("circle")
    .attr("r", 50)
    .attr("onclick", function(d) {
          return "newCanvasViewer('"+d.name+"', '"+d.data+"')";
    });

node.append("canvas")
    .attr("id", function(d) {
          return d.name;
    });

由于我希望每个节点从头开始显示它的画布,我想知道何时调用newCanvasViewer函数?我猜每个节点上的oncreate函数会成功,但不存在。 回叫功能会起作用吗? d3完成整个网络绘图后,我应该调用它吗?

为了更全面,这里是带有回调函数的HTML和javascript代码,一旦d3 d3完成绘图,就会尝试绘制画布内容。我仍然卡住了,画布样本实际上已显示,但节点中没有显示画布内容,可能是由于生成画布容器和使用它们之间的时间安排。

HTML:

<body onload="init(); cypherQuery()">
    <div id="graph" align="left" valign="top"></div>
    <div id="sample">
        <canvas id="mol" style="width: 160px; height: 160px;"></canvas>
    </div>
</body>

Javascript(在HTML标题中):     var xmlhttp;     function init(){         xmlhttp = new XMLHttpRequest();     }

function cypherQuery() {
    // perform neo4j query, extract JSON and call the network drawing (d3).
    // ...
    var obj = JSON.parse(xmlhttp.responseText);
    drawGraph(obj, drawCanvas);

    // this is just to show that canvasViewer does the 
    // job if the <canvas id='...'> element exists in DOM 
    var nodes = obj.data[0][0].nodes;
    var cnv = newCanvasViewer("sample", nodes[0].data);
}

function drawGraph(obj, drawCanvas) {
    // see code above from... 
    var width = 500, height = 500;
    // ... to 
    force.on("tick", function() {
        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("transform", function(d) { 
             return "translate(" + d.x + "," + d.y + ")"; 
        });
    }); 
    function redraw() {
        svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
        // node.attr("font-size", (nodeFontSize / d3.event.scale) + "px");
    }

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

    if (callback && typeof(callback) === "function") {
        callback(nodes);
    }
}

0 个答案:

没有答案