在我的d3条形图中,如果它超过最后一个标记,我应该在最高标尺上方有一个顶部Y轴标记(带有水平网格线)。这是通过计算最后一个滴答,然后使用tickValues()
应用来实现的。
此外,最多应有5个刻度线和网格线,包括x轴域(0)。我使用ticks()
尝试了此操作,但它无法与tickValues()
一起使用。对此有何解决方案?
// container size
var margin = {top: 10, right: 10, bottom: 30, left: 30},
width = 400,
height = 300;
var data = [
{"month":"DEC","setup":{"count":26,"id":1,"label":"Set Up","year":"2016","graphType":"setup"}},
{"month":"JAN","setup":{"count":30,"id":1,"label":"Set Up","year":"2017","graphType":"setup"}},
{"month":"FEB","setup":{"count":30,"id":1,"label":"Set Up","year":"2017","graphType":"setup"}}];
var name = 'dashboard';
// x scale
var xScale = d3.scale.ordinal()
.rangeRoundBands([0, width], 0.2);
// set x and y scales
xScale.domain(data.map(function(d) { return d.month; }));
// x axis
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.outerTickSize(0);
var yScale = d3.scale.linear()
.domain([0, d3.max(data, function(d) {
return d.setup.count;
})])
.range([height, 0]);
var ticks = yScale.ticks(),
lastTick = ticks[ticks.length-1];
var newLastTick = lastTick + (ticks[1] - ticks[0]);
if (lastTick < yScale.domain()[1]){
ticks.push(lastTick + (ticks[1] - ticks[0]));
}
// adjust domain for further value
yScale.domain([yScale.domain()[0], newLastTick]);
// y axis
var yAxis = d3.svg.axis()
.scale(yScale)
.orient('left')
.tickSize(-width, 0, 0)
.tickFormat(d3.format('d'))
.tickValues(ticks);
// create svg container
var svg = d3.select('#chart')
.append('svg')
.attr('class','d3-setup-barchart')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
//.on('mouseout', tip.hide);
// apply tooltip
//svg.call(tip);
// Horizontal grid (y axis gridline)
svg.append('g')
.attr('class', 'grid horizontal')
.call(d3.svg.axis()
.scale(yScale)
.orient('left')
.tickSize(-width, 0, 0)
.tickFormat('')
.tickValues(ticks)
);
// create bars
var bars = svg.selectAll('.bar')
.data(data)
.enter()
.append('g');
bars.append('rect')
.attr('class', function(d,i) {
return 'bar';
})
.attr('id', function(d, i) {
return name+'-bar-'+i;
})
.attr('x', function(d) { return xScale(d.month); })
.attr('width', xScale.rangeBand())
.attr('y', function(d) { return yScale(d.setup.count); })
.attr('height', function(d) { return height - yScale(d.setup.count); })
.on('click', function(d, i) {
d3.select(this.nextSibling)
.classed('label-text selected', true);
d3.select(this)
.classed('bar selected', true);
d3.select('#'+name+'-axis-text-'+i)
.classed('axis-text selected', true);
});
//.on('mouseover', tip.show)
//.on('mouseout', tip.hide);
// apply text at the top
bars.append('text')
.attr('class',function(d,i) {
return 'label-text';
})
.attr('x', function(d) { return xScale(d.month) + (xScale.rangeBand()/2) - 10; })
.attr('y', function(d) { return yScale(d.setup.count) + 2 ; })
.attr('transform', function() { return 'translate(10, -10)'; })
.text(function(d) { return d.setup.count; });
// draw x axis
svg.append('g')
.attr('id', name+'-x-axis')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
// apply class & id to x-axis texts
d3.select('#'+name+'-x-axis')
.selectAll('text')
.attr('class', function(d,i) {
return 'axis-text';
})
.attr('id', function(d,i) { return name+'-axis-text-' + i; });
// draw y axis
svg.append('g')
.attr('class', 'y axis')
.call(yAxis)
.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', 6)
.attr('dy', '.71em')
.style('text-anchor', 'end');
// remove 0 in y axis
svg.select('.y')
.selectAll('.tick')
.filter(function (d) {
return d === 0 || d % 1 !== 0;
}).remove();
svg
.select('.horizontal')
.selectAll('.tick')
.filter(function (d) {
return d === 0 || d % 1 !== 0;
}).remove();
答案 0 :(得分:1)
正如我在comment中告诉你的那样,如果您使用D3 v4.x,这将非常简单:您可以使用tickValues
或{{1}设置d3.ticks
}。
但如果你想坚持使用D3 v3,那就有一个解决方案。
您的案例中的默认方法是使用d3.range
设置滴答数。但是,正如API所说,
如果count是一个数字,那么将返回大约计数的滴答。如果未指定count,则默认为10.指定的计数仅为提示;根据输入域,比例可能会返回更多或更少的值。 (强调我的)
因此,您无法在此处使用scale.ticks
来设置固定数量的5个刻度。
因此,我的解决方案涉及创建自己的函数来计算滴答。它根本不复杂。就是这样:
scale.ticks
此函数有三个参数:第一个值(function createTicks(start, stop, count) {
var difference = stop - start;
var steps = difference / (count - 1);
var arr = [start];
for (var i = 1; i < count; i++) {
arr.push(~~(start + steps * i))
}
return arr;
}
),最后一个值(start
)和刻度数(stop
)。我正在使用双NOT,因为无论出于何种原因,你都会过滤掉非整数值。
因此,我们只需要在count
域中设置最大值。例如,使最大值比最大值大10%:
yScale
(如果你愿意的话,你可以保持数学计算以获得新的最后一个标记,我只是显示了一种不同的方法来设置域的最大值,这与数据中的最大值不同)
然后,我们定义y轴的刻度:
var yScale = d3.scale.linear()
.domain([0, d3.max(data, function(d) {
return d.setup.count;
}) * 1.1])
// ^----- 10% increase
.range([height, 0]);
在您的域中使用我们的自定义函数,它将返回此数组:
var axisTicks = createTicks(yScale.domain()[0], yScale.domain()[1], 5);
然后,只需在[0, 8, 16, 24, 33]
中使用该数组即可。
这是您更新的小提琴:https://jsfiddle.net/7ktzpnno/
这里是Stack片段中的相同代码:
axis.tickValues
// container size
var margin = {
top: 10,
right: 10,
bottom: 30,
left: 30
},
width = 400,
height = 300;
var data = [{
"month": "DEC",
"setup": {
"count": 26,
"id": 1,
"label": "Set Up",
"year": "2016",
"graphType": "setup"
}
}, {
"month": "JAN",
"setup": {
"count": 30,
"id": 1,
"label": "Set Up",
"year": "2017",
"graphType": "setup"
}
}, {
"month": "FEB",
"setup": {
"count": 30,
"id": 1,
"label": "Set Up",
"year": "2017",
"graphType": "setup"
}
}];
var name = 'dashboard';
// x scale
var xScale = d3.scale.ordinal()
.rangeRoundBands([0, width], 0.2);
// set x and y scales
xScale.domain(data.map(function(d) {
return d.month;
}));
// x axis
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.outerTickSize(0);
var yScale = d3.scale.linear()
.domain([0, d3.max(data, function(d) {
return d.setup.count;
}) * 1.1])
.range([height, 0]);
var axisTicks = createTicks(yScale.domain()[0], yScale.domain()[1], 5);
function createTicks(start, stop, count) {
var difference = stop - start;
var steps = difference / (count - 1);
var arr = [start];
for (var i = 1; i < count; i++) {
arr.push(~~(start + steps * i))
}
return arr;
}
// y axis
var yAxis = d3.svg.axis()
.scale(yScale)
.orient('left')
.tickSize(-width, 0, 0)
.tickValues(axisTicks);
// tooltip
// var tip = d3.tip()
// .attr('class', 'd3-tip')
// .offset([-10, 0])
// .html(function(d) {
// return '<span class="tooltip-line">'+d.patientSetup.label+': '+
// d.patientSetup.count + '</span><span>'+d.patientNotSetup.label+': '+
// d.patientNotSetup.count + '</span>';
// });
// create svg container
var svg = d3.select('#chart')
.append('svg')
.attr('class', 'd3-setup-barchart')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
//.on('mouseout', tip.hide);
// apply tooltip
//svg.call(tip);
// Horizontal grid (y axis gridline)
svg.append('g')
.attr('class', 'grid horizontal')
.call(d3.svg.axis()
.scale(yScale)
.orient('left')
.tickSize(-width, 0, 0)
.tickValues(axisTicks)
);
// create bars
var bars = svg.selectAll('.bar')
.data(data)
.enter()
.append('g');
bars.append('rect')
.attr('class', function(d, i) {
return 'bar';
})
.attr('id', function(d, i) {
return name + '-bar-' + i;
})
.attr('x', function(d) {
return xScale(d.month);
})
.attr('width', xScale.rangeBand())
.attr('y', function(d) {
return yScale(d.setup.count);
})
.attr('height', function(d) {
return height - yScale(d.setup.count);
})
.on('click', function(d, i) {
d3.select(this.nextSibling)
.classed('label-text selected', true);
d3.select(this)
.classed('bar selected', true);
d3.select('#' + name + '-axis-text-' + i)
.classed('axis-text selected', true);
});
//.on('mouseover', tip.show)
//.on('mouseout', tip.hide);
// apply text at the top
bars.append('text')
.attr('class', function(d, i) {
return 'label-text';
})
.attr('x', function(d) {
return xScale(d.month) + (xScale.rangeBand() / 2) - 10;
})
.attr('y', function(d) {
return yScale(d.setup.count) + 2;
})
.attr('transform', function() {
return 'translate(10, -10)';
})
.text(function(d) {
return d.setup.count;
});
// draw x axis
svg.append('g')
.attr('id', name + '-x-axis')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
// apply class & id to x-axis texts
d3.select('#' + name + '-x-axis')
.selectAll('text')
.attr('class', function(d, i) {
return 'axis-text';
})
.attr('id', function(d, i) {
return name + '-axis-text-' + i;
});
// draw y axis
svg.append('g')
.attr('class', 'y axis')
.call(yAxis)
.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', 6)
.attr('dy', '.71em')
.style('text-anchor', 'end');
// remove 0 in y axis
svg.select('.y')
.selectAll('.tick')
.filter(function(d) {
return d === 0 || d % 1 !== 0;
}).remove();
svg
.select('.horizontal')
.selectAll('.tick')
.filter(function(d) {
return d === 0 || d % 1 !== 0;
}).remove();
.d3-setup-barchart {
background-color: #666666;
}
.d3-setup-barchart .axis path {
fill: none;
stroke: #000;
}
.d3-setup-barchart .bar {
fill: #ccc;
}
.d3-setup-barchart .bar:hover {
fill: orange;
cursor: pointer;
}
.d3-setup-barchart .bar.selected {
fill: orange;
stroke: #fff;
stroke-width: 2;
}
.d3-setup-barchart .label-text {
text-anchor: middle;
font-size: 12px;
font-weight: bold;
fill: orange;
opacity: 0;
}
.d3-setup-barchart .label-text.selected {
opacity: 1;
}
.d3-setup-barchart .axis text {
fill: rgba(255, 255, 255, 0.6);
font-size: 9px;
}
.d3-setup-barchart .axis-text.selected {
fill: orange;
}
.d3-setup-barchart .y.axis path {
display: none;
}
.d3-setup-barchart .y.axis text {
font-size: 6px;
}
.d3-setup-barchart .x.axis path {
fill: none;
stroke: #353C41;
}
.d3-setup-barchart .grid .tick {
stroke: #fff;
opacity: .18 !important;
stroke-width: 0;
}
.d3-setup-barchart .grid .tick line {
stroke-width: .5 !important;
}
.d3-setup-barchart .grid path {
stroke-width: 0;
}
PS :在您的问题中,您说“应该有最多5个刻度和网格线,包括x轴域(0)。”。但是,在您的代码中,您故意删除0刻度。如果要在y轴上看到0刻度,请删除该块:https://jsfiddle.net/jz0q547u/