D3.js - 如何在拖动线图时解决NAN错误(提供的Jsfiddle)

时间:2014-09-25 21:55:58

标签: javascript jquery svg d3.js data-visualization

我最近开始研究D3.js.我正在开发具有双x轴的交互式多线图。请看看我的工作,JSFIDDLE在这里:http://jsfiddle.net/dalapati/cdkn14j3/1/

    data1 = [
                    {"date": 1357717800000,"value": "5.6"}, 
                    {"date": 1357718400000,"value": "5.6"}, 
                    {"date": 1357719000000,"value": "6"}, 
                    {"date": 1357719600000,"value": "5.1"},
                    {"date": 1357720200000,"value": "5.3"},
                    //{"date": 1357720800000,"value": "5.4"}
                ];

    data2 = [
                    {"date": 1357714800000,"value": "5.2"},
                    {"date": 1357715400000,"value": "5.2"},
                    {"date": 1357716000000,"value": "5.2"}, 
                    {"date": 1357716600000,"value": "5.1"},
                    {"date": 1357717200000,"value": "5.5"}, 
            ]      

// date manipulation to format UTC to js Date obj           
    data1.forEach(function(d){ d.time = new Date(d.time * 1000);});
    data2.forEach(function(d){ d.time = new Date(d.time * 1000);});

// helpers and constants        
var margin = {"top": 50, "right": 50, "bottom": 50, "left": 100, "axis": 55};
var width = 1500 - margin.left - margin.right;
var height = 580 - margin.top - margin.bottom;
var timeFormat = d3.time.format("%X");

// find data range
var x1Domain = d3.extent(data1, function(d){ return d.date; });
var x2Domain = d3.extent(data2, function(d){ return d.date; });

var x1Min = d3.min(data1, function(d){ return Math.min(d.date); });
var x1Max = d3.max(data1, function(d){ return Math.max(d.date); });

var x2Min = d3.min(data2, function(d){ return Math.min(d.date); });
var x2Max = d3.max(data2, function(d){ return Math.max(d.date); });

var y1Min = d3.min(data1, function(d){ return Math.min(d.value); });
var y1Max = d3.max(data1, function(d){ return Math.max(d.value); });

var y2Min = d3.min(data2, function(d){ return Math.min(d.value); });
var y2Max = d3.max(data2, function(d){ return Math.max(d.value); });

var yMin = (y1Min < y2Min) ? y1Min:y2Min;
var yMax = (y1Max > y2Max) ? y1Max:y2Max;

// scales
var x1Scale = d3.time.scale()
                    .domain(d3.extent(data1, function (d) {
                        return d.date;}))
                    .range([0, width]);
var x2Scale = d3.time.scale()
                    .domain(d3.extent(data2, function (d) {
                        return d.date;}))
                    .range([0, width]);
var yScale = d3.scale.linear()
                    .domain([yMin,yMax]).range([height, 0]); 

// set up axes
var x1Axis = d3.svg.axis()
                    .scale(x1Scale)
                    .orient("bottom")
                    .ticks(5)
                    .tickPadding(5)
                    .tickFormat(timeFormat);
var x2Axis = d3.svg.axis()
                    .scale(x2Scale)
                    .orient("top")
                    .ticks(5)
                    .tickPadding(5)
                    .tickFormat(timeFormat);
var yAxis = d3.svg.axis()
                    .scale(yScale)
                    .orient("left")
                    .ticks(5);
var make_y_axis = function () {
                            return d3.svg.axis()
                                .scale(yScale)
                                .orient("left")
                                .ticks(5);
                        };

// Set up chart type
// create a line function that can convert data into x and y points
var line1 = d3.svg.line().interpolate("basis")
                .x(function (d) {
                    return x1Scale(d.date);
                })
                .y(function (d) {
                    return yScale(d.value);
                });
var line2 = d3.svg.line().interpolate("basis")
                .x(function (d) {
                    return x2Scale(d.date);
                })
                .y(function (d) {
                    return yScale(d.value);
                });


//   Create Zoom feature
var zoomBottom = d3.behavior.zoom()
                    .x(x1Scale)
                    //.y(yScale);
                    .scaleExtent([1,10]);
var zoom = d3.behavior.zoom()
                    .x(x2Scale)
                    //.y(yScale)
                    .scaleExtent([1,10])
                    .on("zoom",zoomed);


// Create Drag behaviour
var drag = d3.behavior.drag()
            .origin(function(d){return d;})
            .on("dragstart", dragstarted)
            .on("drag", dragged)
            .on("dragend", dragended)


// create svg container
var svg = d3.select('#chart')
                .append("svg:svg")
                .attr('width', width + margin.left + margin.right)
                .attr('height', height + margin.top + margin.bottom)
                .append("svg:g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
                .call(zoom);

    svg.append("svg:rect")
                .attr("width", width)
                .attr("height", height)
                .attr("class", "plot");

function draw(){
 //Draw Axes
    svg.append("svg:g")
        .attr("class", "x axis axisBottom")
        .attr("transform", "translate(0, " + height + ")")
        .call(x1Axis);

    svg.append("svg:g")
        .attr("class", "x axis axisTop")
        .attr("transform", "translate(0, 0)")
        .call(x2Axis);

    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis);

// grid plot
    svg.append("g")
        .attr("class", "y grid")
        .call(make_y_axis()
        .tickSize(-width, 0, 0)
        .tickFormat(""));

 // add lines
// do this AFTER the axes above so that the line is above the tick-lines
var clip = svg.append("svg:clipPath")
                    .attr("id", "clip")
                    .append("svg:rect")
                    .attr("x1Scale", 0)
                    .attr("x2Scale", 0)
                    .attr("yScale", 0)
                    .attr("width", width)
                    .attr("height", height);

var chartBody = svg.append("g")
        .attr("clip-path", "url(#clip)");

chartBody.append("svg:path")
        .datum(data1)
        .attr("class", "data1")
        .attr("d", line1(data1))
        .attr("cursor", "move")
        .call(drag);

    chartBody.append("svg:path")
        .datum(data2)
        .attr("class", "data2")
        .attr("d", line2(data2))
        .attr("cursor", "move")
        .call(drag);
    }  

    draw();  
/************************** ADDING ZOOMING FEATURE****************************************/

function zoomed() {
        //console.log(d3.event.translate);
        //console.log(d3.event.scale);        
        zoomBottom.scale(zoom.scale()).translate(zoom.translate());
        svg.select(".axisBottom").call(x1Axis);
        svg.select(".axisTop").call(x2Axis);
        svg.select(".y .axis").call(yAxis);
        svg.select(".grid").call(make_y_axis().tickSize(-width,0,0).tickFormat(""));
        svg.select(".data1").attr("d",line1(data1));
        svg.select(".data2").attr("d",line2(data2));
    }

/***************** Adding Dragging feature*****************************************************/

function dragstarted(d){
    d3.event.sourceEvent.stopPropagation();
    d3.select(this).classed("dragging", true);
}

function dragged(d){
    var lineToMove = d3.event.x;
    console.log(lineToMove);
    d3.select(this)
        .attr("cx", d[0] = d3.event.sourceEvent.x);
}

function dragended(d){
    d3.select(this).classed("dragging", false);
}

如您所见,有两个不同的折线图,取自不同的数据源并相对于两个不同的x轴绘制。我正在尝试将拖动行为实现到每个线图,其中如果我选择一个图并拖动它并保持另一个图不变,则相应的轴值也应该更新。

但是当我尝试拖动折线图时,我收到了NAN错误。我不知道如何解决这个错误。有谁知道我哪里出错了。

提前致谢。

1 个答案:

答案 0 :(得分:2)

问题是当您设置原点时,因为它期望具有x和y的对象。

尝试:

.origin(function(){ var t = d3.select(this); return { x: t.attr('x') , y: t.attr('y') } })

参考How to set the Origin (drag.origin) for drag behavior in d3 JavaScript library