簡體   English   中英

從范圍滑塊的用戶輸入重新啟動d3模擬

[英]Restart d3 simulation on user input from range slider

我正在使用d3力布局來構建“彈簧”。 我想通過用戶輸入來操縱它的屬性,例如“強度”和“距離”。 為此,我目前正在使用“輸入范圍滑塊”。 為了更好地理解,我在Codepen上設置了一個工作草案,該問題與以下問題有關: http ://codepen.io/bitHugger/pen/XNqGNE?editors=1010

HTML:

<input id="strengthElem" step="0.1" type="range" min="0" max="2"/>

我想做這樣的事件處理:

let strengthElem = window.document.getElementById('strengthElem');
let strength;

strengthElem.addEventListener('click', function(evt) {
  strength = strengthElem.value;
  console.log('strength', strength);
}, false);

現在,當范圍滑塊發生某些交互時,我想重新啟動或重新計算d3.simulation對象。 這是我當前的模擬:

let simulation = d3.forceSimulation().nodes(nodes)
    .force("link", d3.forceLink()
        .id(function(d) { return d.index; })
        .strength(function(d) { return 2; })
        .distance(function(d) { return 2; }))
    .force("charge", d3.forceManyBody());

對於強度和距離,當前值是硬編碼的。我想將其更改為:

.strength(function(d) { return strength; })
.distance(function(d) { return distance; })

我嘗試設置d3.call()。on()函數,但無法正常工作。 我想知道如何才能基於unser輸入來操縱模擬,這種輸入發生在force()函數外部/ svg容器外部。

可悲的是,我無法正常工作,也不知道如何設置適當的d3事件偵聽器,該偵聽器會對輸入按鈕做出反應,然后根據更改后的值重新計算力的布局。 有任何想法嗎?

而不是在不保持引用力的情況下就地創建鏈接力,您應該首先創建力,然后將引用傳遞給仿真。 這樣,您以后便可以根據滑塊的值來操縱力:

// Create as before, but keep a reference for later manipulations.
let linkForce = d3.forceLink()
  .id(function(d) { return d.index; })
  .strength(2)
  .distance(2);

let simulation = d3.forceSimulation().nodes(nodes)
  .force("link", linkForce)
  .force("charge", d3.forceManyBody());

在滑塊上注冊事件處理程序時,您可能還希望使用d3.select()以便於使用,並使用selection.on()分配功能。

d3.select('#strengthElem')
  .on('click', function() {
    // Set the slider's value. This will re-initialize the force's strenghts.
    linkForce.strength(this.value);   
    simulation.alpha(0.5).restart();  // Re-heat the simulation
  }, false);

d3.select('#distanceElem')
  .on('click', function(evt) {
    // Set the slider's value. This will re-initialize the force's strenghts
    linkForce.distance(this.value);
    simulation.alpha(0.5).restart();  // Re-heat the simulation
  }, false);

在處理函數中, this指向實際的DOM元素,從而可以輕松訪問滑塊的值。 現在可以使用以前保留的參考來更新連桿力的參數。 剩下要做的就是重新加熱模擬以繼續其計算。

請查看此片段以進行演示:

 'use strict'; var route = [[30, 30],[192, 172],[194, 170],[197, 167],[199, 164],[199, 161],[199, 157],[199, 154],[199, 150],[199, 147],[199, 143],[199, 140],[200, 137],[202, 134],[204, 132],[207, 129],[207, 126],[200, 200]]; let distance = 1; let createNode = function(id, coords) { return { radius: 4, x: coords[0], y: coords[1], }; }; let getNodes = (route) => { let d = []; let i = 0; route.forEach(function(coord) { if(i === 0 || i === route.length-1) { d.push(createNode(i, coord)); d[i].fx = coord[0]; d[i].fy = coord[1]; } else { d.push(createNode(i, coord)); } ++i; }); return d; }; let getLinks = (nodes) => { let next = 1; let prev = 0; let obj = []; while(next < nodes.length) { obj.push({source: prev, target: next, value: 1}); prev = next; ++next; } return obj; }; let force = function(route) { let width = 900; let height = 700; let nodes = getNodes(route); let links = getLinks(nodes); d3.select('#strengthElem') .on('click', function() { linkForce.strength(this.value); // Set the slider's value. This will re-initialize the force's strenghts simulation.alpha(0.5).restart(); // Re-heat the simulation }, false); d3.select('#distanceElem') .on('click', function(evt) { linkForce.distance(this.value); // Set the slider's value. This will re-initialize the force's strenghts simulation.alpha(0.5).restart(); // Re-heat the simulation }, false); let linkForce = d3.forceLink() .id(function(d) { return d.index; }) .strength(2) .distance(2); let simulation = d3.forceSimulation().nodes(nodes) .force("link", linkForce) .force("charge", d3.forceManyBody()); let svg = d3.select('svg').append('svg') .attr('width', width) .attr('height', height); let link = svg.append("g") .attr('class', 'link') .selectAll('.link') .data(links) .enter().append('line') .attr("stroke-width", 1); let node = svg.append("g") .attr("class", "nodes") .selectAll("circle") .data(nodes) .enter().append("circle") .attr("r", function(d) { return d.radius; }) .attr("fill", function(d) { return '#fabfab'; }); simulation.nodes(nodes).on("tick", ticked); simulation.force("link").links(links); function ticked() { link .attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node .attr("cx", function(d) { return dx; }) .attr("cy", function(d) { return dy; }); } }; force(route); 
 .link { stroke: #777; stroke-width: 2px; } .links line { stroke: #999; stroke-opacity: 0.6; } .nodes circle { stroke: #fff; stroke-width: 1.5px; } 
 <script src="https://d3js.org/d3.v4.js"></script> <div>Strength <input id="strengthElem" step="0.1" type="range" min="0" max="2"/></div> <div>Distance <input id="distanceElem" step="1" type="range" min="0" max="50"/></div> <svg style="width: 900; height: 700;"></svg> 

我還相應地更新了Codepen

一種方法是刪除svg的內容,並使用所需的常量重新繪制它。

我不明白您的問題所在,因為我只更改了幾行,就像您在問題中說的那樣。

在您的點擊處理程序中,我清除了svg的內容並稱為“繪制”函數:

strengthElem.addEventListener('click', function(evt) {
  strength = strengthElem.value;
  console.log('strength', strength);
  d3.select('svg').selectAll("*").remove();
  force(route);
}, false);

將您的配置變量移至全局范圍:

var distance = 1;
let distElem = window.document.getElementById('distanceElem');
let strengthElem = window.document.getElementById('strengthElem');
var strength = strengthElem.value;
distance = distElem.value;

就像您說的那樣,我已更改為返回參數:

 .strength(function(d) { return strength; })
 .distance(function(d) { return distance; }))

完整示例: http : //codepen.io/anon/pen/ObZYLo?editors=1010

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM