javascript 当范围为 0-1 或 0-2 时,仅在 d3.js 中沿 y 轴显示整数值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13513177/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Display only whole values along y axis in d3.js when range is 0-1 or 0-2
提问by Benjamin Udink ten Cate
I am using d3js to display a realtime representation of the views of a website. For this I use a stack layout and I update my dataset by JSON at the moment.
我正在使用 d3js 来显示网站视图的实时表示。为此,我使用堆栈布局,目前我通过 JSON 更新我的数据集。
When there is only 1 or 2 views being displayed on the y axis, which is dynamic related to the amount of views in the graph, the axis labels are: 1 => 0, 0.2, 0.4, 0.6, 0.8, 1
, the axis labels are: 2 => 0, 0.5, 1, 1.5, 2
This makes no sense for my dataset since it displays views of a page, and you can't have half a view.
当 y 轴上只显示 1 或 2 个视图时,这与图表中的视图数量动态相关,轴标签为: 1 => 0, 0.2, 0.4, 0.6, 0.8, 1
,轴标签为: 2 =>0, 0.5, 1, 1.5, 2
这对我的数据集,因为它显示一个页面的视图,你不能有半个视图。
I have a linear scale in d3js I base my y axis on
我在 d3js 中有一个线性比例,我的 y 轴基于
var y_inverted = d3.scale.linear().domain([0, 1]).rangeRound([0, height]);
According to the documentation of rangeRound()I should only get whole values out of this scale. For drawing my axis I use:
根据rangeRound() 的文档,我应该只得到超出这个比例的整个值。为了绘制我的轴,我使用:
var y_axis = svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(y_inverted.axis = d3.svg.axis()
.scale(y_inverted)
.orient("left")
.ticks(5));
Because it is a realtime application I update this every second by calling:
因为它是一个实时应用程序,所以我通过调用每秒更新一次:
function update(){
y_inverted.domain([yStackMax, 0]);
y_axis.transition()
.duration(interval)
.ease("linear")
.call(y_inverted.axis);
}
yStackMax is calculated from a stacklayout, as far as I know the data used for the y values only contain integers.
yStackMax 是从堆栈布局计算的,据我所知,用于 y 值的数据仅包含整数。
var yStackMax = d3.max(layers, function(layer) {
return d3.max(layer, function(d) {
return d.y0 + d.y;
});
});
I have tried several things to get a proper value for my y axis.
我已经尝试了几种方法来为我的 y 轴获得正确的值。
d3.svg.axis()
.scale(y_inverted)
.orient("left")
.ticks(5).tickFormat(d3.format(",.0f"))
Got me the closest sofar, but it still displays 0, 0, 0, 1, 1, 1
给了我最近的 sofar,但它仍然显示 0, 0, 0, 1, 1, 1
Basically what I want is to only have 1 tick when yStackMax
is 1, 2 ticks when it's 2, but it should also work if yStackMax
is 12 or 1,000,000
基本上我想要的是在 1 时只有 1 个刻度yStackMax
,当它是 2 时有 2 个刻度,但如果yStackMax
是 12 或 1,000,000 ,它也应该工作
采纳答案by Juve
Short answer: You can dynamically set the number of ticks. Set it to 1
to display only two tick labels:
简短回答:您可以动态设置刻度数。将其设置1
为仅显示两个刻度标签:
var maxTicks = 5, minTicks = 1;
if (yStackMax < maxTicks) {
y_axis.ticks(minTicks)
}
else {
y_axis.ticks(maxTicks)
}
Long Answer(going a bit off topic): While playing with your example I came up with a rather "complete solution" to all your formatting problems. Feel free to use it :)
长答案(有点离题):在玩你的例子时,我想出了一个相当“完整的解决方案”来解决你所有的格式问题。随意使用它 :)
var svg = d3.select("#svg")
var width = svg.attr("width")
var height = svg.attr("height")
var yStackMax = 100000
var interval = 500
var maxTicks = 5
var minTicks = 1
var y_inverted = d3.scale.linear().domain([0, 1]).rangeRound([0, height])
var defaultFormat = d3.format(",.0f")
var format = defaultFormat
var y_axis = d3.svg.axis()
.scale(y_inverted)
.orient("left")
.ticks(minTicks)
.tickFormat(doFormat)
var y_axis_root;
var decimals = 0;
function countDecimals(v){
var test = v, count = 0;
while(test > 10) {
test /= 10
count++;
}
return count;
}
function doFormat(d,i){
return format(d,i)
}
function init(){
y_axis_root = svg.append("g")
.attr("class", "y axis")
// I modified your example to move the axis to a visible part of the screen
.attr("transform", "translate(150,0)")
.call(y_axis)
}
// custom formatting functions:
function toTerra(d) { return (Math.round(d/10000000000)/100) + "T" }
function toGiga(d) { return (Math.round(d/10000000)/100) + "G" }
function toMega(d) { return (Math.round(d/10000)/100) + "M" }
function toKilo(d) { return (Math.round(d/10)/100) + "k" }
// the factor is just for testing and not needed if based on real world data
function update(factor){
factor = (factor) || 0.1;
yStackMax*=factor
decimals = countDecimals(yStackMax)
console.log("yStackMax decimals:",decimals, factor)
if (yStackMax < maxTicks) {
format = defaultFormat
y_axis.ticks(minTicks)
}
else {
y_axis.ticks(maxTicks)
if (decimals < 3 ) format = defaultFormat
else if(decimals < 6 ) format = toKilo
else if(decimals < 9 ) format = toMega
else if(decimals < 12) format = toGiga
else format = toTerra
}
y_inverted.domain([yStackMax, 0]);
y_axis_root.transition()
.duration(interval)
.ease("linear")
.call(y_axis);
}
init()
setTimeout(update, 200)
setTimeout(update, 400)
setTimeout(update, 600)
You can try it together with this html snippet:
您可以与此 html 代码段一起尝试:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.v2.js"></script>
</head>
<body>
<div><svg id="svg" width="200" height="300"></svg></div>
<script src="axis.js"></script>
<button id="button1" onclick="update(10)">+</button>
<button id="button2" onclick="update(0.1)">-</button>
</body>
</html>
I know it is a bit off topic but I usually like to provide running examples/solutions. Regard the additional formatting stuff as a bonus to the actual problem.
我知道这有点离题,但我通常喜欢提供运行示例/解决方案。将额外的格式化内容视为对实际问题的奖励。
回答by Superboggly
If you ask for a certain number of ticks (via axis.ticks() ) then d3 will try to give you that many ticks - but will try to use pretty values. It has nothing to do with your data.
如果您要求一定数量的刻度(通过 axis.ticks() ),那么 d3 会尝试给您那么多刻度 - 但会尝试使用漂亮的值。它与您的数据无关。
Your solutions are to use tickFormat, as you did, to round all the values to integer values, only ask for one tick as Juve answered, or explicitly set the tick values using axis.tickValues([...])which would be pretty easy used in conjunction with d3.range
您的解决方案是像您一样使用 tickFormat 将所有值四舍五入为整数值,仅在 Juve 回答时要求一个刻度,或者使用axis.tickValues([...])显式设置刻度值,这会很漂亮易于与d3.range结合使用
rangeRound will not help in this case because it relates to the output range of the scale, which in this case is the pixel offset to plot at: between 0 and height.
rangeRound 在这种情况下将无济于事,因为它与比例的输出范围有关,在这种情况下,它是要绘制的像素偏移:介于 0 和高度之间。
回答by Tyler Rick
Going off of Superboggly's answer, this is what worked for me. First I got the max (largest) number from the y domain using y.domain().slice(-1)[0]
and then I built an array of tick values from that using d3.range()
...
离开Superboggly的回答,这对我有用。首先,我使用 y 域中的最大(最大)数y.domain().slice(-1)[0]
,然后使用d3.range()
...
var y_max = y.domain().slice(-1)[0]
var yAxis = d3.svg.axis()
.scale(y)
.tickValues(d3.range(y_max+1))
.tickFormat(d3.format(",.0f"))
回答by kajo
Or just let the ticks as they are and "hide" decimal numbers
或者只是让刻度保持原样并“隐藏”十进制数字
d3.svg.axis()
.scale(y_inverted)
.orient("left")
.ticks(5).tickFormat(function(d) {
if (d % 1 == 0) {
return d3.format('.f')(d)
} else {
return ""
}
});
回答by user3325786
Here is the code:
这是代码:
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));