带有比例带标签的平滑滚动画笔

时间:2018-06-03 22:39:46

标签: d3.js zoom linear smooth-scrolling pan

问题:使用画笔时,使用比例带并不能平稳地平移。

https://shanegibney.github.io/D3-v4-Bar-chart-Scaleband-with-Brush/

以下是在x轴上使用日期进行平滑平移的示例。

https://shanegibney.github.io/d3-Bar-Chart-Pan-And-Zoom/

但这只适用于x-domain连续的情况。

使用linearscale,我想用数据d.name中的字符串替换x轴上的值。问题是当缩放时,在值之间创建新的刻度。例如,缩放时的值1和2将产生1,1和2和2.5等的值和tciks。这意味着名称不再匹配。

这可以通过两件事来解决;

  1. 防止放大时出现新的滴答声。我不知道该怎么做。

  2. 用数据中的d.name替换x轴刻度上的每个标签

  3. 这些是我尝试这样做的,

    var xAxis = d3.axisBottom(x)
        .tickFormat(function(d, i) {
          label = data[i].name;
          local.set(this, data[i].name);
          return "test";
          return data[i].name;
          return label;
          return i;
          return d;
        });
    

    这些都不起作用。

    The Plunker

    https://plnkr.co/edit/zbWu6AV8MNO9N8m0byre

1 个答案:

答案 0 :(得分:1)

tickFormat函数内,检查数字刻度是否对应data数组中的任何值。如果是,请返回name,否则不返回任何内容:

var xAxis = d3.axisBottom(x)
    .tickFormat(function(d) {
        var found = data.find(function(e){
            return e.num === d
        });
        return found ? found.name : null; 
    })

以下是具有该更改的代码:

<!DOCTYPE html>
<meta charset="utf-8">

<style type="text/css">
  body {
    font-family: avenir next, sans-serif;
    font-size: 12px;
  }

  .zoom {
    cursor: move;
    fill: none;
    pointer-events: all;
  }

  .axis {
    stroke-width: 0.5px;
    stroke: #888;
    font: 10px avenir next, sans-serif;
  }

  .axis>path {
    stroke: #888;
  }

</style>

<body>
  <div id="totalDistance">
  </div>
</body>

<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
  /* Adapted from: https://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172 */
  var data = [{
      "name": "A",
      "date": "2016-11-09 11:15",
      "num": "1",
      "distance": "1900"
    },
    {
      "name": "B",
      "date": "2016-11-10 10:40",
      "num": "2",
      "distance": "1500"
    },
    {
      "name": "C",
      "date": "2016-11-11 16:45",
      "num": "3",
      "distance": "2500"
    },
    {
      "name": "D",
      "date": "2016-11-12 12:48",
      "num": "4",
      "distance": "2300"
    },
    {
      "name": "E",
      "date": "2016-11-15 20:00",
      "num": "5",
      "distance": "2000"
    }
  ];

  var margin = {
      top: 20,
      right: 20,
      bottom: 90,
      left: 50
    },
    margin2 = {
      top: 230,
      right: 20,
      bottom: 30,
      left: 50
    },
    width = 960 - margin.left - margin.right,
    height = 300 - margin.top - margin.bottom,
    height2 = 300 - margin2.top - margin2.bottom;

  var parseTime = d3.timeParse("%Y-%m-%d %H:%M");

  var local = d3.local();

  var x = d3.scaleLinear().range([0, width]),
    x2 = d3.scaleLinear().range([0, width]),
    y = d3.scaleLinear().range([height, 0]),
    y2 = d3.scaleLinear().range([height2, 0]);
  // dur = d3.scaleLinear().range([0, 12]);

  var tickLabels = ['a', 'b', 'c', 'd'];

  var newData = [];
  data.forEach(function(e) {
    newData.push({
      "name": e.name
    });
  });

  //var xAxis = d3.axisBottom(x).tickSize(0);
  var xAxis2 = d3.axisBottom(x2).tickSize(0)
    .tickFormat(function(d) {
      var found = data.find(function(e) {
        return e.num === d
      });
      return found ? found.name : null;
    })
  yAxis = d3.axisLeft(y).tickSize(0);

  var xAxis = d3.axisBottom(x)
    .tickFormat(function(d) {
      var found = data.find(function(e) {
        return e.num === d
      });
      if (!found) {
        d3.select(this.parentNode).select("line").remove()
      }
      return found ? found.name : null;
    })

  // var xAxis = d3.axisBottom(x)
  //   .tickFormat(function(d, i) {
  //     label = data[i].name;
  //     local.set(this, data[i].name);
  //     return "test";
  //     return data[i].name;
  //     return label;
  //     return i;
  //     return d;
  //   });

  var brush = d3.brushX()
    .extent([
      [0, 0],
      [width, height2]
    ])
    .on("start brush end", brushed);

  var zoom = d3.zoom()
    .scaleExtent([1, 10])
    .translateExtent([
      [0, 0],
      [width, height]
    ])
    .extent([
      [0, 0],
      [width, height]
    ])
    .on("zoom", zoomed);

  var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom);

  svg.append("defs").append("clipPath")
    .attr("id", "clip")
    .append("rect")
    .attr("width", width)
    .attr("height", height);

  var focus = svg.append("g")
    .attr("class", "focus")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var context = svg.append("g")
    .attr("class", "context")
    .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");

  // d3.json("data.json", function(error, data) {
  // if (error) throw error;

  var parseTime = d3.timeParse("%Y-%m-%d %H:%M");
  var mouseoverTime = d3.timeFormat("%a %e %b %Y %H:%M");
  var minTime = d3.timeFormat("%b%e, %Y");
  var parseDate = d3.timeParse("%b %Y");

  data.forEach(function(d) {
      d.distance = +d.distance;
      d.num = +d.num;
      return d;
    },
    function(error, data) {
      if (error) throw error;
    });

  var total = 0;

  data.forEach(function(d) {
    total = d.distance + total;
  });

  var minDate = d3.min(data, function(d) {
    return d.num;
  });

  var xMin = d3.min(data, function(d) {
    return d.num;
  });

  var yMax = Math.max(20, d3.max(data, function(d) {
    return d.distance;
  }));

  x.domain([xMin, d3.max(data, function(d) {
    return d.num;
  })]);
  y.domain([0, yMax]);
  x2.domain(x.domain());
  y2.domain(y.domain());

  var rects = focus.append("g");
  rects.attr("clip-path", "url(#clip)");
  rects.selectAll("rects")
    .data(data)
    .enter().append("rect")
    .style("fill", function(d) {
      return "#8DA5ED";
    })
    .attr("class", "rects")
    .attr("x", function(d) {
      return x(d.num);
    })
    .attr("y", function(d) {
      return y(d.distance);
    })
    .attr("width", function(d) {
      return 10;
    })
    .attr("height", function(d) {
      return height - y(d.distance);
    });

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

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

  // Summary Stats
  focus.append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 0 - margin.left)
    .attr("x", 0 - (height / 2))
    .attr("dy", "1em")
    .style("text-anchor", "middle")
    .text("Distance in meters");

  svg.append("text")
    .attr("transform",
      "translate(" + ((width + margin.right + margin.left) / 2) + " ," +
      (height + margin.top + margin.bottom) + ")")
    .style("text-anchor", "middle")
    .text("Date");

  svg.append("rect")
    .attr("class", "zoom")
    .attr("width", width)
    .attr("height", height)
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .call(zoom);

  var rects = context.append("g");
  rects.attr("clip-path", "url(#clip)");
  rects.selectAll("rects")
    .data(data)
    .enter().append("rect")
    .style("fill", function(d) {
      return "#8DA5ED";
    })
    .attr("class", "rects")
    .attr("x", function(d) {
      return x2(d.num);
    })
    .attr("y", function(d) {
      return y2(d.distance);
    })
    .attr("width", function(d) {
      return 10;
    })
    .attr("height", function(d) {
      return height2 - y2(d.distance);
    });

  context.append("g")
    .attr("class", "axis x-axis")
    .attr("transform", "translate(0," + height2 + ")")
    .call(xAxis2);

  context.append("g")
    .attr("class", "brush")
    .call(brush)
    .call(brush.move, x.range());

  // });

  function brushed() {
    if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
    var s = d3.event.selection || x2.range();
    x.domain(s.map(x2.invert, x2));
    focus.selectAll(".rects")
      .attr("x", function(d) {
        return x(d.num);
      })
      .attr("y", function(d) {
        return y(d.distance);
      })
      .attr("width", function(d) {
        return 10;
      })
      .attr("height", function(d) {
        return height - y(d.distance);
      });

    focus.select(".x-axis").call(xAxis);
    svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
      .scale(width / (s[1] - s[0]))
      .translate(-s[0], 0));
    var e = d3.event.selection;
    var selectedrects = focus.selectAll('.rects').filter(function() {
      var xValue = this.getAttribute('x');
      return e[0] <= xValue && xValue <= e[1];
    });
  }

  function zoomed() {
    if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
    var t = d3.event.transform;
    x.domain(t.rescaleX(x2).domain());
    focus.selectAll(".rects")
      .attr("x", function(d) {
        return x(d.num);
      })
      .attr("y", function(d) {
        return y(d.distance);
      })
      .attr("width", function(d) {
        return 10;
      })
      .attr("height", function(d) {
        return height - y(d.distance);
      });

    focus.select(".x-axis").call(xAxis);
    context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
  }

</script>

PS:这显然是XY Problem:将线性刻度伪装成分类尺度,就像你在这里做的那样,可能不是正确的解决方案。而不是那样,你应该找出如何刷自己的尺度。