在d3中创建新波段时如何实现平滑过渡?

时间:2017-01-23 02:55:15

标签: d3.js

在下面的示例中,我尝试为新项目设置外观。

如您所见,它们从图表底部动画到其位置。

但是,现有项目("第二个"在此示例中)跳转,而不是平稳过渡到新位置。

我认为这是因为新乐队突然出现,没有过渡。所以,我尝试添加转换:

const band = bandUpdate.enter()
  .append('g')
    .attr('class', 'band')
  .merge(bandUpdate)
    .transition(t)
    .attr('transform', (_, i) => `translate(0, ${i * bandHeight})`);

但是,我得到了:

  

未捕获的TypeError:band.selectAll(...)。data不是函数

请你解释一下这个错误,并建议一种避免意外跳跃的方法吗?

加分:我如何为y轴标签设置动画?

Playground



const width = 300;
const height = 200;
const margin = { top: 30, right: 30, bottom: 30, left: 50 };

let data = {};

const main = d3.select('.chart')
  .append('svg')
    .attr('width', width + margin.left + margin.right)
    .attr('height', height + margin.top + margin.bottom)
  .append('g')
    .attr('transform', `translate(${margin.left}, ${margin.top})`);

const xScale = d3.scaleLinear().domain([0, 16]).range([0, width]);
const xAxis = d3.axisBottom(xScale);

main.append('g')
  .attr('transform', `translate(0, ${height})`)
  .call(xAxis);

const yScale = d3.scaleBand().domain([]).range([0, height]);
const yAxis = d3.axisLeft(yScale);
const yAxisG = main.append('g').call(yAxis);

const bandG = main.append('g');

function update() {
  const t = d3.transition().duration(500);
  const ids = Object.keys(data);
  
  yScale.domain(ids);
  yAxisG.call(yAxis);
  
  const bandHeight = yScale.bandwidth();
  const bandUpdate = bandG.selectAll('.band').data(ids, id => id);
  const band = bandUpdate.enter()
    .append('g')
      .attr('class', 'band')
    .merge(bandUpdate)
      // .transition(t) // Throws: Uncaught TypeError: band.selectAll(...).data is not a function
      .attr('transform', (_, i) => `translate(0, ${i * bandHeight})`);

  bandUpdate.exit().remove();
  
  const itemUpdate = band.selectAll('.item')
    .data(id => data[id], item => item.value);
  
  const itemG = itemUpdate.enter().append('g').attr('class', 'item');
  const rectHeight = 4;
  
  itemG
    .append('rect')
    .attr('class', (_, i) => `item-${i}`)
    .attr('x', d => xScale(d.value))
    .attr('width', d => width - xScale(d.value))
    .attr('height', rectHeight)
    .attr('y', height)
    .transition(t)
    .attr('y', bandHeight / 2 - rectHeight / 2);

  itemG
    .append('circle')
    .attr('class', (_, i) => `item-${i}`)
    .attr('cx', d => xScale(d.value))
    .attr('r', 6)
    .attr('cy', height)
    .transition(t)
    .attr('cy', bandHeight / 2);
  
  itemUpdate
    .select('rect')
    .attr('x', d => xScale(d.value))
    .attr('width', d => width - xScale(d.value))
    .transition(t)
    .attr('y', bandHeight / 2 - rectHeight / 2);    
  
  itemUpdate
    .select('circle')
    .attr('cx', d => xScale(d.value))
    .transition(t)
    .attr('cy', bandHeight / 2);
  
  itemUpdate.exit().remove();
}

update();

setTimeout(() => {
  data['first'] = [
    {
      value: 7
    },
    {
      value: 10
    }
  ];
  
  update();
}, 1000);

setTimeout(() => {
  data['second'] = [
    {
      value: 1
    }
  ];
  
  update();
}, 2000);

setTimeout(() => {
  data['third'] = [
    {
      value: 13
    }
  ];
  
  update();
}, 3000);

svg {
  margin: 0 30px 30px 30px;
}

.item-0 {
  fill: red;
}

.item-1 {
  fill: green;
}

<div class="chart"></div>
<script src="https://unpkg.com/d3@4.4.1/build/d3.js"></script>
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:1)

打破你的band常数:

const band = bandUpdate.enter()
    .append('g')
    .attr('class', 'band')
    .merge(bandUpdate);

band.transition(t)
    .attr('transform', (_, i) => `translate(0, ${i * bandHeight})`);

以下是更新的CodePen:http://codepen.io/anon/pen/oBWJdp?editors=0010

说明:

根据文件,selection.transition([name])

  

返回具有指定名称的给定选择的新转换。

因此,当您稍后在代码中执行此操作时:

const itemUpdate = band.selectAll('.item')
    .data(id => data[id], item => item.value);

您正在选择新转换,并且会给您错误(您无法将数据绑定到转换)。

打破band常量会使itemUpdate选择基于band选择,而不是在以下转换中。