简体   繁体   English

D3js v4笔刷,缩放和最新笔刷

[英]D3js v4 brush & zoom & recenter brush on click

I'm working with D3js v4. 我正在使用D3js v4。 What I'm trying to achieve is to combine brush & zoom behaviour according to this example with click-to-recenter brush where after click the brush gets recentered and the brush boundarys are rounded with a smooth transition. 我想要实现的是将根据示例的笔刷和缩放行为与单击以单击中心的笔刷相结合,其中单击后笔刷重新居中,并且笔刷边界以平滑过渡圆滑。 Here is my fiddle so far. 到目前为止,这是我的小提琴

My problem is that the function brushended never gets executed. 我的问题是,brushended函数永远不会执行。 It seems like the zoom prevents the brush from receiving mouseup events. 似乎缩放阻止笔刷接收mouseup事件。 Only when zoom is completely disabled by commenting out all zoom functions I get brushended working. 仅当通过注释掉所有缩放功能完全禁用缩放时,我才开始工作。 I tried event.stopPropagation and event.stopImmediatePropagation on mousedown like in the following code snippet to prevent the zoom from receiving mousedown and mouseup events but it did not work. 我在mousedown上尝试了event.stopPropagation和event.stopImmediatePropagation,如下面的代码片段所示,以防止缩放接收到mousedown和mouseup事件,但它不起作用。

context.append("g")
  .attr("class", "brush")
  .call(brush)
  .call(brush.move, [x2(new Date(2013, 0, 1)), x2(new Date(2013, 6, 1))])
  .selectAll(".overlay")
  .each(function(d) {
    d.type = "selection";
  })
  .on("mousedown touchstart", function() { d3.event.stopPropagation(); })
  .on("mousedown touchstart", brushcentered)

Do I placed stopPropagation in the wrong place or am I completely missing something? 我是否将stopPropagation放在错误的位置,或者我完全错过了什么? Any help on this would be appreciated. 任何帮助,将不胜感激。

Whoa! 哇! You got a lot of event handling going on here. 您在这里进行了很多事件处理。 Let's break this down, first, it's this call: 让我们分解一下,首先是这个调用:

 svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
   .scale(width / (s[1] - s[0]))
   .translate(-s[0], 0));

in your brushed event handler that's eating your brushend . 在您的brushed事件处理程序中正在吃掉brushend My first thought was to use the simple setTimeout hack to allow the brushend to process. 我的第一个想法是使用简单的setTimeout hack来允许brushend处理。 While this worked, it just lead a new problem, the brush.move in the brushedend was then being raised as a zoom event and not being process in brushed . 在执行此操作的同时,它只是引发了一个新问题,然后在brush.move中的brushedend作为缩放事件被引发,而不是在brushed进行处理。 So, instead, I simply replace the move with a zoom and let the zoom event handle the brush positioning like: 因此,相反,我只是用zoom替换了move,然后让zoom事件处理画笔位置,如下所示:

function brushended() {
  if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
  if (!d3.event.selection) return; // Ignore empty selections.

  var d0 = d3.event.selection.map(x2.invert), //=Datum, linker+rechter Brush-Rand
    d1 = d0.map(d3.timeMonth.round);

  // If empty when rounded, use floor & ceil instead.
  if (d1[0] >= d1[1]) {
    d1[0] = d3.timeMonth.floor(d0[0]);
    d1[1] = d3.timeMonth.offset(d1[0]);
  };

  var s = d1.map(x2);
  setTimeout(function(){
    svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
      .scale(width / (s[1] - s[0]))
      .translate(-s[0], 0));
  });
}

I think this is producing the behavior you are looking for. 我认为这正在产生您想要的行为。

Running code below and plunker here : 在下面运行代码, 在这里运行:

 <!DOCTYPE html> <html> <head> <script src="https://d3js.org/d3.v4.js"></script> <style> #Type1 { stroke: DarkTurquoise; } #Type2 { stroke: steelblue; } .data-line { fill: none; stroke-width: 2.5; opacity: 0.8; stroke-linecap: round; clip-path: url(#clip); transition: 0.5s; } .context-line { fill: none; stroke-width: 1.5; stroke: grey; stroke-linecap: round; clip-path: url(#clip); } div.svg-container { display: inline-block; position: relative; width: 100%; height: 100%; vertical-align: top; } .zoom { cursor: move; fill: none; pointer-events: all; } </style> </head> <body> <div> </div> <script> var svg = d3.select("div") .append("div").classed("svg-container", true) .append("svg") .attr("width", 700) .attr("height", 400) var margin = { top: 20, right: 20, bottom: 130, left: 60 }, margin2 = { top: 300, right: 20, bottom: 30, left: 60 }, width = +svg.attr("width") - margin.left - margin.right, height = +svg.attr("height") - margin.top - margin.bottom, height2 = +svg.attr("height") - margin2.top - margin2.bottom; // Achsen Datumsbehandlung var parseDate = d3.timeParse("%Y-%m-%d %H:%M:%S"); var x = d3.scaleTime().range([0, width]), x2 = d3.scaleTime().range([0, width]), y = d3.scaleLinear().range([height, 0]); y2 = d3.scaleLinear().range([height2, 0]); var xAxis = d3.axisBottom(x) xAxis2 = d3.axisBottom(x2), yAxis = d3.axisLeft(y); //Linien var line = d3.line() .x(function(d) { return x(d.Datum); }) // .y0(height) //nur area-chType .y(function(d) { return y(d.Summe); }); // .y1(function(d) { return y(d.Summe); }); //nur area-chType //Linien var lineType1 = d3.line() .curve(d3.curveStepAfter) .x(function(d) { return x(d.Datum); }) .y(function(d) { return y(d.Summe); }); var line2 = d3.line() .x(function(d) { return x2(d.Datum); }) .y(function(d) { return y2(d.Summe); }); 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 + ")"); //Zoom & Brush var brush = d3.brushX() .extent([ [0, 0], [width, height2] ]) .handleSize(10) .on("start brush", brushed) .on("end", brushended); var zoom = d3.zoom() .scaleExtent([1, 100]) .translateExtent([ [0, 0], [width, height] ]) .extent([ [0, 0], [width, height] ]) .on("zoom", zoomed); // ===Daten=== var data = [{ "Datum": "2013-02-04 00:00:00", "Summe": "1000.00", "Type": "Type1", "Notizen": null }, { "Datum": "2013-02-04 00:00:00", "Summe": "200.00", "Type": "Type2", "Notizen": null }, { "Datum": "2013-02-21 00:00:00", "Summe": "4000.00", "Type": "Type1", "Notizen": null }, { "Datum": "2013-02-23 00:00:00", "Summe": "2000.00", "Type": "Type1", "Notizen": null }, { "Datum": "2013-02-23 00:00:00", "Summe": "601.00", "Type": "Type2", "Notizen": null }, { "Datum": "2013-03-04 00:00:00", "Summe": "775.00", "Type": "Type1", "Notizen": null }, { "Datum": "2013-03-04 00:00:00", "Summe": "1395.10", "Type": "Type2", "Notizen": null }, { "Datum": "2013-04-03 00:00:00", "Summe": "400.00", "Type": "Type1", "Notizen": null }, { "Datum": "2013-04-03 00:00:00", "Summe": "1040.00", "Type": "Type2", "Notizen": null }, { "Datum": "2013-05-24 00:00:00", "Summe": "400.00", "Type": "Type1", "Notizen": null }, { "Datum": "2013-05-24 00:00:00", "Summe": "3288.88", "Type": "Type2", "Notizen": null }, { "Datum": "2013-05-28 00:00:00", "Summe": "400.00", "Type": "Type1", "Notizen": null }, { "Datum": "2013-05-28 00:00:00", "Summe": "4407.10", "Type": "Type2", "Notizen": null }, { "Datum": "2013-06-01 00:00:00", "Summe": "400.00", "Type": "Type1", "Notizen": null }, { "Datum": "2013-06-01 00:00:00", "Summe": "3525.86", "Type": "Type2", "Notizen": null }, { "Datum": "2013-06-04 00:00:00", "Summe": "400.00", "Type": "Type1", "Notizen": null }, { "Datum": "2013-06-04 00:00:00", "Summe": "2990.17", "Type": "Type2", "Notizen": null }, { "Datum": "2013-06-10 00:00:00", "Summe": "390.00", "Type": "Type1", "Notizen": null }, { "Datum": "2013-06-10 00:00:00", "Summe": "366.00", "Type": "Type2", "Notizen": null }, { "Datum": "2013-06-14 00:00:00", "Summe": "390.00", "Type": "Type1", "Notizen": null }, { "Datum": "2013-06-14 00:00:00", "Summe": "925.18", "Type": "Type2", "Notizen": null }, { "Datum": "2013-06-16 00:00:00", "Summe": "708.44", "Type": "Type1", "Notizen": null }, { "Datum": "2013-06-16 00:00:00", "Summe": "609.10", "Type": "Type2", "Notizen": null }, { "Datum": "2013-06-20 00:00:00", "Summe": "708.44", "Type": "Type1", "Notizen": null }, { "Datum": "2013-06-20 00:00:00", "Summe": "1760.80", "Type": "Type2", "Notizen": null }] data.forEach(function(d) { d.Datum = parseDate(d.Datum); d.Summe = +d.Summe; }); x.domain([d3.min(data, function(d) { return d.Datum; }), d3.max(data, function(d) { return d.Datum; })]).nice(d3.timeYear); y.domain([d3.min(data, function(d) { return d.Summe; }), 1.1 * d3.max(data, function(d) { return d.Summe; })]).nice(); x2.domain(x.domain()); y2.domain(y.domain()); var dataGroup = d3.nest() .key(function(d) { return d.Type; }) .entries(data); focus.append("rect") .attr("class", "zoom") .attr("width", width) .attr("height", height) .call(zoom); focus.append("g") .attr("class", "axis axis--x") .attr("transform", "translate(0," + height + ")") .call(xAxis); focus.append("g") .attr("class", "axis axis--y") .call(yAxis); focus.selectAll("path#Type2") .data(dataGroup) .enter() .append("path") .filter(function(d) { return d.key == "Type2" }) .attr("class", "data-line") .attr("d", function(d) { return line(d.values); }) .attr("id", function(d) { return d.key }); focus.selectAll("path#Type1") .data(dataGroup) .enter() .append("path") .filter(function(d) { return d.key == "Type1" }) .attr("class", "data-line") .attr("d", function(d) { return lineType1(d.values); }) .attr("id", function(d) { return d.key }); context.selectAll(".context-line") .data(dataGroup) .enter() .append("path") .attr("class", "context-line") .attr("d", function(d) { return line2(d.values); }); context.append("g") .attr("class", "axis axis--x") .attr("transform", "translate(0," + height2 + ")") .call(xAxis2); context.append("g") .attr("class", "brush") .call(brush) .call(brush.move, [x2(new Date(2013, 0, 1)), x2(new Date(2013, 6, 1))]) .selectAll(".overlay") .each(function(d) { d.type = "selection"; }) .on("mousedown touchstart", brushcentered); // ===Funktionen=== 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("path#Type1").attr("d", function(d) { return lineType1(d.values); }); focus.selectAll("path#Type2").attr("d", function(d) { return line(d.values); }); focus.select(".axis--x").call(xAxis); setTimeout(function(){ svg.select(".zoom").call(zoom.transform, d3.zoomIdentity .scale(width / (s[1] - s[0])) .translate(-s[0], 0)); }); } function brushcentered() { var dx = 30, // Use a fixed width when recentering, = ca 2 Monate cx = d3.mouse(this)[0], x0 = cx - dx / 2, x1 = cx + dx / 2; d3.select(this.parentNode).call(brush.move, x1 > width ? [width - dx, width] : x0 < 0 ? [0, dx] : [x0, x1]); } function brushended() { if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom if (!d3.event.selection) return; // Ignore empty selections. var d0 = d3.event.selection.map(x2.invert), //=Datum, linker+rechter Brush-Rand d1 = d0.map(d3.timeMonth.round); // If empty when rounded, use floor & ceil instead. if (d1[0] >= d1[1]) { d1[0] = d3.timeMonth.floor(d0[0]); d1[1] = d3.timeMonth.offset(d1[0]); }; var s = d1.map(x2); setTimeout(function(){ svg.select(".zoom").call(zoom.transform, d3.zoomIdentity .scale(width / (s[1] - s[0])) .translate(-s[0], 0)); }); } function zoomed() { if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; var t = d3.event.transform; x.domain(t.rescaleX(x2).domain()); focus.selectAll("path#Type1").attr("d", function(d) { return lineType1(d.values) }); focus.selectAll("path#Type2").attr("d", function(d) { return line(d.values) }); focus.select(".axis--x").call(xAxis); context.select(".brush").call(brush.move, x.range().map(t.invertX, t)); } </script> </body> </html> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM