【问题标题】:D3.js: Moving circles along with line in a 2D graph when zoomedD3.js:缩放时在 2D 图形中沿线移动圆圈
【发布时间】:2016-04-12 08:31:52
【问题描述】:

我目前正在尝试在 d3.js 中绘制时间序列数据。我为每个数据点渲染了一条线并绘制了圆圈(将来圆圈将用于注释特定的数据点)。我正在尝试使用 d3.js 中的“缩放”行为来缩放所有组件。但我无法沿线拖动和缩放圆圈。

我怎样才能沿着线移动圆圈。以下是代码的jsfiddle:

https://jsfiddle.net/adityap16/4sts8nfs/2/

代码:

var data = [{
  "mytime": "2015-12-01T23:10:00.000Z",
  "value": 64
}, {
  "mytime": "2015-12-01T23:15:00.000Z",
  "value": 67
}, {
  "mytime": "2015-12-01T23:20:00.000Z",
  "value": 70
}, {
  "mytime": "2015-12-01T23:25:00.000Z",
  "value": 64
}, {
  "mytime": "2015-12-01T23:30:00.000Z",
  "value": 72
}, {
  "mytime": "2015-12-01T23:35:00.000Z",
  "value": 75
}, {
  "mytime": "2015-12-01T23:40:00.000Z",
  "value": 71
}, {
  "mytime": "2015-12-01T23:45:00.000Z",
  "value": 80
}, {
  "mytime": "2015-12-01T23:50:00.000Z",
  "value": 83
}, {
  "mytime": "2015-12-01T23:55:00.000Z",
  "value": 86
}, {
  "mytime": "2015-12-02T00:00:00.000Z",
  "value": 80
}, {
  "mytime": "2015-12-02T00:05:00.000Z",
  "value": 85
}];
var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;

data.forEach(function(d) {
  d.mytime = parseDate(d.mytime);
});
//var margin = { top: 30, right: 30, bottom: 40, left:50 },
var margin = {
    top: 30,
    right: 30,
    bottom: 40,
    left: 50
  },
  height = 200,
  width = 900;
var color = "green";
var xaxis_param = "mytime";
var yaxis_param = "value";
var params1 = {
  margin: margin,
  height: height,
  width: width,
  color: color,
  xaxis_param: xaxis_param,
  yaxis_param: yaxis_param
};

draw_graph(data, params1);

function draw_graph(data, params) {

  var make_x_axis = function() {
    return d3.svg.axis()
      .scale(x_scale)
      .orient("bottom")
      .ticks(5);
  };

  var make_y_axis = function() {
    return d3.svg.axis()
      .scale(y_scale)
      .orient("left")
      .ticks(5);
  };

  //Get the margin
  var xaxis_param = params.xaxis_param;
  var yaxis_param = params.yaxis_param;
  var color_code = params.color;
  var margin = params.margin;
  var height = params.height - margin.top - margin.bottom,
    width = params.width - margin.left - margin.right;

  var x_extent = d3.extent(data, function(d) {
    return d[xaxis_param]
  });

  var y_extent = d3.extent(data, function(d) {
    return d[yaxis_param]
  });

  var x_scale = d3.time.scale()
    .domain(x_extent)
    .range([0, width]);

  var y_scale = d3.scale.linear()
    .domain([0, y_extent[1]])
    .range([height, 0]);

  var zoom = d3.behavior.zoom()
    .x(x_scale)
    .y(y_scale)
    .on("zoom", zoomed);


  //Line
  var line = d3.svg.line()
    .defined(function(d) {
      return d[yaxis_param];
    })
    .x(function(d) {
      return x_scale(d[xaxis_param]);
    })
    .y(function(d) {
      return y_scale(d[yaxis_param]);
    });

  var lineRef = d3.svg.line()
    .x(function(d) {
      return x_scale(d[xaxis_param]);
    })
    .y(function(d) {
      return y_scale(20);
    });

  var myChart = d3.select('body').append('svg')
    .attr('id', 'graph')
    .style('background', '#E7E0CB')
    .attr('width', width + margin.left + margin.right)
    .attr('height', height + margin.top + margin.bottom)
    .append('g')
    .attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')')
    .call(zoom);

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


  var legend = myChart.append("g")
    .attr("class", "legend")
    .attr("transform", "translate(" + 5 + "," + (height - 25) + ")")

  legend.append("rect")
    .style("fill", color_code)
    .attr("width", 20)
    .attr("height", 20);

  legend.append("text")
    .text(yaxis_param)
    .attr("x", 25)
    .attr("y", 12);


  var vAxis = d3.svg.axis()
    .scale(y_scale)
    .orient('left')
    .ticks(5)


  var hAxis = d3.svg.axis()
    .scale(x_scale)
    .orient('bottom')
    .ticks(5);

  var majorAxis = d3.svg.axis()
    .scale(x_scale)
    .orient('bottom')
    .ticks(d3.time.day, 1)
    .tickSize(-height)
    .outerTickSize(0);

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

  myChart.append("g")
    .attr("class", "x axis major")
    .attr("transform", "translate(0," + height + ")")
    .call(majorAxis);

  myChart.append("g")
    .attr("class", "y axis")
    .call(vAxis);

  var circlePoint = myChart.selectAll('circle')
                         .data(data)
                         .enter()
                         .append("circle");

    var circleAttributes = circlePoint
                           .attr("cx", function (d) { return x_scale(d[xaxis_param]); })
                           .attr("cy", function (d) { return y_scale(d[yaxis_param]); })
                           .attr("r", 3)
                           .style("fill", "none")
                           .style("stroke", "red");

    var clip = myChart.append("svg:clipPath")
    .attr("id", "clip")
    .append("svg:rect")
    .attr("x", 0)
    .attr("y", 0)
    .attr("width", width)
    .attr("height", height);

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



chartBody.append("svg:path")
    .datum(data)
    .attr('class', 'line')
    .attr("d", line)
    .attr('stroke', color_code)
    .attr('stroke-width', 1)
    .attr('fill', 'none');

  chartBody
    .append('svg:path')
    .datum(data)
    .attr('class', 'line1')
    .attr("d", lineRef)
    .attr('stroke', 'blue')
    .attr('stroke-width', 1)
    .style("stroke-dasharray", ("3, 3"))
    .attr('fill', 'none');

  function zoomed() {
    myChart.select(".x.axis").call(hAxis);
    myChart.select(".y.axis").call(vAxis);
    myChart.select(".x.axis.major").call(majorAxis);
    myChart.select(".line")
        .attr("class", "line")
        .attr("d", line);
    myChart.select(".line1")
        .attr("class", "line1")
        .attr("d", lineRef);


  }
}

【问题讨论】:

    标签: javascript d3.js svg zooming


    【解决方案1】:

    您只需要在 zoomed() 处理函数中更新圈子的位置:

    circlePoint
      .attr("cx", function (d) { return x_scale(d[xaxis_param]); })
      .attr("cy", function (d) { return y_scale(d[yaxis_param]); });
    

    因为 D3 的缩放行为会更新比例,所以它们可以很容易地用于计算新的位置。

    查看这个 sn-p 以获得完整示例:

    var data = [{
      "mytime": "2015-12-01T23:10:00.000Z",
      "value": 64
    }, {
      "mytime": "2015-12-01T23:15:00.000Z",
      "value": 67
    }, {
      "mytime": "2015-12-01T23:20:00.000Z",
      "value": 70
    }, {
      "mytime": "2015-12-01T23:25:00.000Z",
      "value": 64
    }, {
      "mytime": "2015-12-01T23:30:00.000Z",
      "value": 72
    }, {
      "mytime": "2015-12-01T23:35:00.000Z",
      "value": 75
    }, {
      "mytime": "2015-12-01T23:40:00.000Z",
      "value": 71
    }, {
      "mytime": "2015-12-01T23:45:00.000Z",
      "value": 80
    }, {
      "mytime": "2015-12-01T23:50:00.000Z",
      "value": 83
    }, {
      "mytime": "2015-12-01T23:55:00.000Z",
      "value": 86
    }, {
      "mytime": "2015-12-02T00:00:00.000Z",
      "value": 80
    }, {
      "mytime": "2015-12-02T00:05:00.000Z",
      "value": 85
    }];
    var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;
    
    data.forEach(function(d) {
      d.mytime = parseDate(d.mytime);
    });
    //var margin = { top: 30, right: 30, bottom: 40, left:50 },
    var margin = {
        top: 30,
        right: 30,
        bottom: 40,
        left: 50
      },
      height = 200,
      width = 900;
    var color = "green";
    var xaxis_param = "mytime";
    var yaxis_param = "value";
    var params1 = {
      margin: margin,
      height: height,
      width: width,
      color: color,
      xaxis_param: xaxis_param,
      yaxis_param: yaxis_param
    };
    
    draw_graph(data, params1);
    
    function draw_graph(data, params) {
    
      var make_x_axis = function() {
        return d3.svg.axis()
          .scale(x_scale)
          .orient("bottom")
          .ticks(5);
      };
    
      var make_y_axis = function() {
        return d3.svg.axis()
          .scale(y_scale)
          .orient("left")
          .ticks(5);
      };
    
      //Get the margin
      var xaxis_param = params.xaxis_param;
      var yaxis_param = params.yaxis_param;
      var color_code = params.color;
      var margin = params.margin;
      var height = params.height - margin.top - margin.bottom,
        width = params.width - margin.left - margin.right;
    
      var x_extent = d3.extent(data, function(d) {
        return d[xaxis_param]
      });
    
      var y_extent = d3.extent(data, function(d) {
        return d[yaxis_param]
      });
    
      var x_scale = d3.time.scale()
        .domain(x_extent)
        .range([0, width]);
    
      var y_scale = d3.scale.linear()
        .domain([0, y_extent[1]])
        .range([height, 0]);
    
      var zoom = d3.behavior.zoom()
        .x(x_scale)
        .y(y_scale)
        .on("zoom", zoomed);
    
    
      //Line
      var line = d3.svg.line()
        .defined(function(d) {
          return d[yaxis_param];
        })
        .x(function(d) {
          return x_scale(d[xaxis_param]);
        })
        .y(function(d) {
          return y_scale(d[yaxis_param]);
        });
    
      var lineRef = d3.svg.line()
        .x(function(d) {
          return x_scale(d[xaxis_param]);
        })
        .y(function(d) {
          return y_scale(20);
        });
    
      var myChart = d3.select('body').append('svg')
        .attr('id', 'graph')
        .style('background', '#E7E0CB')
        .attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom)
        .append('g')
        .attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')')
        .call(zoom);
    
       myChart.append("svg:rect")
        .attr("width", width)
        .attr("height", height)
        .attr("class", "plot");
    
    
      var legend = myChart.append("g")
        .attr("class", "legend")
        .attr("transform", "translate(" + 5 + "," + (height - 25) + ")")
    
      legend.append("rect")
        .style("fill", color_code)
        .attr("width", 20)
        .attr("height", 20);
    
      legend.append("text")
        .text(yaxis_param)
        .attr("x", 25)
        .attr("y", 12);
    
    
      var vAxis = d3.svg.axis()
        .scale(y_scale)
        .orient('left')
        .ticks(5)
    
    
      var hAxis = d3.svg.axis()
        .scale(x_scale)
        .orient('bottom')
        .ticks(5);
    
      var majorAxis = d3.svg.axis()
        .scale(x_scale)
        .orient('bottom')
        .ticks(d3.time.day, 1)
        .tickSize(-height)
        .outerTickSize(0);
    
      myChart.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(hAxis);
    
      myChart.append("g")
        .attr("class", "x axis major")
        .attr("transform", "translate(0," + height + ")")
        .call(majorAxis);
    
      myChart.append("g")
        .attr("class", "y axis")
        .call(vAxis);
    
      var circlePoint = myChart.selectAll('circle')
                             .data(data)
                             .enter()
                             .append("circle");
    
        var circleAttributes = circlePoint
                               .attr("cx", function (d) { return x_scale(d[xaxis_param]); })
                               .attr("cy", function (d) { return y_scale(d[yaxis_param]); })
                               .attr("r", 3)
                               .style("fill", "none")
                               .style("stroke", "red");
    
        var clip = myChart.append("svg:clipPath")
        .attr("id", "clip")
        .append("svg:rect")
        .attr("x", 0)
        .attr("y", 0)
        .attr("width", width)
        .attr("height", height);
    
        var chartBody = myChart.append("g")
        .attr("clip-path", "url(#clip)");
    
    
    
    chartBody.append("svg:path")
        .datum(data)
        .attr('class', 'line')
        .attr("d", line)
        .attr('stroke', color_code)
        .attr('stroke-width', 1)
        .attr('fill', 'none');
    
      chartBody
        .append('svg:path')
        .datum(data)
        .attr('class', 'line1')
        .attr("d", lineRef)
        .attr('stroke', 'blue')
        .attr('stroke-width', 1)
        .style("stroke-dasharray", ("3, 3"))
        .attr('fill', 'none');
    
      function zoomed() {
        myChart.select(".x.axis").call(hAxis);
        myChart.select(".y.axis").call(vAxis);
        myChart.select(".x.axis.major").call(majorAxis);
        myChart.select(".line")
            .attr("class", "line")
            .attr("d", line);
        myChart.select(".line1")
            .attr("class", "line1")
            .attr("d", lineRef);
    
        circlePoint
          .attr("cx", function (d) { return x_scale(d[xaxis_param]); })
          .attr("cy", function (d) { return y_scale(d[yaxis_param]); });
    
      }
    }
    svg {
      font: 10px sans-serif;
    }  
    
    .plot {
        fill: rgba(250, 250, 255, 0.6);
    }
    .axis path,
    .axis line {
        fill: none;
        stroke: black;
        shape-rendering: crispEdges;
    }
    
    .axis text {
        font-family: sans-serif;
        font-size: 11px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

    【讨论】:

    • 开枪!我试图通过调用 "myChart.attr('class','circle') ....." 来使其工作。谢谢这就像一个魅力:)。如果超过某个“值”,你知道我可以控制圆圈颜色的方法吗?
    • @AdityaPatel 切换颜色可以通过 circlePoint.attr("stroke", function(d) { return d[yaxis_param] > THRESHOLD ? "red" : "green"; }) 之类的方式完成。
    猜你喜欢
    • 1970-01-01
    • 2017-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多