线性刻度的十字线/x值工具提示 [英] Crosshair / x value tooltip for linear scale
问题描述
由d3noob签出此屏蔽.
http://bl.ocks.org/d3noob/6eb506b129f585ce5c8a
它最初是为日期格式的x轴设计的.而我的x和y轴都是线性的.我想做的是使用此工具提示样式创建带有趋势线的线性x/线性y散点图.我想让这种工具提示/十字准线沿着x轴值移动,因为趋势线在我的图形中上升,就像上述块中的图形一样.实际上,在他的博客上,有人问过类似的问题,如何使其按序运行,但他的回答是那是未知领域".
所以我心想,噢,这没那么难.那是在我仔细查看代码之前和花了几个小时来调整代码之前.因此,我现在对此事有更健康的尊重,这并不像看起来那么容易.
我相信将其更改为线性比例的最复杂部分是:
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
有人可以发布在线性比例x轴上工作的此工具提示块吗?我很想看看你是怎么做到的.我已经尝试了很多(徒劳)的事情,甚至中点公式也是如此.事后看来,我意识到这是一个愚蠢的主意,但是我确实在如何理解代码方面空白.
只要使用该工具提示,任何图都可以.
谢谢十亿,
菲,我知道有人为此创建一个要点/模块可能是一个很高的要求,但是我相信拥有一个功能性的示例模块可以帮助很多人,并让他们通过示例来学习.此外,线性标尺非常受欢迎,并且拥有适用于此类标尺的工具提示/十字准线系统对于社区来说将是一个很大的补充.我不确定您遇到什么问题.
我以d3noob为例,将日期"转换为数字:
date,close
0,606.98
10,614.48
20,617.62
30,609.86
40,599.55
50,618.63
60,629.32
70,624.31
80,633.68
90,636.23
100,628.44
110,626.20
我改变了比例:
var x = d3.scale.linear().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
然后我修复"了data.forEach
以删除日期强制:
data.forEach(function(d) {
d.date = +d.date; //<-- treat date as number
d.close = +d.close;
});
最后删除了mousemove中的formatDate
调用.
一切仍然有效,这是运行该链接的链接.
完整代码:
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
body { font: 12px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
var bisectDate = d3.bisector(function(d) { return d.date; }).left;
// Set the ranges
var x = d3.scale.linear().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
// Adds the svg canvas
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var lineSvg = svg.append("g");
var focus = svg.append("g")
.style("display", "none");
// Get the data
//d3.csv("atad.csv", function(error, data) {
var data = [{"date":"0","close":"606.98"},{"date":"10","close":"614.48"},{"date":"20","close":"617.62"},{"date":"30","close":"609.86"},{"date":"40","close":"599.55"},{"date":"50","close":"618.63"},{"date":"60","close":"629.32"},{"date":"70","close":"624.31"},{"date":"80","close":"633.68"},{"date":"90","close":"636.23"},{"date":"100","close":"628.44"},{"date":"110","close":"626.20"},{"date":"120","close":"622.77"},{"date":"130","close":"605.23"},{"date":"140","close":"580.13"},{"date":"150","close":"543.70"},{"date":"160","close":"443.34"},{"date":"170","close":"345.44"},{"date":"180","close":"234.98"},{"date":"190","close":"166.70"},{"date":"200","close":"130.28"},{"date":"210","close":"99.00"},{"date":"220","close":"89.70"},{"date":"230","close":"67.00"},{"date":"240","close":"53.98"},{"date":"250","close":"58.13"}];
data.forEach(function(d) {
d.date = +d.date;
d.close = +d.close;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// Add the valueline path.
lineSvg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// append the x line
focus.append("line")
.attr("class", "x")
.style("stroke", "blue")
.style("stroke-dasharray", "3,3")
.style("opacity", 0.5)
.attr("y1", 0)
.attr("y2", height);
// append the y line
focus.append("line")
.attr("class", "y")
.style("stroke", "blue")
.style("stroke-dasharray", "3,3")
.style("opacity", 0.5)
.attr("x1", width)
.attr("x2", width);
// append the circle at the intersection
focus.append("circle")
.attr("class", "y")
.style("fill", "none")
.style("stroke", "blue")
.attr("r", 4);
// place the value at the intersection
focus.append("text")
.attr("class", "y1")
.style("stroke", "white")
.style("stroke-width", "3.5px")
.style("opacity", 0.8)
.attr("dx", 8)
.attr("dy", "-.3em");
focus.append("text")
.attr("class", "y2")
.attr("dx", 8)
.attr("dy", "-.3em");
// place the date at the intersection
focus.append("text")
.attr("class", "y3")
.style("stroke", "white")
.style("stroke-width", "3.5px")
.style("opacity", 0.8)
.attr("dx", 8)
.attr("dy", "1em");
focus.append("text")
.attr("class", "y4")
.attr("dx", 8)
.attr("dy", "1em");
// append the rectangle to capture mouse
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
focus.select("circle.y")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")");
focus.select("text.y1")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.text(d.close);
focus.select("text.y2")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.text(d.close);
focus.select("text.y3")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.text(d.date);
focus.select("text.y4")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.text(d.date);
focus.select(".x")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.attr("y2", height - y(d.close));
focus.select(".y")
.attr("transform",
"translate(" + width * -1 + "," +
y(d.close) + ")")
.attr("x2", width + width);
}
//});
</script>
</body>
Check out this block by d3noob.
http://bl.ocks.org/d3noob/6eb506b129f585ce5c8a
It was originally designed for a date formatted x axis. Whereas both my x and y axes are linear. What I'm trying to do is use this tooltip style for a linear x / linear y scatterplot with a trendline. I'd like to have this kind of tooltip/crosshair to go along the x axis values as the trendline ascends in my graph, just like the graph in the above block. Actually on his blog, someone asked a similar question, how to make it work for an ordinal scale, but he answers back with "That is uncharted territory."
So I thought to myself, aw, it can't be that hard. That was before I took a close look at the code and before I spent hours tweaking this and that. So I now have a healthier respect for this thing, it's not as easy as it looks.
I believe the most complicated part in changing it to linear scale is the following:
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
Can someone post a block of this tooltip working within a linear scaled x axis? I'd love to see how you do it. I've tried lots of things (in vain), even the midpoint formula haha. In hindsight, I realize that was a stupid idea, but I was really blanking out on how to make sense of the code.
Any graph is ok, as long as it uses that tooltip.
Thanks a billion,
fyi, I know it may be a tall order to have someone create a gist/block for this, but I believe having a functional example block can reach out to many and let them learn by example. Additionally, linear scales are very popular and having this tooltip/crosshair system working for these kind of scales will be a great addition for the community.
I'm not sure what part you are having trouble with.
I took d3noob's example and converted the "date" to numeric:
date,close
0,606.98
10,614.48
20,617.62
30,609.86
40,599.55
50,618.63
60,629.32
70,624.31
80,633.68
90,636.23
100,628.44
110,626.20
I changed the scale:
var x = d3.scale.linear().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
I then "fixed" the data.forEach
to remove the date coercion:
data.forEach(function(d) {
d.date = +d.date; //<-- treat date as number
d.close = +d.close;
});
And finally removed the formatDate
calls in the mousemove.
Everything still works, here is a link to it running.
Full code:
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
body { font: 12px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
var bisectDate = d3.bisector(function(d) { return d.date; }).left;
// Set the ranges
var x = d3.scale.linear().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
// Adds the svg canvas
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var lineSvg = svg.append("g");
var focus = svg.append("g")
.style("display", "none");
// Get the data
//d3.csv("atad.csv", function(error, data) {
var data = [{"date":"0","close":"606.98"},{"date":"10","close":"614.48"},{"date":"20","close":"617.62"},{"date":"30","close":"609.86"},{"date":"40","close":"599.55"},{"date":"50","close":"618.63"},{"date":"60","close":"629.32"},{"date":"70","close":"624.31"},{"date":"80","close":"633.68"},{"date":"90","close":"636.23"},{"date":"100","close":"628.44"},{"date":"110","close":"626.20"},{"date":"120","close":"622.77"},{"date":"130","close":"605.23"},{"date":"140","close":"580.13"},{"date":"150","close":"543.70"},{"date":"160","close":"443.34"},{"date":"170","close":"345.44"},{"date":"180","close":"234.98"},{"date":"190","close":"166.70"},{"date":"200","close":"130.28"},{"date":"210","close":"99.00"},{"date":"220","close":"89.70"},{"date":"230","close":"67.00"},{"date":"240","close":"53.98"},{"date":"250","close":"58.13"}];
data.forEach(function(d) {
d.date = +d.date;
d.close = +d.close;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// Add the valueline path.
lineSvg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// append the x line
focus.append("line")
.attr("class", "x")
.style("stroke", "blue")
.style("stroke-dasharray", "3,3")
.style("opacity", 0.5)
.attr("y1", 0)
.attr("y2", height);
// append the y line
focus.append("line")
.attr("class", "y")
.style("stroke", "blue")
.style("stroke-dasharray", "3,3")
.style("opacity", 0.5)
.attr("x1", width)
.attr("x2", width);
// append the circle at the intersection
focus.append("circle")
.attr("class", "y")
.style("fill", "none")
.style("stroke", "blue")
.attr("r", 4);
// place the value at the intersection
focus.append("text")
.attr("class", "y1")
.style("stroke", "white")
.style("stroke-width", "3.5px")
.style("opacity", 0.8)
.attr("dx", 8)
.attr("dy", "-.3em");
focus.append("text")
.attr("class", "y2")
.attr("dx", 8)
.attr("dy", "-.3em");
// place the date at the intersection
focus.append("text")
.attr("class", "y3")
.style("stroke", "white")
.style("stroke-width", "3.5px")
.style("opacity", 0.8)
.attr("dx", 8)
.attr("dy", "1em");
focus.append("text")
.attr("class", "y4")
.attr("dx", 8)
.attr("dy", "1em");
// append the rectangle to capture mouse
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
focus.select("circle.y")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")");
focus.select("text.y1")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.text(d.close);
focus.select("text.y2")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.text(d.close);
focus.select("text.y3")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.text(d.date);
focus.select("text.y4")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.text(d.date);
focus.select(".x")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.attr("y2", height - y(d.close));
focus.select(".y")
.attr("transform",
"translate(" + width * -1 + "," +
y(d.close) + ")")
.attr("x2", width + width);
}
//});
</script>
</body>
这篇关于线性刻度的十字线/x值工具提示的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!