d3-force: при обновлении позиции узлы двигаются медленнее - PullRequest
0 голосов
/ 06 сентября 2018

Я использую форс-макет d3 и хотел бы переместить узлы в триггере событий.

Я создал минимальный блок здесь , показывающий желаемую функциональность.

Как видите, при нажатии кнопки узлы быстро перемещаются в новое положение. Я хотел бы замедлить это. Я играл с комбинациями (как мне казалось,) соответствующих атрибутов, указанных в документации (например, alpha, alphaDecay, velocityDecay), но безрезультатно.

Напомним: как заставить узлы двигаться медленнее при обновлении позиции?

Спасибо!

1 Ответ

0 голосов
/ 06 сентября 2018

Вы, вероятно, хотите использовать высокоскоростной распад. Значение 0,9 замедляет тики до 0,1 от их скорости, «после применения каких-либо сил во время тика скорость каждого узла умножается на 1 - [скорость] затухание. ( docs )». Это, безусловно, замедлит движение. Значение 0,9, вероятно, является избыточным в большинстве ситуаций.

Однако в связи с этим нам необходимо убедиться, что альфа-распад низкий: если альфа-распад слишком высокий, симуляция будет охлаждаться до того, как узлы достигнут своей конечной точки. Я использовал 0,0005 в этом примере ниже,

Наконец, чтобы решить проблему скачков симуляции при повторном центрировании, я уменьшил альфа, чтобы уменьшить появление дрожания. Конечно, чем ниже вы перемещаете альфа, тем меньше должен быть альфа-декай, чтобы поддерживать период охлаждения симуляции той же продолжительности

Используется ниже:

  • alhpaDecay: 0,005,
  • speedDecay: 0,6,
  • перезапустить альфа: 0,1

const width = 500
    const height = 500
    const svg = d3.select("svg")

    const trtCenter = width / 5
	const cntrlCenter = width / 1.5
    
    let sampleData = d3.range(24).map((d,i) => ({r: 40 - i * 0.5}))
    
    // define force
    let force = d3.forceSimulation()
    	.force('charge', d3.forceManyBody().strength(1))
    	force.force('x', d3.forceX().strength(.3).x( width / 2))
		force.force('y', d3.forceY().strength(.3).y(height / 3.5))
    	.force('collision', d3.forceCollide(d => 12))    	
    	.nodes(sampleData)
    	.on('tick', changeNetwork)

    let dots = svg.selectAll('.dot')
    	.data(sampleData)
    	.enter()
    	.append('g')
    	.attr('class', 'dot')
    	.attr('group', (d,i) => i % 2 == 0 ? 'trt' : 'ctrl')
    	.append('circle')
    	.attr('r', 10)
    	.attr('fill', (d,i) => i % 2 == 0 ? 'pink' : 'olive')
    	.attr('stroke', 'black')
    	.attr('stroke-width', .4)

    function nodeTreatmentPos(d) {
	  return d.index % 2 == 0 ? trtCenter : cntrlCenter;
	}


   	function changeNetwork() {
      d3.selectAll('g.dot')
      	.attr('transform', d=> `translate(${d.x}, ${d.y})`)
    }
  
    // 
	function moveNodes() {
	  force.force('center', null)
		.force('collision', d3.forceCollide(d => 12))
		.alphaDecay(.0005)
		.velocityDecay(0.6)
		force.force('x', d3.forceX().strength(1).x(nodeTreatmentPos))
		force.force('y', d3.forceY().strength(1).y(height / 3.5))
        force.alpha(.1).restart();
	}

	// force for center
	function moveCenter() {
	force//.force('center', null)
		.force('collision', d3.forceCollide(d => 12))
		.alphaDecay(.0005)
		.velocityDecay(0.6)
		force.force('x', d3.forceX().strength(1).x( width / 2))
		force.force('y', d3.forceY().strength(1).y(height / 3.5))
		force.alpha(.1).restart();
		}

	// resolve locations of node on cliks
	let toCenter = true;

	d3.select('#clickMe')
		.on('click', function() {
			toCenter === true ? moveNodes() : moveCenter()
			toCenter = !toCenter
		})
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js'></script>
<button id="clickMe" type="button">Move Nodes</button>
<svg id="svg" width="1200" height="500"></svg>
...