【问题标题】:Crosshair / x value tooltip for linear scale用于线性刻度的十字准线/x 值工具提示
【发布时间】:2016-06-24 02:30:58
【问题描述】:

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

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

感谢十亿,

仅供参考,我知道让某人为此创建一个要点/块可能是一项艰巨的任务,但我相信拥有一个功能示例块可以接触到许多人并让他们通过示例学习。此外,线性比例尺非常流行,让这种工具提示/十字准线系统适用于这类比例尺将是社区的一大亮点。

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

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

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

    【讨论】:

    • 你是对的,它很容易应用于其他图表。这很酷,因为大多数十字准线模板都是日期格式的。我会为该页面添加书签,即使我喜欢原版的样式,但概念是主要的。
    【解决方案2】:

    我不确定您遇到的问题。

    我以 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 调用。

    一切仍然有效,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>

    【讨论】:

    • 嘿,谢谢你一步一步的解释。你帮助我理解了为什么把日期当作数字,鼠标移动功能的那部分需要改变。问题解决了!
    猜你喜欢
    • 1970-01-01
    • 2015-08-30
    • 1970-01-01
    • 2012-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-05
    相关资源
    最近更新 更多