我正在构建一个单页应用程序,它使用D3
和Knockout
的组合。目前他们正在分开工作并处理他们自己的数据绑定,但我们必须处理一些交叉。
这让我思考,如果这个交叉可以自动处理并且Knockout
和D3
可以很好地一起玩,那就太棒了。以下是我想做的几个简短示例:
在这种情况下,我有一个文本项,我想直接将值绑定到我的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;红色看起来很傻),我不希望触发笔划过渡。
答案 0 :(得分:0)
有一点需要注意,我对d3的理解与新生小猫的理解大致相同,我现在给你半个答案,希望我有时间让它更具实质性。后面。
由于d3是一个面向DOM的包,因此将其与Knockout一起使用的方法是将其降级为custom binding handlers。这就是DOM操纵的地方。这将有效地让您订阅相关的可观察量,并且您不应该与d3的原型混合。
对于你的颜色变化示例,我认为如果你做了一个计算来返回颜色值,并订阅了那个计算的,那么如果颜色没有实际改变,你的订阅就不会被激活。
<强>更新强>
我写了一些有点通用的&#34; d3&#34;你的绑定处理程序。它将该脚本的参数script
和options
作为参数。该脚本应该使用一个d3对象(绑定处理程序将从它绑定的元素构造)和一个options
对象。绑定处理程序解包options
中的所有可观察对象,以便脚本看到普通值。
对于此处的示例,我已将其绑定到路径元素,并从您的问题中提供了颜色更改脚本。因为我使用计算器来确定颜色,所以当颜色没有实际改变时,你可以看到没有转换。
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;