2012-03-10 27 views
19

Sto usando d3.js per eseguire il rendering di una mappa del mondo in svg (utilizzando https://github.com/johan/world.geo.json/blob/master/countries.geo.json per le funzionalità). Sto incapsulando la logica di rendering in una vista backbone. Quando eseguo il rendering della vista e la allego al DOM, non viene visualizzato nulla nel mio browser, sebbene il markup SVG sia generato correttamente quando si guarda l'HTML generato. Questo rende bene quando non incapsula all'interno di un Backbone.View. Ecco il mio codice utilizzando Backbone.view:SVG non visualizzato correttamente come vista backbone

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

Ecco il codice che funziona, senza l'utilizzo di 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); 

ho anche allegato uno screenshot del markup generato SVG. Qualche idea di cosa potrebbe andare storto qui?

enter image description here

Edit - Ecco il metodo make override che ha finito per la soluzione di questo, per richiesta:

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

risposta

26

Il problema è che l'elemento "svg" richiede uno spazio dei nomi. D3 fa questo per il tuo automaticamente; quando aggiungi un elemento "svg", usa lo spazio dei nomi "http://www.w3.org/2000/svg". Per dettagli, vedere src/core/ns.js. Il backbone, sfortunatamente, non sembra supportare gli elementi namespace. Vorresti cambiare il metodo view.make. Quindi hai bisogno di una proprietà namespaceURI sulla tua vista per impostare lo spazio dei nomi appropriato, o semplicemente farlo automaticamente per gli elementi SVG per coerenza con il parser HTML5.

In ogni caso, una semplice soluzione per il tuo problema è avvolgere il tuo SVG in un elemento DIV, e quindi usare D3 per creare l'elemento SVG.

+0

Grazie! Ho scavalcato 'make' a mio avviso e ho usato createElementNS con 'http://www.w3.org/2000/svg' invece di createElement e sembra che faccia il trucco. Apprezzo la guida. –

+1

@rr: potresti pubblicare il tuo metodo make da qualche parte? –

+0

@PierreSpring: Fatto, non sono sicuro di quanto sia rilevante con le versioni più recenti di Backbone, non ho tenuto il passo. –

4

si può semplicemente impostare elemento della vista nella funzione di inizializzazione come segue:

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

Questo ha il vantaggio di essere esplicita.

+1

Questo è ciò che viene menzionato dagli autori della backbone qui: https://github.com/documentcloud/backbone/pull/1357 –

+1

Non è necessario creare un div, tuttavia 'setElement (documento. createElementNS ('http://www.w3.org/2000/svg','svg')) 'sta bene. –

3

check this out http://jsfiddle.net/nocircleno/QsEp2/ da 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); 
    } 
} 
}); 
Problemi correlati