Javascript 如何在Infovis Toolkit中创建自定义节点?

Javascript 如何在Infovis Toolkit中创建自定义节点?,javascript,infovis,Javascript,Infovis,我一直在做一个Infovis工具包项目,虽然所有功能都完成了,但我还没能完成视觉效果。Infovis toolkit API文档很好,但我的自定义节点类型不起作用。我正在使用一个超树,我想创建两种不同的自定义节点类型。一个来自图像,另一个作为绘制的路径。非常感谢您的帮助,谢谢 EDIT:[我尝试的解决方案没有那么方便。相反,我使用JIT控制器中的onCreateLabel()使用HTML自定义节点。性能明显提高,自定义节点的灵活性大大提高。] 这就是我到目前为止的想法: $jit.Hypertr

我一直在做一个Infovis工具包项目,虽然所有功能都完成了,但我还没能完成视觉效果。Infovis toolkit API文档很好,但我的自定义节点类型不起作用。我正在使用一个超树,我想创建两种不同的自定义节点类型。一个来自图像,另一个作为绘制的路径。非常感谢您的帮助,谢谢

EDIT:[我尝试的解决方案没有那么方便。相反,我使用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();*/
        }
       }
});

因为要将图像src设置为文件URL,所以加载文件需要时间。因此,在加载图像之前,代码正在执行
drawImage
调用

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


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

1:它没有contains方法,没有contains方法,自定义节点将不会触发onMouseEnter或onClick等事件

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

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

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

"data": {
      "$color": "#EBB056",
      "$type": "image",
      "$dim": 7,
      "$url":"magnify.png"  // url of the image
    }
loadImages函数应该在loadJson函数之后调用,即在加载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); 
            } 
}
});

Pratik给出的答案对我的超树不太适用——我需要做一些小的修改,以使节点图像在超树表示中显示并适当缩放。希望这个答案能帮助其他有类似实现问题的用户。免责声明我是一名Javascript程序员新手,很可能我在Pratik的回答中遗漏了一些可以让它工作的重要内容。此外,如果没有他的回答,我永远不会接近他,对此我很感激

对json中的数据定义没有任何更改-按预期工作。me的一个关键注意事项是确保设置
可重写: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');
        }
    });
}
loadImages()函数调用的计时在loadJSON之后没有更改

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

$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); 
              }
          }  
    });
$jit.Hypertree.Plot.NodeType.implement({
'图像':{
“渲染”:函数(节点、画布){
var ctx=canvas.getCtx();
var pos=node.pos.getc().$scale(node.scale);
var nconfig=this.node;
如果(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;//将图像缩放到50px高度的比例因子
w=r*w;
h=r*h;
var dist=Math.sqrt((pos.x*pos.x)+(pos.y*pos.y));//到原点的距离
变量比例=1-(距离/节点比例);
//基于到原点的距离缩放节点
var sw=nconfig.transform?w*比例:w/dim/2;
var sh=nconfig.transform?h*刻度:h/dim/2;
如果(节点深度<2){
ctx.图纸图像(img,
pos.x-sh/2,//在节点上居中显示图像
位置y-sw/2,
西南,
sh);
}  
}  
},  
“包含”:函数(节点,位置){
var npos=node.pos.getc().$scale(node.scale);
var h=node.getData('imageheight');
var w=node.getData('imagewidth');
返回此.nodeHelper.rectangle.contains(npos、pos、w、h);
}
}  
});

唯一一个悬而未决的问题是,在我点击图表之前,图像不会出现。我认为这与img.addEventListener('load',function(){有关,但还不能找出问题的原因。任何想法都值得赞赏。

我不知道我在做什么与其他人不同,或者库是否自恐龙时代以来发生了变化。但无论出于何种原因,
getPos()
节点。pos.getc(true)
为我提供了一个基于数学的坐标系,而不是使用
超树时所需的实际像素

当使用图像时,尤其是使用
ctx.drawImage
时,JavaScript需要基于像素的坐标。我不是数学天才,所以花了一些时间来找出问题所在以及解决方法。将
node.pos.getc(true)
给定的值乘以
画布。宽度
是唯一的解决方法
$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); 
              }
          }  
    });