SVG 路径超出 d3 画笔上的图表区域

2024-04-22

当我尝试刷动和缩放折线图的一部分时,所选区域的某些部分会呈现在图表之外。

代码和行为再现可以在以下位置找到这个jsbin https://jsbin.com/jamojonaqu/edit?js,output.

单击并拖动以选择一部分并放大,双击以缩小。

var svg = d3
  .select('body')
  .append('svg')
  .attr('class', 'chart')
  .attr('width', 960)
  .attr('height', 500);
var margin = {
  top: 40,
  right: 40,
  bottom: 40,
  left: 40
};
var width = +svg.attr('width') - margin.left - margin.right;
var height = +svg.attr('height') - margin.top - margin.bottom;
var g = svg
  .append('g')
  .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var timeParser = d3.timeParse('%Y-%m-%d');
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var brush = d3.brush().on('end', brushended);
var idleTimeout;
var idleDelay = 350;
var x0;
var y0;
var xAxis;
var yAxis;
var line = d3
  .line()
  .x(function(d) {
    return x(d.date);
  })
  .y(function(d) {
    return y(d.price);
  })
  .curve(d3.curveNatural);
var start = new Date();
var end = new Date(start.toDateString());
start.setFullYear(end.getFullYear() - 1);
var startStr = start.toISOString().slice(0, 10);
var endStr = end.toISOString().slice(0, 10);
var url = "https://api.coindesk.com/v1/bpi/historical/close.json?start=" + startStr + "&end=" + endStr;
d3.json(url, function(error, response) {
  var data = Object.keys(response.bpi).map(function(date) {
    return {
      date: timeParser(date),
      price: response.bpi[date]
    };
  });
  x0 = d3.extent(data, function(d) {
    return d.date;
  });
  y0 = d3.extent(data, function(d) {
    return d.price;
  });
  x.domain(x0);
  y.domain(y0);
  xAxis = d3.axisBottom(x);
  yAxis = d3.axisLeft(y);
  g
    .append('g')
    .attr('class', 'axis axis--x')
    .attr('transform', 'translate(0,' + height + ')')
    .call(xAxis);
  g
    .append('g')
    .attr('class', 'axis axis--y')
    .call(yAxis);
  g
    .append('path')
    .attr('class', 'line')
    .datum(data)
    .attr('fill', 'none')
    .attr('stroke', 'steelblue')
    .attr('d', line);
  svg
    .append('g')
    .attr('class', 'brush')
    .call(brush);
});

function brushended() {
  var s = d3.event.selection;
  if (!s) {
    if (!idleTimeout) {
      return (idleTimeout = setTimeout(idled, idleDelay));
    }
    x.domain(x0);
    y.domain(y0);
  } else {
    x.domain([s[0][0] - 40, s[1][0] - 40].map(x.invert, x));
    y.domain([s[1][1] - 40, s[0][1] - 40].map(y.invert, y));
    svg.select('.brush').call(brush.move, null);
  }
  zoom();
}

function idled() {
  idleTimeout = null;
}

function zoom() {
  var t = svg.transition().duration(750);
  svg
    .select('.axis--x')
    .transition(t)
    .call(xAxis);
  svg
    .select('.axis--y')
    .transition(t)
    .call(yAxis);
  svg
    .select('.line')
    .transition(t)
    .attr('d', line);
}
.chart {
  border: 1px solid #bdbdbd;
  box-sizing: border-box;
}
<script src="https://unpkg.com/[email protected] /cdn-cgi/l/email-protection/build/d3.min.js"></script>

这是预期的行为。处理这个问题最常见的方法是使用<clipPath> https://developer.mozilla.org/en-US/docs/Web/SVG/Element/clipPath.

例如,在你的情况下:

var clipPath = g.append("defs")
    .append("clipPath")
    .attr("id", "clip")
    .append("rect")
    .attr("width", width)
    .attr("height", height);

然后,在你的路径中:

g.append('path')
    //etc...
    .attr("clip-path", "url(#clip)");

这是更新后的 JSBin:https://jsbin.com/tatuhipevi/1/edit?js,输出 https://jsbin.com/tatuhipevi/1/edit?js,output

这里是更新后的 S.O.片段:

var svg = d3
  .select('body')
  .append('svg')
  .attr('class', 'chart')
  .attr('width', 960)
  .attr('height', 500);
var margin = {
  top: 40,
  right: 40,
  bottom: 40,
  left: 40
};
var width = +svg.attr('width') - margin.left - margin.right;
var height = +svg.attr('height') - margin.top - margin.bottom;
var g = svg
  .append('g')
  .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var clipPath = g.append("defs")
  .append("clipPath")
  .attr("id", "clip")
  .append("rect")
  .attr("width", width)
  .attr("height", height);
var timeParser = d3.timeParse('%Y-%m-%d');
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var brush = d3.brush().on('end', brushended);
var idleTimeout;
var idleDelay = 350;
var x0;
var y0;
var xAxis;
var yAxis;
var line = d3
  .line()
  .x(function(d) {
    return x(d.date);
  })
  .y(function(d) {
    return y(d.price);
  })
  .curve(d3.curveNatural);
var start = new Date();
var end = new Date(start.toDateString());
start.setFullYear(end.getFullYear() - 1);
var startStr = start.toISOString().slice(0, 10);
var endStr = end.toISOString().slice(0, 10);
var url = "https://api.coindesk.com/v1/bpi/historical/close.json?start=" + startStr + "&end=" + endStr;
d3.json(url, function(error, response) {
  var data = Object.keys(response.bpi).map(function(date) {
    return {
      date: timeParser(date),
      price: response.bpi[date]
    };
  });
  x0 = d3.extent(data, function(d) {
    return d.date;
  });
  y0 = d3.extent(data, function(d) {
    return d.price;
  });
  x.domain(x0);
  y.domain(y0);
  xAxis = d3.axisBottom(x);
  yAxis = d3.axisLeft(y);
  g
    .append('g')
    .attr('class', 'axis axis--x')
    .attr('transform', 'translate(0,' + height + ')')
    .call(xAxis);
  g
    .append('g')
    .attr('class', 'axis axis--y')
    .call(yAxis);
  g
    .append('path')
    .attr('class', 'line')
    .datum(data)
    .attr('fill', 'none')
    .attr('stroke', 'steelblue')
    .attr('d', line)
    .attr("clip-path", "url(#clip)");
  svg
    .append('g')
    .attr('class', 'brush')
    .call(brush);
});

function brushended() {
  var s = d3.event.selection;
  if (!s) {
    if (!idleTimeout) {
      return (idleTimeout = setTimeout(idled, idleDelay));
    }
    x.domain(x0);
    y.domain(y0);
  } else {
    x.domain([s[0][0] - 40, s[1][0] - 40].map(x.invert, x));
    y.domain([s[1][1] - 40, s[0][1] - 40].map(y.invert, y));
    svg.select('.brush').call(brush.move, null);
  }
  zoom();
}

function idled() {
  idleTimeout = null;
}

function zoom() {
  var t = svg.transition().duration(750);
  svg
    .select('.axis--x')
    .transition(t)
    .call(xAxis);
  svg
    .select('.axis--y')
    .transition(t)
    .call(yAxis);
  svg
    .select('.line')
    .transition(t)
    .attr('d', line);
}
.chart {
  border: 1px solid #bdbdbd;
  box-sizing: border-box;
}
<script src="https://d3js.org/d3.v4.min.js"></script>

另外,使用<clipPath>也在轴上。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SVG 路径超出 d3 画笔上的图表区域 的相关文章

随机推荐