4

我一直在研究 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

4 回答 4

7

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

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

$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';
        }
    }
});
于 2011-09-13T07:19:53.933 回答
3

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

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

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

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

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

"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); 
            } 
}
});
于 2013-06-13T14:04:53.193 回答
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(){但无法弄清楚导致麻烦的原因有关。任何想法表示赞赏。

于 2014-07-16T22:56:22.407 回答
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';
        }
    }
});

哪个应该将图像居中在预期的位置。

于 2020-06-13T14:58:51.890 回答