使用画笔时,d3.js图表的内容溢出页边空白

Content of d3.js chart overflows margins when using brush

本文关键字:页边空白 溢出 js 画笔 d3      更新时间:2023-09-26

我在使用d3.js条形图时遇到问题。完整的代码和工作演示如下:https://jsfiddle.net/monotasker/yurqkm3n/.问题是,当我在下(上下文)图表上使用画笔时,上(焦点)图表会溢出其边距。

这是画笔激活的功能:

function brushed() {
    time.domain(brush.empty() ? navTime.domain() : brush.extent())
        .range([0, (width)]);
    x.domain(max_extent_in_days(time))
        .rangeBands([margin.left, (width)], 0.1, 0);
    focus.selectAll('.bar.stack')
        .attr('transform', function(d) {return "translate(" + time(d.date) + ",0)"; })
        .attr('width', x.rangeBand());
    focus.selectAll('.rect')
        .attr('width', x.rangeBand());
    focus.selectAll('.line').attr('d', line);
    focus.select(".x.axis").call(x_axis);
};

图表的一个奇怪之处在于,有两个刻度共同控制x轴:"时间"answers"x"。"时间"刻度用于在x轴上定位内容,但"x"刻度(将"时间"转换为有序刻度)用于计算条形宽度。我不知道这种方法是否是问题的根源。

非常感谢您的帮助。

在这个例子中,Bostock先生将一个剪辑路径应用到区域路径,您需要将一个应用到条形图。最简单的方法是将它们放在自己的g中,然后应用剪辑路径:

svg.append("defs").append("clipPath")
    .attr("id", "clip")
    .append("rect")
    .attr("width", width)
    .attr("height", height);
...
// Plot the stacked bars
var focus_bar = focus
    .append("g") //<-- g just for the bars to be clipped
    .attr("class","barClipper")
    .style('clip-path', 'url(#clip)') //<-- apply clipping
    .selectAll('.g')
    .data(data.answer_counts)
    .enter().append('g')
    .attr('class', 'g bar stack')
    .attr('transform', function(d) {
      return "translate(" + x(d.date) + ",0)"
    });

完整代码:

<!DOCTYPE html>
<html>
<head>
  <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
  <style>
    #milestones_attempts_combo .context {
      /*fill: #efefef;    */
    }
    
    #milestones_attempts_combo .focus .right,
    #milestones_attempts_combo .context .right {
      fill: #0064cd;
    }
    
    #milestones_attempts_combo .focus .wrong,
    #milestones_attempts_combo .context .wrong {
      fill: #cd001d;
    }
    
    #milestones_attempts_combo .y-axis-label,
    #milestones_attempts_combo .y2-axis-label {
      fill: #aaa;
      font-size: 120%;
    }
    
    #milestones_attempts_combo .focus-line,
    #milestones_attempts_combo .context-line {
      stroke: orange;
      stroke-width: 2;
      fill-opacity: 0;
    }
    
    .brush .extent {
      stroke: #efefef;
      fill: #666;
      fill-opacity: .125;
      shape-rendering: crispEdges;
    }
    
    .stats-container {
      width: 700px;
      margin: 0 auto;
    }
    .chart .axis line,
    .chart .axis path,
    .chart .tick line {
      fill: none;
      stroke: #aaa;
      shape-rendering: crispEdges;
    }
  </style>
</head>
<body>
  <div id="milestones_attempts_combo"></div>
  <script>
    (function() {
      var myjsondata = {
        "answerswer_counts": [{
          "date": "2012-09-13",
          "ys": [{
            "y1": 9,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 12,
            "y0": 9,
            "class": "wrong"
          }],
          "total": 12
        }, {
          "date": "2012-09-16",
          "ys": [{
            "y1": 16,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 17,
            "y0": 16,
            "class": "wrong"
          }],
          "total": 17
        }, {
          "date": "2012-09-17",
          "ys": [{
            "y1": 12,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 14,
            "y0": 12,
            "class": "wrong"
          }],
          "total": 14
        }, {
          "date": "2012-09-19",
          "ys": [{
            "y1": 2,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 2,
            "y0": 2,
            "class": "wrong"
          }],
          "total": 2
        }, {
          "date": "2012-09-20",
          "ys": [{
            "y1": 12,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 16,
            "y0": 12,
            "class": "wrong"
          }],
          "total": 16
        }, {
          "date": "2012-09-21",
          "ys": [{
            "y1": 20,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 22,
            "y0": 20,
            "class": "wrong"
          }],
          "total": 22
        }, {
          "date": "2012-09-22",
          "ys": [{
            "y1": 1,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 1,
            "y0": 1,
            "class": "wrong"
          }],
          "total": 1
        }, {
          "date": "2012-09-23",
          "ys": [{
            "y1": 10,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 12,
            "y0": 10,
            "class": "wrong"
          }],
          "total": 12
        }, {
          "date": "2012-09-24",
          "ys": [{
            "y1": 9,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 9,
            "y0": 9,
            "class": "wrong"
          }],
          "total": 9
        }, {
          "date": "2012-09-25",
          "ys": [{
            "y1": 2,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 4,
            "y0": 2,
            "class": "wrong"
          }],
          "total": 4
        }, {
          "date": "2012-09-29",
          "ys": [{
            "y1": 26,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 37,
            "y0": 26,
            "class": "wrong"
          }],
          "total": 37
        }, {
          "date": "2012-10-01",
          "ys": [{
            "y1": 44,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 44,
            "y0": 44,
            "class": "wrong"
          }],
          "total": 44
        }, {
          "date": "2012-10-02",
          "ys": [{
            "y1": 2,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 2,
            "y0": 2,
            "class": "wrong"
          }],
          "total": 2
        }, {
          "date": "2012-10-03",
          "ys": [{
            "y1": 13,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 13,
            "y0": 13,
            "class": "wrong"
          }],
          "total": 13
        }, {
          "date": "2012-10-05",
          "ys": [{
            "y1": 47,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 47,
            "y0": 47,
            "class": "wrong"
          }],
          "total": 47
        }, {
          "date": "2012-10-08",
          "ys": [{
            "y1": 17,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 17,
            "y0": 17,
            "class": "wrong"
          }],
          "total": 17
        }, {
          "date": "2012-10-09",
          "ys": [{
            "y1": 19,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 20,
            "y0": 19,
            "class": "wrong"
          }],
          "total": 20
        }, {
          "date": "2012-10-10",
          "ys": [{
            "y1": 31,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 31,
            "y0": 31,
            "class": "wrong"
          }],
          "total": 31
        }, {
          "date": "2012-10-11",
          "ys": [{
            "y1": 6,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 6,
            "y0": 6,
            "class": "wrong"
          }],
          "total": 6
        }, {
          "date": "2012-10-14",
          "ys": [{
            "y1": 6,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 6,
            "y0": 6,
            "class": "wrong"
          }],
          "total": 6
        }, {
          "date": "2012-10-19",
          "ys": [{
            "y1": 30,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 32,
            "y0": 30,
            "class": "wrong"
          }],
          "total": 32
        }, {
          "date": "2012-10-20",
          "ys": [{
            "y1": 20,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 22,
            "y0": 20,
            "class": "wrong"
          }],
          "total": 22
        }, {
          "date": "2012-10-23",
          "ys": [{
            "y1": 20,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 20,
            "y0": 20,
            "class": "wrong"
          }],
          "total": 20
        }, {
          "date": "2012-10-24",
          "ys": [{
            "y1": 20,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 21,
            "y0": 20,
            "class": "wrong"
          }],
          "total": 21
        }, {
          "date": "2012-10-29",
          "ys": [{
            "y1": 22,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 22,
            "y0": 22,
            "class": "wrong"
          }],
          "total": 22
        }, {
          "date": "2012-11-01",
          "ys": [{
            "y1": 1,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 1,
            "y0": 1,
            "class": "wrong"
          }],
          "total": 1
        }, {
          "date": "2012-11-02",
          "ys": [{
            "y1": 26,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 29,
            "y0": 26,
            "class": "wrong"
          }],
          "total": 29
        }, {
          "date": "2012-11-05",
          "ys": [{
            "y1": 23,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 27,
            "y0": 23,
            "class": "wrong"
          }],
          "total": 27
        }, {
          "date": "2012-11-06",
          "ys": [{
            "y1": 10,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 11,
            "y0": 10,
            "class": "wrong"
          }],
          "total": 11
        }, {
          "date": "2012-11-07",
          "ys": [{
            "y1": 15,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 18,
            "y0": 15,
            "class": "wrong"
          }],
          "total": 18
        }, {
          "date": "2012-11-09",
          "ys": [{
            "y1": 26,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 31,
            "y0": 26,
            "class": "wrong"
          }],
          "total": 31
        }, {
          "date": "2012-11-10",
          "ys": [{
            "y1": 2,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 2,
            "y0": 2,
            "class": "wrong"
          }],
          "total": 2
        }, {
          "date": "2012-11-14",
          "ys": [{
            "y1": 15,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 17,
            "y0": 15,
            "class": "wrong"
          }],
          "total": 17
        }, {
          "date": "2012-11-16",
          "ys": [{
            "y1": 20,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 23,
            "y0": 20,
            "class": "wrong"
          }],
          "total": 23
        }, {
          "date": "2012-11-18",
          "ys": [{
            "y1": 2,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 2,
            "y0": 2,
            "class": "wrong"
          }],
          "total": 2
        }, {
          "date": "2012-11-19",
          "ys": [{
            "y1": 23,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 26,
            "y0": 23,
            "class": "wrong"
          }],
          "total": 26
        }, {
          "date": "2012-11-21",
          "ys": [{
            "y1": 2,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 3,
            "y0": 2,
            "class": "wrong"
          }],
          "total": 3
        }, {
          "date": "2012-11-23",
          "ys": [{
            "y1": 9,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 10,
            "y0": 9,
            "class": "wrong"
          }],
          "total": 10
        }, {
          "date": "2012-11-26",
          "ys": [{
            "y1": 2,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 2,
            "y0": 2,
            "class": "wrong"
          }],
          "total": 2
        }, {
          "date": "2012-11-27",
          "ys": [{
            "y1": 25,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 26,
            "y0": 25,
            "class": "wrong"
          }],
          "total": 26
        }, {
          "date": "2012-11-28",
          "ys": [{
            "y1": 73,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 75,
            "y0": 73,
            "class": "wrong"
          }],
          "total": 75
        }, {
          "date": "2012-12-02",
          "ys": [{
            "y1": 19,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 20,
            "y0": 19,
            "class": "wrong"
          }],
          "total": 20
        }, {
          "date": "2012-12-05",
          "ys": [{
            "y1": 1,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 1,
            "y0": 1,
            "class": "wrong"
          }],
          "total": 1
        }, {
          "date": "2013-01-11",
          "ys": [{
            "y1": 2,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 3,
            "y0": 2,
            "class": "wrong"
          }],
          "total": 3
        }, {
          "date": "2013-01-16",
          "ys": [{
            "y1": 8,
            "y0": 0,
            "class": "right"
          }, {
            "y1": 8,
            "y0": 8,
            "class": "wrong"
          }],
          "total": 8
        }],
        "badge_set_reached": [{
          "date": "2014-05-24",
          "set": 3
        }, {
          "date": "2014-09-29",
          "set": 5
        }, {
          "date": "2014-11-10",
          "set": 6
        }, {
          "date": "2014-08-29",
          "set": 7
        }, {
          "date": "2015-08-12",
          "set": 9
        }, {
          "date": "2016-01-09",
          "set": 9
        }]
      }
      var data = myjsondata;
      // preprocess data to get usable date objects
      var parse_date = d3.time.format('%Y-%m-%d').parse
      for (var i in data) {
        var myObj = data[i]
        for (var j in myObj) {
          myObj[j].date = parse_date(myObj[j].date);
        }
      }
      console.log(data);
      // set variables
      var margin = {
          left: 60,
          right: 60,
          top: 10,
          bottom: 140
        },
        navMargin = {
          top: 300,
          right: 60,
          bottom: 40,
          left: 60
        },
        height = 400 - margin.top - margin.bottom,
        width = 800 - margin.left - margin.right,
        navWidth = width, // for context band
        navHeight = 400 - navMargin.top - navMargin.bottom,
        max_total_counts = d3.max(data.answer_counts, function(d) {
          return d.total;
        }),
        max_extent_dates = d3.extent(data.answer_counts, function(d) {
          return d.date;
        });
      max_extent_in_days = function(timescale) {
        return d3.time.days(timescale.domain()[0], d3.time.day.offset(timescale.domain()[1], 1))
      };
      // scales
      var y = d3.scale.linear().domain([0, max_total_counts]).rangeRound([height, 0]),
        navY = d3.scale.linear().domain([0, max_total_counts]).rangeRound([navHeight, 0]),
        time = d3.time.scale().domain(max_extent_dates).range([0, width]),
        navTime = d3.time.scale().domain(max_extent_dates).range([0, width]),
        x = d3.scale.ordinal().domain(max_extent_in_days(time)).rangeBands([0, width], 0.1, 0), // used to calculate bar widths
        navX = d3.scale.ordinal().domain(max_extent_in_days(navTime)).rangeBands([0, width], 0.1, 0),
        y2 = d3.scale.linear().domain([0, d3.max(data.badge_set_reached, function(d) {
          return d.set
        })]).rangeRound([height, 0]);
      navY2 = d3.scale.linear().domain([0, d3.max(data.badge_set_reached, function(d) {
        return d.set
      })]).rangeRound([navHeight, 0]);
      // axes
      var x_axis = d3.svg.axis().scale(time).orient('bottom') //    .tickFormat(d3.time.format('%Y-%m-%d'))
        .outerTickSize(0), // at start and end of axis line
        nav_x_axis = d3.svg.axis().scale(navTime).orient('bottom') //    .tickFormat(d3.time.format('%Y-%m-%d'))
        .outerTickSize(0), // at start and end of axis line
        y_axis = d3.svg.axis().scale(y).orient('left').tickFormat(d3.format('d'));
      y2_axis = d3.svg.axis().scale(y2).orient('right').tickFormat(d3.format('d'));
      // add brush
      var brush = d3.svg.brush()
        .x(navTime)
        .on("brush", brushed);
      // svg context
      var svg = d3.select("#milestones_attempts_combo")
        .append('svg')
        .attr('class', 'chart')
        .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(' + navMargin.left + ',' + navMargin.top + ')');
      // Plot the stacked bars
      var focus_bar = focus
        .append("g")
        .attr("class","barClipper")
        .style('clip-path', 'url(#clip)')
        .selectAll('.g')
        .data(data.answer_counts)
        .enter().append('g')
        .attr('class', 'g bar stack')
        .attr('transform', function(d) {
          return "translate(" + x(d.date) + ",0)"
        });
      var focus_rects = focus_bar.selectAll('rect')
        .data(function(d) {
          return d.ys;
        })
        .enter().append('rect')
        .attr('width', x.rangeBand())
        .attr('height', function(d) {
          return y(d.y0) - y(d.y1);
        })
        .attr('y', function(d) {
          return y(d.y1);
        })
        .attr('class', function(d) {
          return 'rect ' + d['class'];
        });
      var context_bar = context.selectAll('.g')
        .data(data.answer_counts)
        .enter().append('g')
        .attr('class', 'g')
        .attr('transform', function(d) {
          return "translate(" + navTime(d.date) + ",0)"
        });
      var context_rects = context_bar.selectAll('rect')
        .data(function(d) {
          return d.ys;
        })
        .enter().append('rect')
        .attr('width', navX.rangeBand())
        .attr('height', function(d) {
          return navY(d.y0) - navY(d.y1);
        })
        .attr('y', function(d) {
          return navY(d.y1);
        })
        .attr('class', function(d) {
          return 'rect ' + d['class'];
        });
      // Plot lines
      var line = d3.svg.line()
        .x(function(d) {
          return time(d.date)
        })
        .y(function(d) {
          return y2(d.set)
        })
        .interpolate('step-after');
      var focus_line = focus.append('path')
        .datum(data.badge_set_reached)
        .attr('class', 'line focus-line')
        .attr('d', line);
      var line2 = d3.svg.line()
        .x(function(d) {
          return time(d.date)
        })
        .y(function(d) {
          return navY2(d.set)
        })
        .interpolate('step-after');
      var context_line = context.append('path')
        .datum(data.badge_set_reached)
        .attr('class', 'line context-line')
        .attr('d', line2);
      // Plot axes
      focus.append('g')
        .attr('class', 'x axis')
        .attr('transform', 'translate(0, ' + height + ')')
        .call(x_axis)
        .selectAll('text')
        .style('text-anchor', 'end')
        // .attr('transform', 'rotate(-45)')
        //         .attr('dx', '-.5em')
        //         .attr('dy', '.5em');
      focus.append('g')
        .attr('class', 'y axis')
        .attr('transform', 'translate(0, 0)')
        .call(y_axis);
      focus.append('g')
        .attr('class', 'y2 axis')
        .attr('transform', 'translate(' + width + ', 0)')
        .call(y2_axis);
      context.append('g')
        .attr('class', 'navX axis')
        .attr('transform', 'translate(0, ' + navHeight + ')')
        .call(nav_x_axis);
      // Label axes
      svg.append('text')
        .attr('class', 'label y-axis-label')
        .attr('transform', 'rotate(-90)')
        .attr('x', 0 - ((height + margin.top) / 2))
        .attr('y', 0)
        .attr('dy', 20)
        .style('text-anchor', 'middle')
        .text('Paths Attempted');
      svg.append('text')
        .attr('class', 'label y2-axis-label')
        .attr('transform', 'rotate(+90)')
        .attr('y', 0 - (width + margin.left + margin.right))
        .attr('x', 0 + ((height + margin.top) / 2))
        .attr('dy', '2em')
        .style('text-anchor', 'middle')
        .text('Badge Set Reached');
      // Add brush to svg
      context.append('g')
        .attr('class', 'x brush')
        .call(brush)
        .selectAll("rect")
        .attr("y", -6)
        .attr("height", navHeight + 7);
      function brushed() {
        time.domain(brush.empty() ? navTime.domain() : brush.extent())
          .range([0, (width)]);
        x.domain(max_extent_in_days(time))
          .rangeBands([margin.left, (width)], 0.1, 0);
        focus.selectAll('.bar.stack')
          .attr('transform', function(d) {
            return "translate(" + time(d.date) + ",0)";
          })
          .attr('width', x.rangeBand());
        focus.selectAll('.rect')
          .attr('width', x.rangeBand());
        focus.selectAll('.line').attr('d', line);
        focus.select(".x.axis").call(x_axis);
      };
    })();
  </script>
</body>
</html>