如何在Infovis Toolkit中制作自定义节点?

时间:2011-09-02 17:43:37

标签: javascript infovis

我一直在研究Infovis工具包项目,虽然所有功能都已完成,但我还是无法完成视觉效果。 Infovis工具包API文档很好,但我的自定义节点类型不起作用。我正在使用高速,我想制作两种不同的自定义节点类型。一个来自图像,另一个来自绘制路径。非常感谢所有帮助,谢谢!

编辑:[我尝试的解决方案结果并不那么方便。相反,我使用来自JIT控制器的onCreateLabel()来自定义HTML节点。看到了性能的明显改善,并在定制节点方面获得了更大的灵活性。 ]

这是我到目前为止所提出的:

$jit.Hypertree.Plot.NodeTypes.implement({  
    'customNode': {  
         'render': function(node, canvas) {
            var img = new Image();
            img.src = "../icon.png";
            var pos = node.pos.getc(true);
            var ctx = canvas.getCtx();

            ctx.drawImage(img, pos.x-15, pos.y-15);                 
            /*
            //...And an other one like this but drawn as a path
            ctx.beginPath();
            ctx.moveTo(pos.x-25, pos.y-15);
            ctx.lineTo(25, -15);
            ctx.lineTo(-35, 0);
            ctx.closePath();
            ctx.strokeStyle = "#fff";
            ctx.fillStyle = "#bf5fa4";
            ctx.fill();
            ctx.stroke();*/
        }
       }
});

4 个答案:

答案 0 :(得分:7)

因为您正在将图像src设置为文件URL,所以加载文件需要一些时间。因此代码在图像加载之前就已经进入了drawImage调用。

您可以通过修改代码来在图像的onload事件处理程序中运行drawImage调用(在图像加载完成后运行)来解决此问题。

$jit.Hypertree.Plot.NodeTypes.implement({  
    'customNode': {  
        'render': function (node, canvas) {
            var img = new Image(),
                pos = node.pos.getc(true),
                ctx = canvas.getCtx();

            img.onload = function () {
                ctx.drawImage(img, pos.x - 15, pos.y - 15);
            };

            img.src = '../icon.png';
        }
    }
});

答案 1 :(得分:3)

上面给出的答案是正确的。然而,它有两个缺点。

1:没有包含方法,自定义节点不会触发onMouseEnter或onClick等事件。

2:每次移动图形中的任何节点或平移整个图形时,图像都会闪烁。这是因为每次调用渲染函数时都会加载图像以重新绘制节点。由于同样的原因,这也会影响性能。更好的选择是为具有“图像”类型的所有节点加载一次图像,并在渲染功能中重新绘制它们。这将有助于提高性能并防止图像闪烁。

我们可以有一个'loadImages'函数,它将每个节点中的图像加载为'type'作为'image'。每个类型为“image”的节点也应该有一个名为“url”的属性,该属性是要加载的图像的URL。

json数据中此类节点的“数据”部分如下所示。

"data": {
      "$color": "#EBB056",
      "$type": "image",
      "$dim": 7,
      "$url":"magnify.png"  // url of the image
    }

应该在loadJson函数之后立即调用loadImages函数,即在加载json数据之后立即调用。

// load JSON data.
fd.loadJSON(json);

//load images in node
loadImages();

以下是loadImages函数的实现。

function loadImages(){
    fd.graph.eachNode( function(node){
        if( node.getData('type') == 'image' ){
            var img = new Image();
            img.addEventListener('load', function(){
                node.setData('image',img); // store this image object in node
            }, false);
            img.src=node.getData('url');
        }
    });
}

最后,您的自定义节点实现如下所示。

$jit.ForceDirected.Plot.NodeTypes.implement({
'image': { 
         'render': function(node, canvas){
                var ctx = canvas.getCtx(); 
                var pos = node.pos.getc(true);
                if( node.getData('image') != 0 ){
                var img = node.getData('image');
                ctx.drawImage( img, pos.x-15, pos.y-15);
                }
            }, 
            'contains': function(node,pos){ 
                var npos = node.pos.getc(true); 
                dim = node.getData('dim'); 
                return this.nodeHelper.circle.contains(npos, pos, dim); 
            } 
}
});

答案 2 :(得分:1)

Pratik给出的答案并不适用于我的超文本 - 我需要进行一些小的修改才能使节点图像出现并在高速表示中适当缩放。希望这个答案可以帮助其他用户遇到类似的实施问题。 免责声明我是一名新手Javascript程序员,很可能我错过了Pratik的答案中必不可少的内容,这些内容可以使其发挥作用。而且,如果没有他的回答,我永远不会接近,为此我感激不尽。

json中的数据定义没有变化 - 按预期工作。对我来说,一个关键的注意事项是确保设置overridable: true,例如

var ht = new $jit.Hypertree({ 
  Node: {
      overridable: true,
      dim: 9,
      color: "#ffffff",
      type: "square"
    },

我需要图像高度/宽度以便稍后适当缩放它们。我在Pratik提供的loadImages函数中实现了它,所以它只运行一次:

function loadImages(){
    ht.graph.eachNode( function(node){
        if( node.getData('type') == 'image' ){
            var img = new Image();
            img.addEventListener('load', function(){
                node.setData('image',img); // store this image object in node
                node.setData('imageheight',this.height); 
                node.setData('imagewidth',this.width);
            }, false);
            img.src=node.getData('url');
        }
    });
}

在loadJSON之后,没有改变loadImages()函数调用的时间。

我的自定义节点如下所示:

$jit.Hypertree.Plot.NodeTypes.implement({
    'image': { 
             'render': function(node, canvas){
                    var ctx = canvas.getCtx(); 
                    var pos = node.pos.getc().$scale(node.scale); 
                    var nconfig = this.node; 
                    if( node.getData('image') != 0 ){ 
                        var img = node.getData('image');
                        var dim = node.getData('dim');
                        var w = node.getData('imagewidth');
                        var h = node.getData('imageheight');
                        var r = 50/h; // Scaling factor to scale images to 50px height
                        w = r * w;
                        h = r * h;
                        var dist = Math.sqrt( (pos.x*pos.x)+(pos.y*pos.y)); //dist to origin
                        var scale = 1 - (dist/node.scale);
                        // scale nodes based on distance to origin
                        var sw = nconfig.transform ? w * scale : w/dim/2;
                        var sh = nconfig.transform ? h * scale : h/dim/2;
                        if (node._depth < 2) { 
                            ctx.drawImage(img, 
                                pos.x - sh/2, //center images on nodes
                                pos.y - sw/2, 
                                sw, 
                                sh);
                        }  
                    }  
              },  
              'contains': function(node,pos){ 
                    var npos = node.pos.getc().$scale(node.scale);
                    var h = node.getData('imageheight');
                    var w = node.getData('imagewidth');
                    return this.nodeHelper.rectangle.contains(npos, pos, w, h); 
              }
          }  
    });

唯一突出的问题是,在我点击图表之后才会出现图像。我认为这与img.addEventListener('load', function(){有关,但未能找出导致问题的原因。任何想法都表示赞赏。

答案 3 :(得分:0)

我不知道我在做什么与众不同,或者自恐龙时代以来图书馆是否发生了变化。但是无论出于何种原因,getPos()node.pos.getc(true)给出的都是基于数学的坐标系,而不是使用HyperTree时所需的实际像素。

在使用图片时,尤其是在使用ctx.drawImage时,JavaScript需要基于像素的坐标。我不是数学天才,所以花了一段时间才弄清楚问题是什么以及如何解决。我发现的唯一解决方法是将node.pos.getc(true)给出的值与canvas.width相乘,这导致我执行了以下操作:

$jit.Hypertree.Plot.NodeTypes.implement({  
    'customNode': {
        'render': function (node, canvas) {
            let img = new Image(),
                pos = node.pos.getc(true),
                ctx = canvas.getCtx();

            img.onload = function () {
                ctx.drawImage(img, pos.x*canvas.Width -(imageWidth/2), pos.y*canvas.Height -(imageHeight/2))
            };

            img.src = '/resources/images/customImage.png';
        }
    }
});

应将图像居中放置在预期位置。