Snap.svg и JavaScript: создание фигур и анимация каждой с задержкой внутри цикла for

впервые/давно (кря, кря).

Я немного расстроен и почти озадачен этой загадкой, которую я не могу решить в Snap.svg. Вероятно, это оплошность, за которую я буду ругать себя за пропажу, но сейчас я этого не вижу.

У меня есть данные x и y, которые я извлекаю из элемента DOM и сохраняю в серию массивов, фильтрую на основе определенных значений в определенных столбцах и, в конечном итоге, создаю несколько экземпляров объекта ChartLine в js. По сути, он сортирует определенный столбец по количеству, присваивает каждой строке значения цвет из объекта RainbowVis.js, помещает все соответствующие значения из каждой строки в массив для y и рисует путь на линейной диаграмме, где y — это значение. а x — постоянно возрастающее целое число в цикле For.

Что я сейчас делаю здесь, в функции draw(), так это: для каждого соответствующего столбца создайте <circle> с переменной «точка» с переменными x и y объекта, назначьте атрибуты, анимируйте радиус от 0 до 8 за четверть секунды и добавить значения x и y i в строку, которая будет использоваться в <path>, которую я создаю сразу после цикла For. Затем я анимирую путь и т.д. и т.п.

Без setTimeout() работает хорошо. Все круги и пути анимируются одновременно при загрузке. Однако я хочу добавить задержку к каждому .animate с увеличением числа миллисекунд на polyDelayInterval в каждой итерации, чтобы каждая "точка" анимировалась по мере поступления строки. По крайней мере, я хочу анимировать все «точки» после завершения анимации пути.

Проблема в том, что независимо от того, что я пробовал до сих пор, я могу анимировать только последний набор «точек» (с максимальным значением x для каждой строки); остальные остаются на r:0. Я прочитал несколько похожих сообщений, как здесь, так и в другом месте; Я искал вверх и вниз документы Snap.svg на их сайте. Я просто не могу найти, что я делаю неправильно. Заранее спасибо!

var svgMFLC = Snap('svg#ElementID');


function ChartLine(x, y, color, row) {  
  this.x = x;
  this.y = y;
  this.color = color;
  this.row = row;
  var propNames = Object.keys(this.row);
  var yAdjust;
  var resetX = this.x;

  for (var i = 0; i < propNames.length; i++) { //  get only the calculated score columns and their values
    if (propNames[i].toLowerCase().includes(calcKeyword)) {
      yAdjustedToChartArea = chartBottom - (this.row[propNames[i]] * yInterval);
      this.y.push(yAdjustedToChartArea);    //  returns the value of that score column and pushes it to the y array
    }
  }

  this.draw = function () {
    var points = "M";   //  the string that will determine the coordinates of each line
    var dotShadow = svgMFLC.filter(Snap.filter.shadow(0, 0, 2, "#000000", 0.4));
    var polyTime = 1500;    //  in milliseconds
    var dot;
    var polyDelayInterval = polyTime / (semesterCols.length - 1);
    for (var i = 0; i < semesterCols.length; i++) { //  for each data point, create a "dot"
      dot = svgMFLC.circle(this.x, this.y[i], 0);
      dot.attr({
        fill: this.color,
        stroke: "none",
        filter: dotShadow,
        class: "chartPointMFLC"
      });
      setTimeout(function () {
        dot.animate({ r: 8 }, 250, mina.easeout);
      }, polyDelayInterval * i);
      points += this.x + " " + this.y[i] + " L";
      this.x = this.x + xInterval;
    }
    points = points.slice(0, -2);   //  take away the excessive " L" from the end of the points string
    var poly = svgMFLC.path(points);    
    var polyLength = poly.getTotalLength(); 
    poly.attr({
      fill: "none",
      stroke: this.color,
      class: "chartLineMFLC",
      strokeDasharray: polyLength + " " + polyLength, //  setting the strokeDash attributes will help create the "drawing the line" effect when animated
      strokeDashoffset: polyLength
    });
    poly.animate({ strokeDashoffset: 0.00 }, polyTime);
    this.x = resetX;
  }
}

person ToddOMassey    schedule 09.02.2018    source источник


Ответы (1)


Я не могу протестировать решение без полного кода для тестирования, но проблема почти наверняка в том, что вам, по крайней мере, нужно получить замыкание для вашего элемента "точка".

Итак, эта линия...

setTimeout(function () {
    dot.animate({ r: 8 }, 250, mina.easeout);
}, polyDelayInterval * i);

Когда дело доходит до вызова этой функции, «точка» будет последней «точкой» в цикле. Поэтому вам нужно создать замыкание (создать функциональную область для точки).

Так что-то вроде...

(function() {
    var laterDot = dot;
    setTimeout(function () {
        laterDot.animate({ r: 8 }, 250, mina.easeout);
    }, polyDelayInterval * i)
})();
person Ian    schedule 09.02.2018
comment
ДА! Это ответ! Я видел другие сообщения о предоставлении dot собственной области действия, но это не сработало для меня, ПОКА я не воспользовался вашей идеей создать для нее новую переменную (позже Dot). Большое спасибо; диаграмма теперь выглядит великолепно. - person ToddOMassey; 09.02.2018