我喜欢在我的Handlebars文件中定义Ember中的d3图表,并尽可能避免d3.select()和数据连接。我不想使用d3的数据连接,而是依赖于Ember的计算属性。实际上,在我在Ember中的d3可视化中,我倾向于将我的数据点包装在{{each}}
循环中,并且当底层数据发生变化时,Ember会负责更新图表。这一切都很好,但有一个重要的d3功能,我觉得我正在失去这种方式,即transitions。我喜欢在底层数据的两个状态之间进行转换。是否有任何方法同时依赖Ember进行数据连接(并避免d3.select()。data()舞蹈)并且仍能以某种方式应用d3过渡?
这是一个示例代码,我想在更改高度时将条形图从一个状态转换为另一个状态(这是一个简单的例子,但我认为这是说明性的)。在此先感谢您的帮助!
// bar-chart.js
export default Ember.Component.extend({
tagName: "svg",
attributeBindings: ["width", "height"],
width: 950,
height: 600,
padding: { top:..., bottom:..., left:..., right:... },
transformG: function() {
var padding = this.get("padding");
return "translate(%@,%@)".fmt(padding.left, padding.top);
}.property(),
chartW: function() {
...
}.property("width"),
chartH: function() {
...
}.property("height"),
yScale: function() {
var data = this.get("data");
return d3.scale.linear().domain(d3.extent(data, function(d) { return d.value; })).range([...]);
}.property("data", "chartH"),
xScale: function() {
...
}.property("data", "width"),
actions: {
// Some action that modifies the height of the svg
}
});
// bar-chart.hbs
<g class="axis x"></g>
<g class="axis y"></g>
<g class="bars" {{bind-attr transform=transformG width=chartW height=chartH}}>
{{#each d in data}}
{{vertical-bar value=d.value timestamp=d.timestamp yScale=yScale xScale=xScale}}
{{/each}}
<g>
假设子组件{{vertical-bar}}
负责计算与每个单独栏相关的一些属性。
答案 0 :(得分:0)
尽管d3进入/更新/退出范例可能令人困惑/繁琐,但它确实处于d3的核心并尝试覆盖它但仍然使用d3的其他部分(如transition
)将可能会给你带来很多麻烦。
例如:
d3一次绑定svg元素,但我认为你在这里做的事情会在每次data
更改时重新呈现元素,而这可能不是你想要的。如果您尝试使用transition
并同时更新了Ember中的数据,那么事情可能会爆炸。
我熟悉&#34; d3.select().data()
舞蹈的头痛&#34;但最终通常意味着我并没有很好地理解我的数据的形状。是否有一些技术原因你不想这样做?或者each d in data
在概念上更容易?
你可能想要考虑一个由d3消费的data
的中间集,但是由Ember控制。我当前使用的策略是创建一个ObjectProxy
的数组来包装一些自定义属性,然后将整个事件传递给d3:
ObjectProxy.create({content: {timestamp: d.x, some_property: d.foo, type: "bar" }})
当您更新此中间集时,您可以简单地将整个内容再次传递给d3,并且在正确设置输入/更新/退出选择的情况下它将执行正确的操作。
确保其有效的一个提示是在您的基准上设置唯一ID并在选择期间使用它:
svg.selectAll("rect").data(@get("myData"), (d) -> d.get("id"))
注意:coffeescript =)
我认为允许Ember控制输入的数据大概是在你使用D3完成其余工作之前就已经去了,否则你只是混淆了责任而你也可以推出你自己的图库。上面的替代方案为您提供了更多的控制,并为您的基准添加了计算属性,可以这样使用:
selection.transition().attr({x: (d) -> d.get("end_x")})
其中end_x
是Ember为您处理的事情。
或:.on("click", (d,i) => d.get("controller").transitionToRoute("myRoute", d.id))