将缩放行为从v3更新到v5

时间:2019-03-27 12:05:23

标签: d3.js charts zoom

我面临的问题是由于版本不匹配,我无法在代码中编写以下函数。 代码是

var zoom = d3.behavior.zoom()
.x(x)
.y(y)
.scaleExtent([1, 10])
.on("zoom", zoomed);

我已经尝试过这种方式

  const zoom = d3.zoom()
  .scaleExtent([1, 4])
  .x(this.xScale)
  .on('zoom', () => {})

但这对我不起作用。

如何在d3版本5中编写相同的功能?我想使用d3版本5将折线图在y轴作为固定位置的x轴上滚动

这是我的实现基本代码

    private createLineChart() {
    this.width = 2000 - this.margin.left - this.margin.right;
    this.height = 600 - this.margin.top - this.margin.bottom;

// X AXIS
this.xScale = d3.scaleBand()
  .domain(this.dataset[0].fluencyData.map((data) => {
    return new Date(data.date);
  }))
  .range([0, this.width]);

// Y AXIS
this.yScale = d3.scaleLinear()
  .domain([0, 110])
  .range([this.height, 0]);

// Line Generator
this.line = d3.line()
  .x((data) => this.xScale(new Date(data.date)))
  .y((data) => this.yScale(data.wcpm));
// .curve(d3.curveMonotoneX);

// Add SVG to Div
this.svg = d3.select('#displayChart').append('svg')
  .attr('preserveAspectRatio', 'xMinYMin meet')
  .attr(
    'viewBox',
    '0 0 ' +
    (this.width + this.margin.left + this.margin.right) +
    ' ' +
    (this.height + this.margin.top + this.margin.bottom))
  // .attr('width', this.width + this.margin.left + this.margin.right)
  // .attr('height', this.height + this.margin.top + this.margin.bottom)
  .append('g')
  .attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');

// Define the div for the tooltip
this.toolTipDiv = d3.select('#displayChart').append('div')
  .attr('class', 'tooltip')
  .style('opacity', 0);

// Append XAXIS to the SVG
this.svg.append('g')
  .attr('class', 'xAxis')
  .attr('transform', 'translate(0,' + this.height + ')')
  .call(d3.axisBottom(this.xScale).tickSizeOuter(0).tickFormat(d3.timeFormat('%b %d')));

const zoom = d3.zoom()
  .scaleExtent([1, 4])
  .extent([100, 100], [this.width - 100, this.height - 100])
  .x(this.xScale)
  .on('zoom', () => {
    console.log(d3.event.transform);
    // this.svg.select('#displayChart').attr('d', this.line);
  });
this.svg.call(zoom);

// Append YAXIS to SVG
this.svg.append('g')
  .attr('class', 'yAxis')
  .call(d3.axisLeft(this.yScale).tickSize(-this.width)
  );

// Make a Path for Dataset
this.svg.append('path')
  .datum(this.dataset[0].fluencyData)
  .attr('class', 'line')
  .attr('d', this.line)
  .attr('transform', 'translate(' + this.margin.left + ',0)');

// Text Heading of DATE in chart
this.svg.append('text')
  .attr('transform', 'translate(' + (-20) + ',' + (this.height + 13) + ')')
  .attr('dy', '.35em')
  .attr('class', ' xAxis')
  .text('Date');
 }
 }

我遇到的错误是

LineChartComponent_Host.ngfactory.js? [sm]:1 ERROR TypeError: d3__WEBPACK_IMPORTED_MODULE_2__.zoom(...).scaleExtent(...).x is not a function
    at LineChartComponent.push../src/app/line-chart/line-chart.component.ts

1 个答案:

答案 0 :(得分:3)

使用d3v3及更早版本,缩放可以跟踪刻度的状态。在文档中,scale.x():“指定在缩放时应自动调整其域的x比例尺。” (docs)。这样会修改原始比例。

D3v4 +没有zoom.x或zoom.y方法。

使用d3v4 +,缩放不会跟踪或修改d3标尺的状态。实际上,对于d3v4 +,缩放行为甚至无法跟踪当前的缩放状态:“缩放行为不再在内部存储活动的缩放变换(即可见区域;缩放和平移)。现在,缩放变换存储在任何缩放行为已应用到的元素。(change log)”。

其中的一部分,更重要的是,“缩放行为不再取决于比例,而是可以使用transform.rescaleX,transform.rescaleY,transform.invertX或transform.invertY来转换比例域({{3 }})”。

因此,我们不必自己进行缩放来更新d3比例尺。最常见的方法是通过保持不变的参考比例以及应用缩放变换的比例:

var zoom = d3.zoom()
  .on("zoom",zoomed)

var x = d3.scaleLinear()....   // working scale
var x2 = x.copy(); // reference scale.

function zoomed() {
  x = d3.event.transform.rescaleX(x2) // update the working scale.
  // do something...
}

所以,像这样:

var x = d3.scale.linear()
  .domain([0,1])
  .range([0,500]);

var zoom = d3.behavior.zoom()
.x(x)
.scaleExtent([1, 10])
.on("zoom", zoomed);

var svg = d3.select("svg")
  .call(zoom);
  
var axis = d3.svg.axis()
   .orient("bottom")
   .scale(x);
  
var axisG = svg.append("g")
    .attr("transform", "translate(0,30)")
    .call(axis);
  
function zoomed() {
   axisG.call(axis);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>

<svg width="500" height="200"></svg>

成为类似这样的东西:

var x = d3.scaleLinear()
  .domain([0,1])
  .range([0,500]);
var x2 = x.copy(); // reference.

var zoom = d3.zoom()
  .scaleExtent([1, 10])
  .on("zoom", zoomed);

var svg = d3.select("svg")
  .call(zoom);
  
var axis = d3.axisBottom().scale(x)
  
var axisG = svg.append("g")
    .attr("transform", "translate(0,30)")
    .call(axis);
  
function zoomed() {
   x = d3.event.transform.rescaleX(x2)
   axis.scale(x);
   axisG.call(axis);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

<svg width="500" height="200"></svg>

请注意,d3.event.transform.rescaleX用于连续刻度-您有序带刻度,因此我们需要对带刻度和/或点刻度使用略微修改的方法:

var x = d3.scaleBand()
  .domain(d3.range(10).map(function(d) { return d/10; }))
  .range([0,500]);
var x2 = x.copy(); // reference.

var zoom = d3.zoom()
  .scaleExtent([1, 10])
  .on("zoom", zoomed);

var svg = d3.select("svg")
  .call(zoom);
  
var axis = d3.axisBottom().scale(x)
  
var axisG = svg.append("g")
    .attr("transform", "translate(0,30)")
    .call(axis);
  
function zoomed() {
   // Rescale the range of x using the reference range of x2.
   x.range(x2.range().map(function(d) {
     return d3.event.transform.applyX(d);
   }))
   axisG.call(axis);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

<svg width="500" height="200"></svg>

这是基于change log和Bostock提出的解决方案的带/点比例解决方案