2012-03-10 72 views
19

我使用d3.js渲染世界地圖的svg(使用https://github.com/johan/world.geo.json/blob/master/countries.geo.json作爲特徵)。我將渲染邏輯封裝在Backbone View中。當我渲染視圖並將其附加到DOM時,雖然在查看生成的HTML時正確生成了SVG標記,但瀏覽器中沒有顯示任何內容。當沒有封裝在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

編輯 - 下面是重寫的化妝方法,它結束瞭解決這個,每個請求:

/** 
* 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',並用'http://www.w3.org/2000/svg'而不是createElement來使用createElementNS,這看起來有訣竅。欣賞指導。 – 2012-03-11 06:43:22

+1

@rr:你可以在某處發佈make方法嗎? – 2012-07-23 13:58:33

+0

@PierreSpring:完成,不確定這與更新版本的骨幹有多相關,我一直沒有跟上。 – 2012-07-28 06:08:33

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

這具有explicite的優勢。

+1

這就是骨幹作者在這裏提到的:https://github.com/documentcloud/backbone/pull/1357 – 2012-09-01 02:01:54

+1

雖然你不需要創建一個div,但是'setElement(document。 createElementNS('http://www.w3.org/2000/svg','svg'))'很好。 – 2012-09-01 02:02:47

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