2013-12-24 3 views
2

У меня есть структура с данными и определения формы:Как создать формы SVG на основе данных?

var data = [ 
    { 
    "id": "first", 
    "shapes": [ 
     { 
     "shape": "polygon", 
     "points": [["8","64"],["8","356"],["98","356"],["98","64"]] 
     } 
    ] 
    }, 
    { 
    "id": "second", 
    "shapes": [ 
     { 
     "shape": "ellipse", 
     "cx": "63", "cy": "306", "rx": "27","ry": "18" 
     }, { 
     "shape": "polygon", 
     "points": [["174","262"],["171","252"],["167","262"]] 
     } 
    ] 
    } 
]; // in the data may be stored any SVG shape 

Я хотел бы создать SVG:

<svg width="218" height="400"> 
    <g transform="translate(0,400) scale(1,-1)"> 
    <g> 
     <polygon points="8,64 8,356 98,356 98,64"/> 
    </g> 
    <g> 
     <ellipse cx="63" cy="306" rx="27" ry="18"/> 
     <polygon points="174,262 171,252 167,262"/> 
    </g> 
    </g> 
</svg> 

Для каждого элемента data Я добавляющим <g>:

var groups = svg.selectAll("g").data(data, function (d) { 
    return d.id; 
}); 
groups.enter().append("g"); 

сейчас Я привязываю данные для групповых фигур:

var shapes = groups.selectAll(".shape").data(function (d) { 
    return d.shapes; 
}, function(d,i){return [d.shape,i].join('-');}); 

До сих пор это было так, как ожидалось. Теперь я хочу, чтобы для каждой входящей функции рисования узла DOM с правильной формой, но, видимо, shapes.enter().each() не работает в этом контексте (не определен). Я полагаю, что это работает на каждом узле DOM, чем на каждом подлежащем привязке. И это работает:

shapes.enter().append("g").each(function(draw, i) { 
    var shape = draw.shape; 
    d3.select(this).call(drawer[shape]); 
}); 

Но болезненный побочный эффект в том, что SVG имеет два уровня <g>:

<svg width="218" height="400"> 
    <g transform="translate(0,400) scale(1,-1)"> 
    <g> 
     <g> 
     <polygon points="8,64 8,356 98,356 98,64"/> 
     </g> 
    </g> 
    ... 
</svg> 

Как избавиться от этого? Как правильно строить данные?

+0

Вы можете поместить это в jsfiddle или jsbin. Я думаю, что я бы проверял каждую функцию, чтобы увидеть, какой результат визуализируется, и, возможно, получить форму svg как строку и добавить ее в innerHtml в каждом цикле. –

+1

Вы могли бы что-то сделать с этими строками с [superformula] (http://bl.ocks.org/mbostock/1021103) или с заранее вычисленными формами, такими как [здесь] (http://bl.ocks.org/mbostock/1062383). –

ответ

4

Два способа добавления базовых фигур на основе данных, предоставленных AmeliaBR, хорошо, но немного вне d3.js API. После долгих размышлений я решил добавить ответ на свой вопрос.

Вдохновение для поиска другого решения было комментировать Lars Kotthoff под вопросом, предлагая использовать пути вместо примитивных форм SVG. При таком подходе вместо добавления второго уровня <g> следует добавить <path>:

shapes.enter().append("path").attr("d", function(d) { 
    return drawer[d.shape](d); 
}); 

Оригинал drawer объект генерации основных форм будут возвращать значение для атрибута d из <path>.

var drawer = { 
     polygon: function (d) { 
      return [ 
       "M", d.points[0].join(','), 
       "L", d.points 
        .slice(1, d.points.length) 
        .map(function (e) { 
        return e.join(",") 
       }), "Z"].join(" "); 
     }, 
     ellipse: function (d) { 
      return [ 
       'M', [d.cx, d.cy].join(','), 
       'm', [-d.rx, 0].join(','), 
       'a', [d.rx, d.ry].join(','), 
       0, "1,0", [2 * d.rx, 0].join(','), 
       'a', [d.rx, d.ry].join(','), 
       0, "1,0", [-2 * d.rx, 0].join(',')].join(' '); 
     }, 
     circle: function (d) { 
      return this.ellipse({ 
       cx: d.cx, 
       cy: d.cy, 
       rx: d.r, 
       ry: d.r 
      }); 
     }, 
     rect: function (d) { 
      return this.polygon({ 
       points: [ 
        [d.x, d.y], 
        [d.x + d.width, d.y], 
        [d.x + d.width, d.y + d.height], 
        [d.x, d.y + d.height] 
       ] 
      }); 
     }, 
     path: function (d) { 
      return d.d; 
     } 
}; 

Рабочий пример можно проверить на http://fiddle.jshell.net/daKkJ/4/

+0

Отличная функция преобразования. Пока не было логической причины для сохранения отдельных типов элементов, это, вероятно, оптимальное решение, так как теперь все ваши элементы представляют собой единый набор элементов «пути», которые принимают одинаковые атрибуты (например, x и y для верхнего левого угла) , – AmeliaBR

3

Tricky вопрос. Позор, который вы не можете назвать каждый по выбору enter(). Вы также не можете использовать function(d){} в заявлении append. (См. Комментарии)

Но я получил его, используя Javascript forEach() вызов самого массива данных. Он вызывает вашу функцию с помощью элемента массива, индекса и массива в качестве параметров, и вы можете указать контекст this - я просто передал искомый родительский элемент в качестве выделения.

Сказочные скрипку: http://fiddle.jshell.net/994XM/1/
(проще, чем ваш случай, но должен иметь возможность легко адаптировать)

Это немного сбивает с толку, так как код D3 внутри forEach все на элемент, с d и i значения уже доступны для вас, поэтому вам не нужны какие-либо внутренние функции в ваших вызовах метода D3. Но как только вы это осознаете, все работает.

+0

Фактически вы можете использовать функцию, которая возвращает элемент DOM для добавления в '.append()'. –

+0

@ Lars Kotthoff: это то, что он говорит в спецификации, но при запуске он выдает «NotFoundError». Первый вызов анонимной функции внутри 'append()' корректно передается, но он возвращает ошибки при возврате, даже если я просто возвращаю статическую строку. Я предполагаю, что проблема заключается в том, что ссылка на 'this' неправильно установлена ​​в действительном вызове функции append. Вот [пример] (http://fiddle.jshell.net/994XM/6/) Я попробовал, любые предложения приветствуются. Если вы согласны с тем, что это * должно * работать, возможно, стоит разобраться в проблеме на gitHub. – AmeliaBR

+0

Вам необходимо создать и вернуть фактический элемент DOM - http://fiddle.jshell.net/994XM/7/ –

1

Я нашел этот вопрос через Google, и jsfiddles, связанные с выше перестали работать. Теперь вам нужно вызвать document.createElementNS, предоставив ему пространство имен SVG, иначе формы не будут отображаться.

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

+0

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

+0

Конечно, это нормально для вас, но в моем случае я хотел иметь прямоугольники или круги, в зависимости от типа узла. Данный узел никогда не меняет тип, поэтому нет необходимости в анимации. Кроме того, я хотел, чтобы другие знали, что я исправил скрипку - те, что связаны с выше, сломаны. – Klortho

Смежные вопросы