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

4
我一直在进行信息可视化工具包项目的开发,虽然所有功能已经完成,但我还没有能够完成可视化部分。 信息可视化工具包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个回答

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';
        }
    }
});

3

以上回答是正确的。然而,它有两个缺点。

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

2:每当移动任何节点或整个图表时,图像都会闪烁。这是因为在调用重新绘制节点的render函数时会加载图像。由于同样的原因,这也会影响性能。更好的选择是仅对所有类型为'image'的节点加载一次图像,并在render函数中重新绘制它们。这将有助于性能并防止图像闪烁。

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

以下是json数据中此类节点的“Data”部分的示例:

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

在加载JSON数据后,应立即调用loadImages函数。

// 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); 
            } 
}
});

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(){ 有关,但我无法弄清楚是什么原因导致了麻烦。欢迎提出任何想法。

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';
        }
    }
});

这应该将图像居中到预期位置。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接