2012-03-10 10 views
19

Estoy usando d3.js para representar un mapa del mundo en svg (usando https://github.com/johan/world.geo.json/blob/master/countries.geo.json para las características). Estoy encapsulando la lógica de renderizado en una Vista Backbone. Cuando visualizo la vista y la adjunto al DOM, no aparece nada en mi navegador, aunque el marcado SVG se genera correctamente cuando se mira el HTML generado. Esto rinde bien cuando no encapsula dentro de Backbone.View. Aquí está mi código utilizando Backbone.view:SVG no se representa correctamente como una vista de red troncal

/** 
* 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(); 

Aquí está el código que funciona, sin necesidad de utilizar 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); 

También he adjuntado una captura de pantalla del marcado SVG generado. ¿Alguna idea de qué podría estar yendo mal aquí?

enter image description here

Editar - Este es el método reemplazado maquillaje que terminó la solución de este, por solicitud:

/** 
* 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; 
} 

Respuesta

26

La cuestión es que el elemento "SVG" requiere un espacio de nombres. D3 hace esto automáticamente; cuando agrega un elemento "svg", utiliza el espacio de nombres "http://www.w3.org/2000/svg". Para más detalles, vea src/core/ns.js. Por desgracia, Backbone no parece admitir elementos de espacio de nombres. Desearía cambiar el método view.make. Entonces necesitaría una propiedad namespaceURI en su vista para establecer el espacio de nombre apropiado, o simplemente hacerlo automáticamente para elementos SVG para coherencia con el analizador HTML5.

En cualquier caso, una solución simple para su problema es envolver su SVG en un elemento DIV, y luego usar D3 para crear el elemento SVG.

+0

Gracias! Sobreescribí 'make' desde mi punto de vista y utilicé createElementNS con 'http://www.w3.org/2000/svg' en lugar de createElement y eso parece ser el truco. Apreciar la guía. –

+1

@rr: ¿podría publicar su método make en alguna parte? –

+0

@PierreSpring: Hecho, no estoy seguro de cuán relevante es esto con las versiones más nuevas de Backbone, no he estado al día. –

4

usted podría simplemente establezca el elemento de la vista en la función de inicialización de la siguiente manera:

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] 
     ); 
    } 
); 

Esto tiene la ventaja de ser explicite.

+1

Esto es lo que mencionan los autores de la red troncal aquí: https://github.com/documentcloud/backbone/pull/1357 –

+1

No es necesario crear un div, 'setElement (document. createElementNS ('http://www.w3.org/2000/svg','svg')) 'está bien. –

3

mira esto http://jsfiddle.net/nocircleno/QsEp2/ de 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); 
    } 
} 
}); 
Cuestiones relacionadas