我已经建立了一个包含一系列“小倍数”的图表,每个图表代表完成的百分比,按行和列排列。每行都有一个不同的数字代表“完整”。
您可以在此小提琴中查看图表:http://jsfiddle.net/rolfsf/Lhnm9a9m/
我正在使用过渡来将条形从0增加到x%宽度。
我还想使用转换来显示从0开始递增的每个度量的实际计数(例如,17个中的10个)。
我使用文本补间功能将所有数字从0递增,但所有指标都停在相同的计数,而不是正确的计数。
我必须要么在补间中使用错误的数据,要么将补间放在脚本的错误部分......但我无法弄清楚问题出在哪里。
如何让数字正确递增?
我的数据如下所示:
var sets = [
{"title": "Set-1", "count": 17, "measures": [10, 13, 16, 14]},
{"title": "Set-2", "count": 23, "measures": [12, 18, 19, 23]},
{"title": "Set-3", "count": 25, "measures": [19, 22, 23, 20]},
{"title": "Set-4", "count": 4, "measures": [4, 4, 4, 4]},
{"title": "Set-5", "count": 8, "measures": [5, 7, 8, 6]}
];
图表的调用方式如下:
d3 .select('#overview-graph')
.datum(sets)
.call(relativeCompletionChart()
//options
);
,这是可重复使用的图表脚本:
function relativeCompletionChart() {
var width = 1200,
margin = {top: 16, right: 16, bottom: 16, left: 16},
onSetMouseOver = null,
onSetClick = null;
function chart(selection) {
selection.each(function(data) {
var titleColWidth = 0.3*width,
setHeight = 24,
barHeight = 22,
setCount = data.length,
colCount = data[0].measures.length,
colWidth = (width - titleColWidth)/colCount,
rangeWidth = colWidth - 4,
height = ((setHeight * setCount) + margin.top + margin.bottom);
var svg = d3.select(this)
.append("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", "0 0 " + width + " " + height )
.attr("preserveAspectRatio", "xMidYMin meet");
svg .append('rect')
.attr("x", 0)
.attr('y', 0)
.attr('height', height)
.attr('width', width)
.attr('class', 'chart-bg');
/**
* Tween functions
*/
function tweenText( newValue ) {
return function() {
// get current value as starting point for tween animation
var currentValue = +this.textContent;
// create interpolator and do not show nasty floating numbers
var i = d3.interpolateRound( currentValue, newValue );
return function(t) {
this.textContent = i(t);
};
}
}
function update(data) {
var set = svg.selectAll("g.set")
.data(data);
set.exit().remove();
var setEnter = set
.enter().append("g")
.attr("class", "set")
.attr('transform', function (d, i) {
return 'translate(0, ' + (margin.top + i*setHeight) + ')';
});
set.append("text")
.attr("class", "title")
.attr("x", (titleColWidth - 80))
.attr("y", 16)
.attr("text-anchor", "end")
.text(function (d){return d.title;});
set.append("text")
.attr("class", "count")
.attr("x", (titleColWidth - 32))
.attr("y", 16)
.attr("text-anchor", "end")
.text(function (d){return d.count;});
var ranges = set.selectAll("rect.range")
.data(function(d, i){return d.measures})
.enter().append('rect')
.attr("class", "range")
.attr("x",2)
.attr("y",0)
.attr('rx', 2)
.attr('ry', 2)
.attr("width", rangeWidth)
.attr("height",barHeight)
.attr("fill", "#CCCCCC")
.attr('transform', function (d, i) {
return 'translate(' + (titleColWidth + i*colWidth) + ', 0)';
});
var measures = set.selectAll("rect.measure")
.data(function(d, i){return d.measures})
.enter().append('rect')
.attr("class", "measure")
.attr('rx', 2)
.attr('ry', 2)
.attr("x",2)
.attr("y",0)
.attr("width", 1)
.attr("height",barHeight)
.attr('transform', function (d, i) {
return 'translate(' + (titleColWidth + i*colWidth) + ', 0)';
});
var markers = set.selectAll("line.marker")
.data(function(d, i){return d.measures})
.enter().append('line')
.attr("class", "marker")
.attr('x1', 2)
.attr('y1', 0)
.attr("x2",2)
.attr("y2",barHeight)
.attr('transform', function (d, i) {
return 'translate(' + (titleColWidth + i*colWidth) + ', 0)';
});
var values = set.selectAll("text.value")
.data(function(d, i){return d.measures})
.enter().append('text')
.text('0')
.attr("class", "value")
.attr("x", 8)
.attr("y", 16)
.attr("text-anchor", "start")
.attr('transform', function (d, i) {
return 'translate(' + (titleColWidth + i*colWidth) + ', 0)';
});
//update widths
set.selectAll("rect.measure")
.transition()
.duration(1000)
.delay(function(d, i) { return i * 20; })
.attr("width", function(d, i) { return d3.round((d/d3.select(this.parentNode).datum().count)*rangeWidth);});
set.selectAll("line.marker")
.transition()
.duration(1000)
.delay(function(d, i) { return i * 20; })
.attr("x1", function(d, i) { return d3.round((d/d3.select(this.parentNode).datum().count)*rangeWidth + 1);})
.attr("x2", function(d, i) { return d3.round((d/d3.select(this.parentNode).datum().count)*rangeWidth + 1);});
set.selectAll('text.value')
.transition()
.duration(1000)
.tween( 'text', function() {
// get current value as starting point for tween animation
var currentValue = +this.textContent;
// create interpolator and do not show nasty floating numbers
var interpolator = d3.interpolateRound( currentValue, 10 );
// this returned function will be called a couple
// of times to animate anything you want inside
// of your custom tween
return function( t ) {
// set new value to current text element
this.textContent = interpolator(t) + '/' + d3.select(this.parentNode).datum().count;
};
});
}
update(data);
});
}
chart.width = function(_) {
if (!arguments.length) return width;
width = _;
return chart;
};
chart.onSetClick = function(_) {
if (!arguments.length) return onSetClick;
onSetClick = _;
return chart;
};
chart.onSetMouseOver = function(_) {
if (!arguments.length) return onSetMouseOver;
onSetMouseOver = _;
return chart;
};
return chart;
}
此处提取补间的相关代码:
set.selectAll('text.value')
.transition()
.duration(1000)
.tween( 'text', function() {
// get current value as starting point for tween animation
var currentValue = +this.textContent;
// create interpolator and do not show nasty floating numbers
var interpolator = d3.interpolateRound( currentValue, 10 );
// this returned function will be called a couple
// of times to animate anything you want inside
// of your custom tween
return function( t ) {
// set new value to current text element
this.textContent = interpolator(t) + '/' + d3.select(this.parentNode).datum().count;
};
});
虽然我在脚本中也有一个未使用的辅助函数,但我无法工作:
/**
* Tween functions
*/
function tweenText( newValue ) {
return function() {
// get current value as starting point for tween animation
var currentValue = +this.textContent;
// create interpolator and do not show nasty floating numbers
var i = d3.interpolateRound( currentValue, newValue );
return function(t) {
this.textContent = i(t);
};
}
}