d3.bisector使用Date()对象无法解析

时间:2014-05-27 13:46:02

标签: javascript d3.js bisection

Demo jsFiddle

我有一个基本 d3线图,使用UNIX timestamp和float value数据的简单JSON数组,例如:

"value": 10.04,"time": 1401185375354 [...]

在生成图表之前,此时间戳(time)数据将转换为Date()对象。一切都很好,直到我想在悬停时添加标记。为了获得正确的y值,我暂时使用bisector并在当前悬停点传递x的值。因此,我目前有:

var bisect = d3.bisector(function(data) {
  return data.time;
}).right;

然后在悬停功能中:

var timestamp = xScale.invert(mouse[0]),
    index = bisect(data, timestamp),
    startDatum = data[index - 1],
    endDatum = data[index],
    interpolate = d3.interpolateNumber(startDatum.value, endDatum.value),
    range = endDatum.timestamp - startDatum.timestamp,
    valueY = interpolate((timestamp % range) / range);
    marker.attr('cy', yScale(valueY));

但是,bisector返回无效值。表示它不能将传递的参数(日期对象)与数组项等同。任何帮助将不胜感激,完整的代码如下:

HTML

<svg id="graph"></svg>

D3

var JSONData = [{
    "value": 10.04,
        "time": 1401185375354
}, {
    "value": 0.02,
        "time": 1401185374854
}, {
    "value": 1.61,
        "time": 1401185373854
}, {
    "value": 8.47,
        "time": 1401185373353
}, {
    "value": 1.65,
        "time": 1401185372852
}, {
    "value": 0.46,
        "time": 1401185371852
}, {
    "value": 3.17,
        "time": 1401185370888
}]
JSONData.forEach(function (d) {
    d.time = new Date(d.time);
});
data = JSONData.slice();
var margin = 45,
    width = parseInt(d3.select("#graph").style("width")) - margin * 2,
    height = parseInt(d3.select("#graph").style("height")) - margin * 2;

var timeFormat = d3.time.format("%I:%M:%S");

var xMin = d3.min(data, function (d) {
    return Math.min(d.time);
});
var xMax = d3.max(data, function (d) {
    return Math.max(d.time);
});

var yMin = d3.min(data, function (d) {
    return Math.min(d.value);
});
var yMax = d3.max(data, function (d) {
    return Math.max(d.value);
});

var xScale = d3.time.scale().domain(d3.extent(data, function (d) {
    return d.time;
})).range([0, width]);

var yScale = d3.scale.linear().domain(d3.extent(data, function (d) {
    return d.value;
})).range([height, 0]);

var xAxis = d3.svg.axis().scale(xScale).orient("bottom").tickFormat(timeFormat);

var yAxis = d3.svg.axis().scale(yScale).orient("left");

var line = d3.svg.line().x(function (d) {
    return xScale(d.time);
}).y(function (d) {
    return yScale(d.value);
});

var line2 = d3.svg.line().x(function (d) {
    return xScale(d.time);
}).y(function (d) {
    return yScale(d.askPrice);
});

var graph = d3.select("#graph").attr("width", width + margin * 2).attr("height", height + margin * 2).append("g").attr("transform", "translate(" + margin + "," + margin + ")");

graph.append("g").attr("class", "x axis").attr("transform", "translate(0," + height + ")").call(xAxis);

graph.append("g").attr("class", "y axis").call(yAxis).append("text").attr("transform", "rotate(-90)").attr("y", 6).attr("dy", ".71em").style("text-anchor", "end");

graph.append("path").datum(data).attr("class", "line").style('pointer-events', 'none').attr("d", line);

graph.append("path").datum(data).attr("class", "line2").style('pointer-events', 'none').attr("d", line2)

var marker = graph.append('circle').attr('r', 7).style('fill', '#FFFFFF').style('pointer-events', 'none').style('stroke', '#FB5050').style('stroke-width', '3px');

graph.append("rect").attr("class", "overlay").attr("width", width).attr("height", height).on("mouseover", function () {
    focus.style("display", "block");
}).on("mouseout", function () {
    focus.style("display", "none");
}).on("mousemove", update);

var bisect = d3.bisector(function (date) {
    return date.time;
}).right;

function update() {
    var mouse = d3.mouse(this);
    marker.attr('cx', mouse[0]);
    /* the issue is with the below */
    var timestamp = xScale.invert(mouse[0]),
        index = bisect(data, timestamp),
        startDatum = data[index - 1],
        endDatum = data[index],
        interpolate = d3.interpolateNumber(startDatum.value, endDatum.value),
        range = endDatum.timestamp - startDatum.timestamp,
        valueY = interpolate((timestamp % range) / range);
    marker.attr('cy', yScale(valueY));
}

CSS

html, body {
    height: 100%;
    width: 100%;
    margin: 0;
    padding: 0;
    font-family:arial;
    font-size:11px;
    color:#AAAAAA;
    overflow:hidden;
}
.overlay {
    fill: none;
    pointer-events: all;
}
.axis path, .axis line {
    fill: none;
    stroke: #555;
    shape-rendering: crispEdges;
}
.axis text {
    fill: #555;
}
.line {
    fill: none;
    stroke: red;
    stroke-width: 1.5px;
}

1 个答案:

答案 0 :(得分:7)

您当前的代码存在一些问题:

  • d3.bisect仅适用于已排序的数据。已修复data.sort(function(a, b) { return a.time - b.time; });
  • 在鼠标悬停功能中,您引用的是不存在的.timestamp属性,而不是.time
  • 插值的计算不正确。应该是interpolate((timestamp - startDatum.time) / range);

包含修正here的完整演示。您可能还会发现this example有帮助。