2016-09-09 4 views
1

Я создаю простую линейную диаграмму с d3, и я пытаюсь анимировать линию (прямо сейчас есть одна строка) при выполнении обновлений данных.animate d3.js line path path exit

// https://bl.ocks.org/mbostock/3883245 
// http://bl.ocks.org/d3noob/7030f35b72de721622b8 
function LineChart(options) { 
    // Mike Bostock margin conventions. 
    // See http://bl.ocks.org/mbostock/3019563 for more info. 
    const margin = this.margin = { 
     top: 20, 
     right: 20, 
     bottom: 30, 
     left: 40 
    }; 

    // Used by some of the functions that get a different context when called by d3. 
    const thisRef = this; 

    this.width = options.width - margin.left - margin.right; 
    this.height = options.height - margin.top - margin.bottom; 

    this.x = d3.time.scale() 
     .range([0, this.width]); 
    this.y = d3.scale.linear() 
     .range([this.height, 0]); 

    this.xAxis = d3.svg.axis() 
     .scale(this.x) 
     .orient('bottom'); 
    this.yAxis = d3.svg.axis() 
     .scale(this.y) 
     .orient('left'); 

    this.line = d3.svg.line() 
     // https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes#line_interpolate 
     .interpolate(options.interpolate) 
     .x(function(d) { 
      return thisRef.x(d.date); 
     }) 
     .y(function(d) { 
      return thisRef.y(d.value); 
     }); 

    // Create an SVG element (appended to `#chart`): 
    // 1. set size; 
    // 2. add a `g` element (as in a group) - the `svg` variable here is a `g` element now; 
    // 3. set the transform on `<g>` (not on `<svg>`). 
    this.svg = d3.select(options.target) 
     .append('svg') 
     .attr('width', this.width + margin.left + margin.right) 
     .attr('height', this.height + margin.top + margin.bottom) 
     .append('g') 
     .attr('transform', `translate(${margin.left}, ${margin.top})`); 

    // Add x axis. 
    this.svg.append('g') 
     .attr('class', 'x axis') 
     .attr('transform', `translate(0, ${this.height})`) 

    // Add y axis (with a label). 
    this.svg.append('g') 
     .attr('class', 'y axis') 
     // Just for the title (ticks are automatic). 
     .append('text') 
     // Rotate the text. 
     .attr('transform', 'rotate(-90)') 
     .attr('y', 6) 
     .attr('dy', '.71em') 
     .style('text-anchor', 'end') 
     .text('Price ($)'); 
} 

LineChart.prototype.update = function update(data) { 
    const thisRef = this; 

    this.x.domain(d3.extent(data, function(d) { 
     return d.date; 
    })); 
    this.y.domain(d3.extent(data, function(d) { 
     return d.value; 
    })); 

    this.svg.select('.x.axis') 
     .transition() 
     .call(this.xAxis); 
    this.svg.select('.y.axis') 
     .transition() 
     .call(this.yAxis) 

    const lines = this.svg.selectAll('.line') 
     .data(data, function(d) { 
      return d.label; 
     }); 

    lines.enter() 
     .append('path') 
     .attr('class', 'line'); 

    lines.transition() 
     .duration(1500) 
     .attr('d', this.line(data)); 

    lines.exit() 
     .remove(); 
} 

Вы можете увидеть пример работы here.

В примере есть задержка 3000 мс, после которой вы увидите обновление и анимацию.

Но у меня небольшая проблема, часть линии выбрасывается без какой-либо анимации.

У меня трудно понять, что происходит, как я не очень опытный с d3, какая-то помощь была бы оценена :)

ответ

1

D3 одушевляет элементы DOM. В вашем примере есть только один элемент DOM (строка), и D3 считает, что вы меняете одну строку (на 1500 мс). Он не знает, что делать с выброшенными точками (или с добавленными точками). Сегменты пути SVG - это всего лишь единица анимации. Выбор «ввести» и «выйти» не изменяется при изменении данных (и выбор «exit» пуст).

У вас есть два варианта:

  1. Перед удалением части данных, создают дополнительную строку (DOM элемент) с отсутствующими точками, и анимировать его отдельно.
  2. Создайте сегменты этой линии (таким образом, создавая несколько линий, по одной линии на каждые 2 точки) и анимируйте их (см. http://jsfiddle.net/L1tLot0v/15/). К сожалению, интерполяция не будет такой классной.

    var segmentedData = segments(data); 
    const lines = this.svg.selectAll('.line') 
        .data(segmentedData, function(d) { 
         return d[0].date; 
    }); 
    
    var lineFunc = this.line; 
    lines.enter() 
        .append('path') 
        .attr('class', 'line') 
        .style("opacity", 0.0) 
        .attr('d', function(d) { 
         return lineFunc(d)}) 
        .transition() 
        .duration(1000) 
        .style("opacity", 1.0); 
    
    lines.exit() 
        .transition() 
        .duration(1000) 
        .style("opacity", 0.0) 
        .remove(); 
    
Смежные вопросы