有条件地递归地改变所有节点和边缘的不透明度(d3)

时间:2016-09-22 02:33:27

标签: javascript dictionary recursion d3.js filtering

更新:I have created a JSFiddle here。请将更新后的小提琴发布。

我有用户可以应用于数据的动态过滤器,但是它们会更改节点的不透明度以指示过滤进出的内容(过滤后的“out”元素仍然部分可见,实际d3 filter()功能未被使用(故意))。我还在每个被过滤掉的节点上设置了一个属性(例如node = {"name": "test", "isFilteredOut": true};)。 所以出于这个问题的目的,即使我使用“过滤器”这个词,它实际上只是一个条件样式更改 (我会试着把这个词在这篇文章的引号中“过滤”作为对此的提醒)。

这一切都运行正常,但现在我想递归“过滤”所有子节点和“过滤”节点的边缘,以及将初始“过滤”节点连接到其未过滤的节点的边缘 - out父节点。

我能找到的所有示例都以点击事件开始,因此可以使用this来获取所选初始节点的数据。我没有这种奢侈 ,因为过滤器是使用不在图表本身内的UI元素来应用的。

我目前正在“过滤”节点,如下所示:

node.style("opacity", function(n) {
    if (my_filter_conditions) {
        return 1;
    } else {
        n.isFilteredOut = true;
        return 0.1;
    }
});

我基本上需要做的是:

  1. 递归选择当前“已滤除”节点的所有子节点,并将其“过滤”出来(即将其不透明度更改为0.1并设置n.isFilteredOut = true;)。

  2. 将所有边的不透明度更改为0.1,其中源节点目标节点被“过滤掉”(即边缘两端的n.isFilteredOut = true;

  3. 我尝试了什么

    我不知道如何只从边缘处获得每个节点的索引来访问源节点和目标节点的数据(记住我没有从点击事件开始的this节点)。我尝试传递从边缘获得的节点索引,以获得节点数据:

    var node_data = d3.select(current_edge.source.index).datum();
    

    但是,这导致与this.node()相关的d3库中的错误为空(因此在此处传递索引不起作用)。

    我还尝试通过嵌套函数来处理边缘,以处理传递给node.style()函数的函数内部的链接,但随后它尝试处理每个所有边缘的 node我无法让它提供所需的结果。

    link.style("opacity", function (e) {
        return ( (n.isFilteredOut)
                && (n.index==e.source.index | n.index==e.target.index) ) ? 0.1 : 1;
    });
    

    这是我试图“过滤掉”“滤出”节点两侧的边缘,但是当我出于某种原因使用它时,没有任何边缘被过滤掉(似乎根本没有发生任何事情)

    更新:I have created a JSFiddle here

    关于小提琴的注释:

    • 我知道它很简单(它应该是一个最小的工作示例)
    • 实际应用程序包含在类型中应用的过滤器(即使只是搜索特定设备/部件/等),因此逻辑能够有条件地遵循“链”仅对那些节点具有重要性node.isFilteredOut = true;
    • 在此示例中,正确答案将导致创建过滤设备也将过滤掉所有部分的情况
    • 使用dataSet本身进行任何过滤的解决方案将无效,因为我的大部分数据都是从各种JSON源动态填充的。您可以随意使用nodesedgeslinksnode和/或link
    • 请不要重写我的过滤方法。是的,我知道eval()陈述不是很好。但这不是关于如何最好地应用无限联合滤波器的问题,而是基于应用的滤波器递归地改变节点和边缘的不透明度

2 个答案:

答案 0 :(得分:2)

这是一种可能的方法,它实现递归过滤(如果设备被过滤,其部分被过滤),以及基于过滤节点过滤链接:http://jsfiddle.net/Lsr9c8nL/4/

我改变了你实现过滤器的方式。使用字符串来构建过滤器,然后eval()被认为是非常糟糕的,因为工具对eval()不起作用,例如检测错误或优化浏览器上的JS代码。

我直接在dataSet上进行过滤,而不是在节点上(您必须查询节点的type并比较字符串,这很慢)。直接在dataSet上执行此操作还可以轻松找到给定部件的设备。

技巧基本上是每次重绘整个图表,并仔细使用d3的exitenterupdate选项。如果您需要,还可以添加动画

答案 1 :(得分:0)

我知道你强烈关注你在这里使用eval,但你可以重构你的过滤函数来返回一个lambda:

function isNTrueForAll(arrayOFuncts,n){
return arrayOFuncts.every(i=>i(n))

}

然后做这样的事情:

function isNTrueForAll(arrayOFuncts,n){
return arrayOFuncts.reduce((current,next)=>{return current && next(n)},true)

}

或者使用reduce,如果你没有Array.every:

RandomAccessFile

或者做一个递归迭代器,如果这更像你的包。 重点是,条件布尔评估和延迟执行不是你需要eval的东西,并且你需要一个很大的性能来运行eval。

- #编辑 -

看起来DOM在这里并没有实际反映您的设备和部分节点之间的关系,因此您无法使用D3选择对象将特定部分节点与其父设备相关联节点

因此,递归选择孩子是一个有争议的问题。

或者您可以调整过滤器功能以反映关系 - IE,如果我们按部分过滤,则返回设备或部件的假。

如果没有那么多过滤条件并且您已经知道您希望提前使用的业务逻辑来过滤它们,那么这只是一个真正可持续的解决方案,但是,它可以为你工作。