Javascript 如何在Infovis Toolkit中创建自定义节点?
我一直在做一个Infovis工具包项目,虽然所有功能都完成了,但我还没能完成视觉效果。Infovis toolkit API文档很好,但我的自定义节点类型不起作用。我正在使用一个超树,我想创建两种不同的自定义节点类型。一个来自图像,另一个作为绘制的路径。非常感谢您的帮助,谢谢 EDIT:[我尝试的解决方案没有那么方便。相反,我使用JIT控制器中的onCreateLabel()使用HTML自定义节点。性能明显提高,自定义节点的灵活性大大提高。] 这就是我到目前为止的想法:Javascript 如何在Infovis Toolkit中创建自定义节点?,javascript,infovis,Javascript,Infovis,我一直在做一个Infovis工具包项目,虽然所有功能都完成了,但我还没能完成视觉效果。Infovis toolkit API文档很好,但我的自定义节点类型不起作用。我正在使用一个超树,我想创建两种不同的自定义节点类型。一个来自图像,另一个作为绘制的路径。非常感谢您的帮助,谢谢 EDIT:[我尝试的解决方案没有那么方便。相反,我使用JIT控制器中的onCreateLabel()使用HTML自定义节点。性能明显提高,自定义节点的灵活性大大提高。] 这就是我到目前为止的想法: $jit.Hypertr
$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
调用(在映像完成加载后运行)
上面给出的答案是正确的。然而,它有两个缺点 1:它没有contains方法,没有contains方法,自定义节点将不会触发onMouseEnter或onClick等事件 2:每次移动图形中的任何节点或平移整个图形时,图像都会闪烁。这是因为每次调用渲染函数重新打印节点时都会加载图像。由于同样的原因,这也会影响性能。更好的选择是为所有具有“image”类型的节点加载一次图像,然后在渲染函数中重新打印它们。这将有助于提高性能并防止图像闪烁 我们可以有一个函数“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给出的答案对我的超树不太适用——我需要做一些小的修改,以使节点图像在超树表示中显示并适当缩放。希望这个答案能帮助其他有类似实现问题的用户。免责声明我是一名Javascript程序员新手,很可能我在Pratik的回答中遗漏了一些可以让它工作的重要内容。此外,如果没有他的回答,我永远不会接近他,对此我很感激 对json中的数据定义没有任何更改-按预期工作。me的一个关键注意事项是确保设置
可重写: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);
}
}
});
$jit.Hypertree.Plot.NodeType.implement({
'图像':{
“渲染”:函数(节点、画布){
var ctx=canvas.getCtx();
var pos=node.pos.getc().$scale(node.scale);
var nconfig=this.node;
如果(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;//将图像缩放到50px高度的比例因子
w=r*w;
h=r*h;
var dist=Math.sqrt((pos.x*pos.x)+(pos.y*pos.y));//到原点的距离
变量比例=1-(距离/节点比例);
//基于到原点的距离缩放节点
var sw=nconfig.transform?w*比例:w/dim/2;
var sh=nconfig.transform?h*刻度:h/dim/2;
如果(节点深度<2){
ctx.图纸图像(img,
pos.x-sh/2,//在节点上居中显示图像
位置y-sw/2,
西南,
sh);
}
}
},
“包含”:函数(节点,位置){
var npos=node.pos.getc().$scale(node.scale);
var h=node.getData('imageheight');
var w=node.getData('imagewidth');
返回此.nodeHelper.rectangle.contains(npos、pos、w、h);
}
}
});
唯一一个悬而未决的问题是,在我点击图表之前,图像不会出现。我认为这与img.addEventListener('load',function(){有关,但还不能找出问题的原因。任何想法都值得赞赏。我不知道我在做什么与其他人不同,或者库是否自恐龙时代以来发生了变化。但无论出于何种原因,
getPos()
和节点。pos.getc(true)
为我提供了一个基于数学的坐标系,而不是使用超树时所需的实际像素
当使用图像时,尤其是使用ctx.drawImage
时,JavaScript需要基于像素的坐标。我不是数学天才,所以花了一些时间来找出问题所在以及解决方法。将node.pos.getc(true)
给定的值乘以画布。宽度
是唯一的解决方法
$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);
}
}
});