如何在Infovis Toolkit中创建自定义节点?
我一直在研究一个Infovis工具包项目,尽管所有的功能都完成了,但我还是无法完成视觉效果。 Infovis工具包API文档很好,但我的自定义节点类型不起作用。我使用的是hypertree,我想制作两种不同的自定义节点类型。一个来自图像,另一个是绘制的路径。所有的帮助非常感谢,谢谢!如何在Infovis Toolkit中创建自定义节点?
编辑:[我试图解决的方案并不那么得心应手。相反,我使用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
调用(在图像加载完成后运行)来解决此问题。
$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';
}
}
});
上面给出的答案是正确的。但是它有两个缺点。
1:它没有包含没有它,自定义节点将不会触发像OnMouseEnter在或的onClick事件等方法
2:图像闪烁每次移动任何节点图,或者你出锅的任何时间整个图。这是因为每次调用render函数重新绘制节点时都会加载图像。由于相同的原因,这也会影响性能。更好的选择是为所有具有“图像”类型的节点加载一次图像,并仅在渲染功能中重新绘制它们。这将有助于提高性能并阻止图像闪烁。
我们可以有一个函数'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给出的答案也不太工作对我超树 - 有我需要做得到节点图像的显示,并在超树表示适当扩展一些小的修改。希望这个答案能够帮助其他类似实现麻烦的用户。 免责声明我是一个新手程序员,很可能我错过了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');
}
});
}
没有变化的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(){
有关,但一直未能找出造成问题的原因。任何想法都表示赞赏。