简体   繁体   English

响应式 d3 刷牙

[英]Responsive d3 brushing

I have a d3 timeline based on Mike Bostock's 'Focus + Context via brushing' which I'm trying to make responsive.我有一个基于 Mike Bostock 的“Focus + Context via Brushing”的 d3 时间线,我正在尝试使其具有响应性。

I've been able to achieve this with most of it, but I'm struggling with the extent of the brush.我已经能够用它的大部分来实现这一点,但是我正在为刷子的范围而苦苦挣扎。 As a workaround I've tried just making it the new width of the context, but it behaves extremely erratically.作为一种解决方法,我尝试将其设为上下文的新宽度,但它的行为非常不稳定。 Everything else I've tried seems to have no effect – the extent rect doesn't change width.我尝试过的所有其他方法似乎都没有效果——范围 rect 不会改变宽度。

I need a way to find the x and width of the extent rect and apply them to my x-scale (named xContext) on resize.我需要一种方法来找到范围 rect 的 x 和宽度,并在调整大小时将它们应用到我的 x 比例(名为 xContext)。 There's a 'working' version of it here and the full code is below.有一个“工作”的版本,它在这里和完整的代码如下。 The resize function is towards the bottom.调整大小功能朝向底部。

Many thanks in advance.提前谢谢了。

var marginTimeline = {top: 0, right: 18, bottom: 260, left: 0},
    marginContext = {top: 400, right: 18, bottom: 80, left: 0},
    w = parseInt(d3.select("#chart").style("width")) - marginTimeline.left - marginTimeline.right,
    hTimeline = parseInt(d3.select("#chart").style("height")) - marginTimeline.top - marginTimeline.bottom,
    hContext = parseInt(d3.select("#chart").style("height")) - marginContext.top - marginContext.bottom;

//Height of the bars drawn. Context bars are half this.
var barHeight = hTimeline * 0.04;    

var formatDate = d3.time.format("%Y%m%d"),
    parseDate = formatDate.parse;

var xTimeline = d3.time.scale().range([0, w]),
    xContext = d3.time.scale().range([0, w]),
    yTimeline = d3.scale.linear().domain([0, 6]).range([hTimeline, 0]).nice(),
    yContext = d3.scale.linear().range([hContext, 0]);

var thous = d3.format(",");
var displayDate = d3.time.format("%d %b %Y");
var displayMonthYear = d3.time.format("%b %Y");
var displayYear = d3.time.format("%Y");

var xAxisTimeline = d3.svg.axis().scale(xTimeline).orient("bottom"),
    xAxisContext = d3.svg.axis().scale(xContext).orient("bottom"),
    yAxisTimeline = d3.svg.axis().scale(yTimeline).orient("left").outerTickSize(0).ticks(0),
    yAxisContext = d3.svg.axis().scale(yContext).orient("left").outerTickSize(0).ticks(0);

var svg = d3.select("#chart")
    .attr("width", w + marginTimeline.left + marginTimeline.right)
    .attr("height", hTimeline + marginTimeline.top + marginTimeline.bottom)
    .append("g");

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

var opTimeline = svg.append("g")
    .attr("class", "timeline")
    .attr("width", w)
    .attr("height", hTimeline)
    .attr("transform", "translate(10,0)");

var opContext = svg.append("g")
    .attr("class", "context")
    .attr("transform", "translate(10," + marginContext.top + ")");

var brush = d3.svg.brush()
    .x(xContext)
    .extent([0, 1])
    .on("brush", brushed);

queue()
  .defer(d3.json, "http://pasi.com.au/omarpasha/api/get_category_posts/?slug=shows&include=title,url,content,custom_fields")
  .defer(d3.json, "http://pasi.com.au/omarpasha/api/get_category_posts/?slug=timeline&include=title,url,content,custom_fields")
  .await(ready); 

function ready(error, shows, history) {
                  shows.posts.forEach(function(d) {
                  d.id = d.id;
                  d.title = d.title;
                  d.showpage = d.url;
                  d.startDate = parseDate(d.custom_fields.starting_date[0]);
                  d.endDate = parseDate(d.custom_fields.finishing_date[0]);
})
                  history.posts.forEach(function(d) {
                  d.id = d.id;
                  d.title = d.title;
                  d.startDate = parseDate(d.custom_fields.starting_date[0]);
                  d.endDate = parseDate(d.custom_fields.finishing_date[0]);
                  d.line = d.custom_fields.line;
                  d.dateFormat = d.custom_fields.date_format;
});


var minDateShows = d3.min(shows.posts.map(function(d) { return d.startDate; })); 

var minDateHistory = d3.min(history.posts.map(function(d) { return d.startDate; })); 

var minDate =  (minDateShows < minDateHistory ? minDateShows : minDateHistory);

var leftDate = new Date(minDate.getTime());
    leftDate.setDate(leftDate.getDate()-40);

var maxDateShows = d3.max(shows.posts.map(function(d) { return d.endDate; })); 

var maxDateHistory = d3.max(history.posts.map(function(d) { return d.endDate; })); 

var maxDate =  (maxDateShows > maxDateHistory ? maxDateShows : maxDateHistory);

var rightDate = new Date(maxDate.getTime());
    rightDate.setDate(rightDate.getDate()+1400);


  xTimeline.domain([leftDate, rightDate]);
  xContext.domain(xTimeline.domain());
  yContext.domain(yTimeline.domain());

var tip = d3.tip()
  .attr('class', 'd3-tip')
  .offset(function(d) { if (xTimeline(d.endDate)  > 800) { return [-10, 8] } else { return [-10, -8]  } })
  .direction(function(d) { if (xTimeline(d.endDate)  > 800) { return 'nw' } else { return 'ne'  } })
  .html(function(d) {
    if (displayMonthYear(d.startDate) == displayMonthYear(d.endDate)) {
          return d.title + "<br/><p class='yellow'>" + displayMonthYear(d.startDate) + "</p>"; }
        else { 
          return d.title + "<br/><p class='yellow'>"+ displayMonthYear(d.startDate) + " to " + displayMonthYear(d.endDate) + "</p>"; }
  });

var tip2 = d3.tip()
  .attr('class', 'd3-tip')
  .direction(function(d) { if (xTimeline(d.endDate)  > 800) { return 'nw' } else { return 'ne'  } })
  .offset(function(d) {
      if (xTimeline(d.endDate)  > 800) {
        return [-10, 8];
      } else {
        return [-10, -8];
      }
  })
  .html(function(d) {
    var toolTipContent = "";
    if ((xTimeline(d.endDate) - xTimeline(d.startDate) == 0)) {
      toolTipContent = getToolTipContent(d, true);
    } else {
      toolTipContent = getToolTipContent(d, false);
    }
    return toolTipContent;
  });

function getToolTipContent(d, sameDates) {
  var toolTipContent = d.title + "<br/><p class='yellow'>";
  if (d.dateFormat == "Year only") {
    toolTipContent +=  (sameDates)
      ? displayYear(d.startDate) + "</p>" + d.content
      : displayYear(d.startDate) + " to " + displayYear(d.endDate);
  } else if (d.dateFormat == "Month and year") {
    toolTipContent +=  (sameDates)
      ? displayMonthYear(d.startDate) + "</p>" + d.content
      : displayMonthYear(d.startDate) + " to " + displayMonthYear(d.endDate);
  } else {
    toolTipContent +=  (sameDates)
      ? displayDate(d.startDate) + "</p>" + d.content
      : displayDate(d.startDate) + " to " + displayDate(d.endDate);
  }
  toolTipContent += "</p>" + d.content;
  return toolTipContent;
}  

svg.call(tip);
svg.call(tip2);

opTimeline.append("line")
   .attr("class", "show show-line")
   .attr("x1", 0)
   .attr("x2",  w)
   .attr("y1", yTimeline(5))
   .attr("y2", yTimeline(5));

opTimeline.append("line")
   .attr("class", "ost ost-line")
   .attr("x1", 0)
   .attr("x2",  w)
   .attr("y1", yTimeline(3))
   .attr("y2", yTimeline(3));

opTimeline.append("line")
   .attr("class", "blackart blackart-line")
   .attr("x1", 0)
   .attr("x2",  w)
   .attr("y1", yTimeline(1))
   .attr("y2", yTimeline(1));

opContext.append("line")
   .attr("class", "context show context-show-line")
   .attr("x1", 0)
   .attr("x2",  w)
   .attr("y1", yContext(5))
   .attr("y2", yContext(5));

opContext.append("line")
   .attr("class", "context ost context-ost-line")
   .attr("x1", 0)
   .attr("x2",  w)
   .attr("y1", yContext(3))
   .attr("y2", yContext(3));

opContext.append("line")
   .attr("class", "context blackart context-blackart-line")
   .attr("x1", 0)
   .attr("x2",  w)
   .attr("y1", yContext(1))
   .attr("y2", yContext(1));

opTimeline.append("text")
   .attr("class", "show show-text")
   .attr("x", 10)
   .attr("y", yTimeline(5) + 26)
   .text("Shows");

opTimeline.append("text")
   .attr("class", "ost ost-text")
   .attr("x", 10)
   .attr("y", yTimeline(3) + 26)
   .text("Ostrowsky Family");

opTimeline.append("text")
   .attr("class", "blackart blackart-text")
   .attr("x", 10)
   .attr("y", yTimeline(1) + 26)
   .text("Black Art");

svg.append("text")
   .attr("class", "explanation")
   .attr("x", 10)
   .attr("y", 380)
   .text("Move the handles below to adjust the time period");

opTimeline.append("g")
   .selectAll("rect")
   .data(shows.posts)
   .enter()
   .append("svg:a")
   .attr("xlink:href", function(d){return d.showpage;})
   .append("rect")
   .attr("class", "event show-event show")
   .attr("clip-path", "url(#clip)")
   .attr("x", (function(d) { return xTimeline(d.startDate); }))
   .attr("width",  (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 12)) {
    return (xTimeline(d.endDate) - xTimeline(d.startDate));}
    else {
      return 12
    } }))
   .attr("y", yTimeline(5) - (barHeight * 0.5))
   .attr("height", barHeight)
   .attr("rx", 10)
   .attr("ry", 10);

opTimeline.append("g")
   .selectAll("rect")
   .data(history.posts)
   .enter()
   .append("rect")
   .attr("class", (function(d) { if (d.line == "Ostrowsky family") { return "event ost-event ost" } else { return "event blackart-event blackart" } }))
   .attr("clip-path", "url(#clip)")
   .attr("x", (function(d) { return xTimeline(d.startDate); }))
   .attr("width",  (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 12)) {
    return (xTimeline(d.endDate) - xTimeline(d.startDate));}
    else {
      return 12
    } }))
   .attr("y", (function(d) { if (d.line == "Ostrowsky family") { return yTimeline(3) - (barHeight * 0.5) } else { return yTimeline(1) - (barHeight * 0.5) } }))
   .attr("height", barHeight)
   .attr("rx", 10)
   .attr("ry", 10);

opContext.append("g")
   .selectAll("rect")
   .data(shows.posts)
   .enter()
   .append("rect")
   .attr("class", "event show-event show")
   .attr("x", (function(d) { return xContext(d.startDate); }))
   .attr("width",  (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 6)) {
    return (xTimeline(d.endDate) - xTimeline(d.startDate));}
    else {
      return 6
    } }))
   .attr("y", yContext(5) - (barHeight * 0.25))
   .attr("height", barHeight/2)
   .attr("rx", 5)
   .attr("ry", 5);

opContext.append("g")
   .selectAll("rect")
   .data(history.posts)
   .enter()
   .append("rect")
   .attr("class", (function(d) { if (d.line == "Ostrowsky family") { return "event ost-event ost" } else { return "event blackart-event blackart" } }))
   .attr("x", (function(d) { return xContext(d.startDate); }))
   .attr("width",  (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 6)) {
    return (xTimeline(d.endDate) - xTimeline(d.startDate));}
    else {
      return 6
    } }))
   .attr("y", (function(d) { if (d.line == "Ostrowsky family") { return yContext(3) - (barHeight * 0.25) } else { return yContext(1) - (barHeight * 0.25) } }))
   .attr("height", barHeight/2)
   .attr("rx", 5)
   .attr("ry", 5);

opTimeline.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + hTimeline + ")")
    .call(xAxisTimeline);


opContext.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + hContext + ")")
      .call(xAxisContext);

var brushg = opContext.append("g")
      .attr("class", "x brush")
      .call(brush)
      .selectAll("rect")
      .attr("y", -6)
      .attr("height", hContext + 7);

opContext.selectAll(".e")
      .append("image")
      .attr("xlink:href",'../wp-content/themes/omarpasha/img/right-handle.png')
      .attr("width", 10)
      .attr("height", 70)
      .attr("y", -6);

opContext.selectAll(".w")
      .append("image")
      .attr("xlink:href",'../wp-content/themes/omarpasha/img/left-handle.png')
      .attr("width", 10)
      .attr("height", 70)
      .attr("x", -10)
      .attr("y", -6);

opTimeline.selectAll(".show-event")
   .on('mouseover', tip.show)
   .on('mouseout', tip.hide);

opTimeline.selectAll(".ost-event, .blackart-event")
   .on('mouseover', tip2.show)
   .on('mouseout', tip2.hide);


function resize() {
    marginContext = {top: 400, right: 18, bottom: 80, left: 0},
    w = parseInt(d3.select("#chart").style("width")) - marginTimeline.left - marginTimeline.right,
    hTimeline = parseInt(d3.select("#chart").style("height")) - marginTimeline.top - marginTimeline.bottom,
    hContext = parseInt(d3.select("#chart").style("height")) - marginContext.top - marginContext.bottom;

    var barHeight = hTimeline * 0.04;    

        xTimeline.range([0, w]),
        xContext.range([0, w]),
        yTimeline.range([hTimeline, 0]).nice(),
        yContext.range([hContext, 0]);

    svg
      .attr("width", w + marginTimeline.left + marginTimeline.right)
      .attr("height", hTimeline + marginTimeline.top + marginTimeline.bottom);

    svg.select("#clip rect")
      .attr("width", w)
      .attr("height", hTimeline);

    d3.select(".background")
      .attr("width", w);

    opTimeline
      .attr("width", w)
      .attr("height", hTimeline)
      .attr("transform", "translate(10,0)");

    opContext
      .attr("transform", "translate(10," + marginContext.top + ")");

    opTimeline.select('.x.axis')
      .attr("transform", "translate(0," + hTimeline + ")")
      .call(xAxisTimeline);

    opContext.select('.x.axis')
      .attr("transform", "translate(0," + hContext + ")")
      .call(xAxisContext);

    opTimeline.select(".show-line")
       .attr("x2",  w);

    opTimeline.select(".ost-line")
       .attr("x2",  w);

    opTimeline.select(".blackart-line")
       .attr("x2",  w);

    opContext.select(".context-show-line")
       .attr("x2",  w);

    opContext.select(".context-ost-line")
       .attr("x2",  w);

    opContext.select(".context-blackart-line")
       .attr("x2",  w);

    opTimeline.selectAll(".event")
       .attr("x", (function(d) { return xTimeline(d.startDate); }))
       .attr("width",  (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 12)) {
        return (xTimeline(d.endDate) - xTimeline(d.startDate));}
        else {
          return 12
        } }));

    opContext.selectAll(".event")
       .attr("x", (function(d) { return xContext(d.startDate); }))
       .attr("width",  (function(d) { if ((xContext(d.endDate) - xContext(d.startDate) > 6)) {
        return (xContext(d.endDate) - xContext(d.startDate));}
        else {
          return 6
        } }));

    brush
      .x(xContext)
      .extent([0, 1])
      .on("brush", brushed);
}

d3.select(window).on('resize', resize); 
  resize();

};

function brushed() {
  xTimeline.domain(brush.empty() ? xContext.domain() : brush.extent());
  opTimeline.selectAll("rect").attr("x", (function(d) { return xTimeline(d.startDate); }))
      .attr("width",  (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 12)) { return (xTimeline(d.endDate) - xTimeline(d.startDate));} else { return 12 } }));
  opTimeline.select(".x.axis").call(xAxisTimeline);
}

I had someone outside Stack Overflow sort this out for me.我让 Stack Overflow 之外的人帮我解决了这个问题。 The solution was straightforward – capture the state of the brush's extent at the beginning of the resize function.解决方案很简单——在调整大小函数开始时捕获画笔范围的状态。 Nothing else was changed.没有其他任何改变。 So the resize function now looks like this (still rather verbose, but working):所以 resize 函数现在看起来像这样(仍然相当冗长,但有效):

function resize() {
    var extent = brush.extent();

    w = parseInt(d3.select("#chart").style("width")) - marginTimeline.left - marginTimeline.right,
    hTimeline = parseInt(d3.select("#chart").style("height")) - marginTimeline.top - marginTimeline.bottom;

    var barHeight = hTimeline * 0.04;    

        xTimeline.range([0, w]),
        xContext.range([0, w]),
        yTimeline.range([hTimeline, 0]).nice(),
        yContext.range([hContext, 0]);


    svg
      .attr("width", w + marginTimeline.left + marginTimeline.right);

    svg.select("#clip rect")
      .attr("width", w);

    opTimeline
      .attr("width", w)
      .attr("transform", "translate(10,0)");

    opContext
      .attr("transform", "translate(10," + marginContext.top + ")");

    opTimeline.select('.x.axis')
      .attr("transform", "translate(0," + hTimeline + ")")
      .call(xAxisTimeline);

    opContext.select('.x.axis')
      .attr("transform", "translate(0," + hContext + ")")
      .call(xAxisContext);

    opTimeline.select(".show-line")
       .attr("x2",  w);

    opTimeline.select(".ost-line")
       .attr("x2",  w);

    opTimeline.select(".blackart-line")
       .attr("x2",  w);

    opContext.select(".context-show-line")
       .attr("x2",  w);

    opContext.select(".context-ost-line")
       .attr("x2",  w);

    opContext.select(".context-blackart-line")
       .attr("x2",  w);

    opTimeline.selectAll(".event")
       .attr("x", (function(d) { return xTimeline(d.startDate); }))
       .attr("width",  (function(d) { if ((xTimeline(d.endDate) - xTimeline(d.startDate) > 12)) {
        return (xTimeline(d.endDate) - xTimeline(d.startDate));}
        else {
          return 12
        } }));

    opContext.selectAll(".event")
       .attr("x", (function(d) { return xContext(d.startDate); }))
       .attr("width",  (function(d) { if ((xContext(d.endDate) - xContext(d.startDate) > 6)) {
        return (xContext(d.endDate) - xContext(d.startDate));}
        else {
          return 6
        } }));

  brush.extent(extent);
  // Now just call the methods to update the brush.
  opContext.select("g.x.brush").call(brush);
  brushed();


}

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

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