Как сделать пользовательские узлы в Infovis Toolkit? - PullRequest
4 голосов
/ 02 сентября 2011

Я работал над проектом набора инструментов Infovis, и, хотя вся функциональность выполнена, я не смог закончить визуальные эффекты.Документация по API инструментария Infovis хороша, но мои пользовательские типы узлов не работают.Я использую гипердерево и хочу создать два разных типа узлов.Один из изображения, а другой как нарисованный путь.Спасибо всем за помощь, спасибо!

РЕДАКТИРОВАТЬ: [Решение, которое я пытался, оказалось не очень удобно.Вместо этого я использовал onCreateLabel () из контроллеров JIT для настройки узлов с помощью 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();*/
        }
       }
});

Ответы [ 3 ]

7 голосов
/ 13 сентября 2011

Поскольку вы устанавливаете для 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';
        }
    }
});
3 голосов
/ 13 июня 2013

Ответ, приведенный выше, является правильным.Однако у этого есть два недостатка.

1: у него нет метода содержит, без которого пользовательский узел не будет запускать события, такие как onMouseEnter или onClick и т. Д.

2: изображения будут мерцать каждыйвремя перемещения любого узла на графике или любое перемещение панорамы.Это связано с тем, что изображение загружается каждый раз, когда вызывается функция рендеринга для повторного построения узла.Это также влияет на производительность по той же причине.Лучшим вариантом будет загрузить изображения один раз для всех узлов, имеющих тип «изображение», и просто перенести их в функцию рендеринга.Это поможет производительности и предотвратит мерцание изображений.

У нас может быть функция loadImages, которая загружает изображения в каждом узле с типом в качестве изображения.Каждый узел типа 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); 
            } 
}
});
1 голос
/ 17 июля 2014

Ответ, данный Pratik, не совсем сработал для моего гипердерева - было несколько небольших изменений, которые мне нужно было сделать, чтобы изображения узлов отображались и масштабировались соответствующим образом в представлении гипердерева.Надеемся, что этот ответ поможет другим пользователям с аналогичными проблемами реализации. Disclaimer Я начинающий программист Javascript, и, вероятно, я упустил что-то существенное в ответе Пратика, что заставило бы его работать.Кроме того, я бы никогда не подошел близко без его ответа, за что я благодарен.

Без изменений в определении данных в json - работает как положено.Ключевым моментом для меня было убедиться, что вы установили overridable: true, например,

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

. Мне понадобилась высота / ширина изображения, чтобы масштабировать их позже.Я реализовал это в функции loadImages, предоставленной Pratik, поэтому он будет запускаться только один раз:

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

Единственная нерешенная проблема заключается в том, что изображения не появляются до тех пор, пока я не нажму на график.Я думаю, что это связано с img.addEventListener('load', function(){, но я не смог выяснить, что является причиной проблемы.Любые мысли приветствуются.

...