【问题标题】:How to make custom nodes in Infovis Toolkit?如何在 Infovis Toolkit 中制作自定义节点?
【发布时间】:2011-09-02 17:43:36
【问题描述】:

我一直在从事 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();*/
        }
       }
});

【问题讨论】:

    标签: javascript infovis


    【解决方案1】:

    因为您将图像 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';
            }
        }
    });
    

    【讨论】:

      【解决方案2】:

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

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

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

      我们可以有一个函数“loadImages”,它在每个节点中加载“type”为“image”的图像。每个 'image' 类型的节点还应该有一个名为 '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); 
                  } 
      }
      });
      

      【讨论】:

        【解决方案3】:

        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(){ 有关,但无法弄清楚导致问题的原因。任何想法表示赞赏。

        【讨论】:

          【解决方案4】:

          我不知道我在做什么与其他人不同,或者图书馆自恐龙时代以来是否发生了变化。但无论出于何种原因,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';
                  }
              }
          });
          

          这应该使图像在预期位置居中。

          【讨论】:

            猜你喜欢
            • 2013-07-03
            • 2013-01-19
            • 2012-01-28
            • 1970-01-01
            • 2011-02-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多