SVG路径超出d3笔刷上的图表区域 [英] SVG path goes beyond chart area on d3 brush

查看:56
本文介绍了SVG路径超出d3笔刷上的图表区域的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我尝试刷&放大折线图的一部分,选定区域的某些部分在图表外呈现.

When I try to brush & zoom a portion of the line chart, some parts of the selected area render outside the chart.

可以在此jsbin 中找到代码和行为再现.

Code and behavior reproduction can be found at this jsbin.

点击&拖动以选择一个部分并放大,双击以缩小.

Click & drag to select a portion and zoom in, double click to zoom out.

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/d3@4.12.2/build/d3.min.js"></script>

推荐答案

这是预期的行为.解决该问题的最常见方法是使用 < clipPath> .

That's the expected behaviour. The most common way to deal with that is using a <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/编辑?js,输出

Here is the updated JSBin: https://jsbin.com/tatuhipevi/1/edit?js,output

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

And here the updated S.O. snippet:

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> 是一个好主意.

Also, it's a good idea using a <clipPath> in the axes as well.

这篇关于SVG路径超出d3笔刷上的图表区域的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆