线性刻度的十字线/x值工具提示 [英] Crosshair / x value tooltip for linear scale

查看:68
本文介绍了线性刻度的十字线/x值工具提示的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

由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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆