2012-03-10 2 views
19

Я использую d3.js, чтобы сделать карту мира в формате SVG (с использованием https://github.com/johan/world.geo.json/blob/master/countries.geo.json для функций). Я инкапсулирую визуализацию в представлении Backbone. Когда я визуализую представление и прикрепляю его к DOM, в моем браузере ничего не отображается, хотя разметка SVG правильно генерируется при просмотре сгенерированного HTML. Это делает штраф, если он не инкапсулируется в Backbone.View. Вот мой код, используя Backbone.view:SVG не отображается правильно как вид магистральную

/** 
* SVG Map view 
*/ 
var MapView = Backbone.View.extend({ 
    tagName: 'svg', 
    translationOffset: [480, 500], 
    zoomLevel: 1000, 

    /** 
    * Sets up the map projector and svg path generator 
    */ 
    initialize: function() { 
     this.projector = d3.geo.mercator(); 
     this.path = d3.geo.path().projection(this.projector); 
     this.projector.translate(this.translationOffset); 
     this.projector.scale(this.zoomLevel); 
    }, 

    /** 
    * Renders the map using the supplied features collection 
    */ 
    render: function() { 
     d3.select(this.el) 
      .selectAll('path') 
      .data(this.options.featureCollection.features) 
      .enter().append('path') 
      .attr('d', this.path); 
    }, 

    /** 
    * Updates the zoom level 
    */ 
    zoom: function(level) { 
     this.projector.scale(this.zoomLevel = level); 
    }, 

    /** 
    * Updates the translation offset 
    */ 
    pan: function(x, y) { 
     this.projector.translate([ 
      this.translationOffset[0] += x, 
      this.translationOffset[1] += y 
     ]); 
    }, 

    /** 
    * Refreshes the map 
    */ 
    refresh: function() { 
     d3.select(this.el) 
      .selectAll('path') 
      .attr('d', this.path); 
    } 
}); 

var map = new MapView({featureCollection: countryFeatureCollection}); 
map.$el.appendTo('body'); 
map.render(); 

Вот код, который работает без использования Backbone.View

var projector = d3.geo.mercator(), 
    path = d3.geo.path().projection(projector), 
    countries = d3.select('body').append('svg'), 
    zoomLevel = 1000; 

coords = [480, 500]; 
projector.translate(coords); 
projector.scale(zoomLevel); 

countries.selectAll('path') 
     .data(countryFeatureCollection.features) 
     .enter().append('path') 
     .attr('d', path); 

Я также приложил скриншот сгенерированного SVG разметки. Любая идея, что здесь может быть неправильным?

enter image description here

Edit - Вот переопределяется метод делают, что в конечном итоге решить это, по запросу:

/** 
* Custom make method needed as backbone does not support creation of 
* namespaced HTML elements. 
*/ 
make: function(tagName, attributes, content) { 
    var el = document.createElementNS('http://www.w3.org/2000/svg', tagName); 
    if (attributes) $(el).attr(attributes); 
    if (content) $(el).html(content); 
    return el; 
} 

ответ

26

Вопрос заключается в том, что элемент «SVG» требует пространства имен. D3 делает это автоматически; когда вы добавляете элемент «svg», он использует пространство имен «http://www.w3.org/2000/svg». Для получения дополнительной информации см. src/core/ns.js. К сожалению, магистральная система не поддерживает элементы с именами. Вы хотите изменить метод view.make. Тогда вам понадобится свойство namespaceURI на вашем представлении, чтобы установить соответствующее пространство имен или просто сделать это автоматически для элементов SVG для согласованности с парсером HTML5.

Во всяком случае, простое решение проблемы заключается в том, чтобы обернуть SVG в элемент DIV, а затем использовать D3 для создания элемента SVG.

+0

Спасибо! Я изменил «make» на мой взгляд и использовал createElementNS с «http://www.w3.org/2000/svg» вместо createElement и, похоже, это трюк. Цените руководство. –

+1

@rr: Вы могли бы разместить свой метод make где-нибудь? –

+0

@PierreSpring: Сделано, не уверен, насколько уместно это с более новыми версиями Backbone, я не поддерживал. –

4

Вы можете просто установить элемент мнение, в функции инициализации следующим образом:

Backbone.View.extend({ 
    // This is only for informaiton. The node will 
    // be raplaced in the initialize function. 
    tagName: 'svg', 

    initialize: function() { 
     this.setElement(
      d3.select($('<div/>')[0]).append('svg')[0] 
     ); 
    } 
); 

Это имеет преимущество в том, Явные.

+1

Это то, что упомянуто авторами основной ссылки здесь: https://github.com/documentcloud/backbone/pull/1357 –

+1

Вам не нужно создавать div, хотя 'setElement (document. createElementNS ('http://www.w3.org/2000/svg''svg')) 'отлично. –

3

Проверить это http://jsfiddle.net/nocircleno/QsEp2/ от http://nocircleno.com/blog/svg-with-backbone-js/

Backbone.View.extend({ 
    nameSpace: "http://www.w3.org/2000/svg", 
    _ensureElement: function() { 
    if (!this.el) { 
     var attrs = _.extend({}, _.result(this, 'attributes')); 
     if (this.id) attrs.id = _.result(this, 'id'); 
     if (this.className) attrs['class'] = _.result(this, 'className'); 
     var $el = $(window.document.createElementNS(_.result(this, 'nameSpace'), _.result(this, 'tagName'))).attr(attrs); 
     this.setElement($el, false); 
    } else { 
     this.setElement(_.result(this, 'el'), false); 
    } 
} 
}); 
Смежные вопросы