2017-01-28 7 views
2

Я использую компоновку силы в D3.js v4. Теперь я хочу щелкнуть узел и использовать физику силы как collide только для одного узла.Использование физики силового макета для разделенных элементов

Моделирование для каждого узла на всей SVG это:

var simulation = d3.forceSimulation() 
    .force("link", d3.forceLink().id(function(d) { 
     return d.index 
    })) 
    .force("collide", d3.forceCollide(function(d) { 
     return d.r + 8 
    }).iterations(16)) 
    .force("charge", d3.forceManyBody()) 
    .force("center", d3.forceCenter(chartWidth/2, chartWidth/2)) 

Теперь я хочу, чтобы изменить поведение collide для одного узла я нажал на толкать другие узлы прочь или привлечь их.

Есть ли кто-нибудь, кто знает решение? Я немного поработал с функцией фильтрации и остановил/перезапустил макет, но это не сработало.

ответ

1

Вы сказали: «Я хочу изменить поведение коллизии для одного узла, на который я нажал, чтобы оттолкнуть другие узлы или привлечь их».

Если вы хотите, чтобы притягивал других узлов, используя d3.forceCollide, вы используете неправильный инструмент для выполнения этой задачи.

Согласно API:

силы удара обрабатывает узлы как окружности с заданным радиусом, а не точками, а также предотвращает узлы от перекрытия.

Таким образом, collide в основном отталкивает другие узлы, чтобы избежать перекрытия.

Это, как говорится, это решение имеет дело только с первой частью вашей проблемы: «Я хочу, чтобы изменить поведение Collide для одного узла я нажал на толчок других узлами прочь».

Существуют разные способы для этого. В моем решении, если щелкнуть узел, он изменяет r свойства в связанных данных:

d3.select(this).datum().r = 20; 

без перерисовки фактического SVG круга. Это оттесняет другие узлы, поддерживая щелчок узла с одинаковым размером.

Вот демо (нажмите на узлах):

var svg = d3.select("svg"); 
 

 
var colour = d3.scaleOrdinal(d3.schemeCategory10); 
 

 
var data = d3.range(30).map(d => ({ 
 
    r: 6 
 
})); 
 

 
var simulation = d3.forceSimulation(data) 
 
    .force("x", d3.forceX(150).strength(0.05)) 
 
    .force("y", d3.forceY(75).strength(0.05)) 
 
    .force("collide", d3.forceCollide(function(d) { 
 
     return d.r + 1; 
 
    })); 
 

 
var node = svg.selectAll(".circles") 
 
    .data(data) 
 
    .enter() 
 
    .append("circle") 
 
    .attr("r", d => d.r) 
 
    .attr("fill", (d, i) => colour(i)); 
 

 
node.on("click", function(d) { 
 
    d3.selectAll("circle").data().forEach(d => d.r = 6); 
 
    d3.select(this).datum().r = 20; 
 
    simulation.nodes(data); 
 
    simulation.alpha(0.8).restart(); 
 
}) 
 

 
simulation.nodes(data) 
 
    .on("tick", d => { 
 
     node.attr("cx", d => d.x).attr("cy", d => d.y); 
 
    });
<script src="https://d3js.org/d3.v4.min.js"></script> 
 
<svg></svg>

+0

Благодаря Херардо, могу ли я задать вопрос? Я не очень хорошо понял ваш код. Эта строка 'var data = d3.range (30) .map (d => ({ r: 6 ..." немного смущает меня, потому что я думал, что 'map()' используется для использования функции на существующий массив и получает новый массив с этой используемой функцией? В этом случае я не вижу, что такое старый массив? –

+1

Я сделал это только для того, чтобы быстрее создать 20 объектов в массиве. Старый массив - '[ 0, 1, 2, 3 ..., 20] ', сделанные' d3.range (20) '. –

+1

@Derick Я должен быть честен с вами: когда мы тратим некоторое время на написание кода для ответа, а просто просто говорит «спасибо», это немного раздражает. Я, как правило, мысленно черный список этих людей и игнорирую их будущие вопросы. Не будьте такими ... Вам не нужно принимать ответ, но выбивать его - это простой и дешевый токен признательность. –