d3根據數據繪制不同的形狀


   繪制力導向圖的時候通常節點都是圓形,但也會遇到公司節點繪制成圓型,人繪制成方形的情況,那我們怎么依據數據繪制不同的形狀。

   你可能首先會想到,這很簡單啊,是公司的時候append circle,是人的時候append rect。但是append並沒有提供回調也就是說我們不能這樣做

node.append((data)=>{
   return data.type === 'person'  ? 'rect' : 'circle';  
});

  下面介紹兩種方案:

 第一種,先append一個g然后根據數據設置不同的類名

var nodeGUpdate = this.nodeG
                  .selectAll('g')
                  .data(this.nodesData, (data) => data.id);

var nodeGEnter = nodeGUpdate.enter(); var nodeGExit = nodeGUpdate.exit(); // 更新 nodeGUpdate
.transition() .attr(
'class', (data) => {   return (data.hide && 'hide') || (data.nodeStatus < 0 && 'noActive') || (data.cateType === 0 && 'mainCompany') || (data.cateType === 1 && 'relativeCompany') || (data.cateType === 2 && 'relativePerson'); }) nodeGEnter.append('g')
.attr(
'class', (data)=> {   return data.cateType === 2 ? 'person' : 'company'; })

然后依據類名append不同形狀

添加矩形

this.nodeG
.selectAll('.person')
.append('rect')
.attr('class', (data) => {
  return (data.hide && '.hide') || (data.cateType === 0 && 'mainCompany') || (data.cateType === 1 && 'relativeCompany') || (data.cateType === 2 && 'relativePerson');
 })
.attr('width', 20)
.attr('height', 20);

添加圓形

this.nodeG
.selectAll('.company')
.append('circle')
.attr('class', (data) => {
   return (data.hide && '.hide') || (data.cateType === 0 && 'mainCompany') || (data.cateType === 1 && 'relativeCompany') || (data.cateType === 2 && 'relativePerson');
 })
.attr('r', 20);

最后效果

這有點尷尬,矩形是以它自身的左上角的為基點,所以你可能還需要根據象限進行平移。

上面這種思路是我從其它文章看來的,但出處忘了,但是由於還要將矩形的中心移到線的端點太麻煩,所以最終沒有采用這種方法。

下面來講另外一種方法

整體思路是統一append circle 但是當是人的時候填充圓形。就是可以想象append的這個circle相當於一個透明的畫布,如果fill的值是顏色,那就是用這個顏色去填充這個圓。如果fill的是一個形狀,就是用這個形狀填充,由於背景是透明的,所以看起來好像append了其它形狀上去。

首先在svg中定義一個矩形,defs簡單來說就是一個容器,在這個元素里面你可以定義一些元素供你重復使用,例如箭頭和這里定義的矩形。

這里有幾個地方需要注意:

矩形 x,y的值

x,y是矩形的左上角相對於圓形的位置,設置x,y的值將矩形移至圓的中心,這樣才能確保線的端點指向矩形的中心。

計算公式是 x=y = r  - 矩形寬度 / 2

確保矩形小於圓的內切正方形

打個比方,如果你透過一個圓形孔看一個較小的紅色正方形,你會看到一個個完整的正方形,但如果正方形過大,你可能就只能看到一個紅色的圓形了。

所以正方形寬度推導公式就是 width < 2*r / √2

<svg width="1000" height="1000">
  <defs>
     <pattern id="person" patternUnits="objectBoundingBox" width="1" height="1">
         <rect x="10" y="10" width="20" height="20" fill="#7FBBA1" stroke="#5CA083"/>
       </pattern>
    </defs>
 </svg>

確保圓是透明的

通俗點講就是用於填充矩形的圓的樣式中不能在設置fill和stroke了

 然后在繪制圓形的地方引用

nodeEnter
  .append('circle')
    .attr('class', (data) => {
       return (data.hide && '.hide') || (data.cateType === 0 && 'mainCompany') || (data.cateType === 1 && 'relativeCompany') || (data.cateType === 2 && 'shape-relativePerson');
    })
    .attr('r', 20)
    .attr('fill', (data)=>{
        return data.cateType === 2 ? 'url(#person)' : '';
     })

下面是結果圖

 

當然你可以擴展,例如如果是人的話繪制一個人的圖片

<pattern id="person" patternUnits="objectBoundingBox" width="1" height="1">
  <image href={user} width="20" height="20" x="10" y="10"/>
</pattern>

效果如圖

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM