是否有可能在D3

时间:2015-11-04 09:54:48

标签: javascript d3.js knockout.js

我正在构建一个单页应用程序,它使用D3Knockout的组合。目前他们正在分开工作并处理他们自己的数据绑定,但我们必须处理一些交叉。

这让我思考,如果这个交叉可以自动处理并且KnockoutD3可以很好地一起玩,那就太棒了。以下是我想做的几个简短示例:

绑定单个值

在这种情况下,我有一个文本项,我想直接将值绑定到我的observable

d3.select("text").text(myObservable);

绑定多个值

在这种情况下,我有很多文本项,并希望绑定到驻留在数据元素上的observable:

d3.select("text").text(function(d) { return d.myObservable(); });

使用函数(和转换)绑定单个值

这是两种情况之间的交叉(但我认为类似于绑定到多个值),因为我现在有一个项目,但我需要能够执行一个函数来正确地获取我的值。在这里,如果观察者要改变,这将触发动画以改变笔画风格。

d3.select("path")
  .style("stroke", "white")
  .transition()
  .duration(1000)
  .style("stroke", function(d) { 
    var value = d.myObservable();
    if(value < 0) return "red";
    if(value === 0) return "orange";
    return "green";
});

就解决方案而言,我正在尝试按照以下方式覆盖d3.selection.prototype.text函数(可能还有其他一些函数):

var setText = d3.selection.prototype.setText;
d3.selection.prototype.setText = function(value, index, groupIndex) {

    var selection = this;
    if(value && ko.isObservable(value)) {
       value.subscribe(function() {
          setText.call(selection, value(), index, groupIndex);
       });
       setText.call(selection, value(), index, groupIndex);
       return;
    }

    if(typeof value === function) {
        var result = value();

        // Not sure how to achieve this bit
        if(value used an observable) {
           allObservablesUsedInFunction.subscribe(function() {
              result = value();
              setText.call(selection, value(), index, groupIndex);
           });
        }

        setText.call(selection, result, index, groupIndex);
        return;
    }

    setText.call(selection, value, index, groupIndex);
};

我不确定如何监视函数中使用的observable,或者之后如何为订阅获取它们。我的直觉也是我要努力解决d3.transition()那个我真正喜欢的中风。所以我想知道是否还有其他方法可以达到我想要的效果?

我已经简要地考虑了对任何更改的订阅 - 这将触发典型的D3更新操作 - 但是我的一些动画只有在自上次更新后值发生变化时才需要触发,而我不是确定我怎么能够确定。例如 - 如果当前可观察值与前一个值相同(红色 - &gt;白色 - &gt;红色看起来很傻),我不希望触发笔划过渡。

1 个答案:

答案 0 :(得分:0)

有一点需要注意,我对d3的理解与新生小猫的理解大致相同,我现在给你半个答案,希望我有时间让它更具实质性。后面。

由于d3是一个面向DOM的包,因此将其与Knockout一起使用的方法是将其降级为custom binding handlers。这就是DOM操纵的地方。这将有效地让您订阅相关的可观察量,并且您不应该与d3的原型混合。

对于你的颜色变化示例,我认为如果你做了一个计算来返回颜色值,并订阅了那个计算的,那么如果颜色没有实际改变,你的订阅就不会被激活。

<强>更新 我写了一些有点通用的&#34; d3&#34;你的绑定处理程序。它将该脚本的参数scriptoptions作为参数。该脚本应该使用一个d3对象(绑定处理程序将从它绑定的元素构造)和一个options对象。绑定处理程序解包options中的所有可观察对象,以便脚本看到普通值。

对于此处的示例,我已将其绑定到路径元素,并从您的问题中提供了颜色更改脚本。因为我使用计算器来确定颜色,所以当颜色没有实际改变时,你可以看到没有转换。

&#13;
&#13;
ko.bindingHandlers.d3 = {
  update: function(element, valueAccessor, allBindingsAccessor, data, context) {
    var d3element = d3.select(element),
      params = valueAccessor();
    for (opt in params.options) {
      params.options[opt] = ko.utils.unwrapObservable(params.options[opt]);
    }
    params.script(d3element, params.options);
  }

};

vm = (function() {
  var v = ko.observable(3),
    color = ko.computed(function() {
      if (v() < 0) return 'red';
      if (v() === 0) return 'orange';
      return 'green';
    });
  return {
    v: v,
    color: color,
    d3script: function(d3element, options) {
      d3element.style("stroke", "black")
        .transition()
        .duration(1000)
        .style("stroke", options.color);
    },
    decrement: function() {
      v(v() - 1);
    },
    increment: function() {
      v(v() + 1);
    }

  };
}());

ko.applyBindings(vm);
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.3/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<button data-bind="click:decrement">-</button>
<span data-bind="text:v"></span>

<button data-bind="click:increment">+</button>
<svg width="4cm" height="4cm" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg" version="1.1">
  <title>Example triangle01- simple example of a 'path'</title>
  <desc>A path that draws a triangle</desc>
  <rect x="1" y="1" width="398" height="398" fill="none" stroke="blue" />
  <path data-bind="d3:{script: d3script, options: {color:color}}" d="M 100 100 L 300 100 L 200 300 z" fill="#fdd" stroke="blue" stroke-width="7" />
</svg>
&#13;
&#13;
&#13;