用于线性刻度的十字准线/ x值工具提示

时间:2016-06-23 15:50:42

标签: javascript d3.js

通过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轴上发布此工具提示的块吗?我很乐意看到你是怎么做到的。我尝试了很多东西(徒劳),甚至是中点公式哈哈。事后看来,我意识到这是一个愚蠢的想法,但我真的在知道如何理解代码。

任何图表都可以,只要它使用该工具提示。

感谢十亿,

fyi,我知道让某人为此创建一个要点/块可能是一个很高的命令,但我相信有一个功能性的示例块可以与许多人联系并让他们通过示例来学习。此外,线性刻度非常受欢迎,并且这个工具提示/十字准线系统适用于这种尺度将是社区的一个很好的补充。

2 个答案:

答案 0 :(得分:1)

我不确定你遇到什么问题。

我拿了d3noob的例子并将“date”转换为数字:

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次调用。

一切仍然有效,here is a link to it running.

完整代码:

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

答案 1 :(得分:0)

你是说这样的意思吗? https://jsfiddle.net/gerardofurtado/ayta89cz/5/

在这个小提琴中,我只是使用d3.mouse()来获取鼠标在透明矩形上的位置,并使用两条虚线作为十字准线。您可以轻松地将这些值转换为相对x刻度和y刻度值。我在这里硬编码数学因为我知道我刚创建的趋势线的斜率,但你必须根据你的趋势线修改数学。