如何添加D3 Force的起始位置? [英] How can I add starting positions for D3 Force?

查看:88
本文介绍了如何添加D3 Force的起始位置?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用以下示例: https://codepen.io/AndrewGHC/pen/mPXjKr

刷新页面时,您会在不同位置看到元素.一个圆圈可能在右边,而下一次则在左边.如何避免这种情况?我认为我需要设置起始位置.

When you refresh the page, you see elements in different places. One circle may be right, and the next time the left. How can this be avoided? I think I need to set the starting positions.

// Request data, generate bg with Trianglify & set up loading function.

function slowPrint(tgt, i, msg, spd) {
  if (i < msg.length) {
    $(tgt).append(msg[i]);
    i++;
    var writeTimer = setTimeout(function() {
      slowPrint(tgt, i, msg, spd)
    }, spd);
  }
}

function writeThis(tgt, msg, spd) {
  if ($(tgt).html() === msg) {
    return;
  }
  $(tgt).html('');
  slowPrint(tgt, 0, msg, spd);
}

writeThis('#info', 'Loading . . .', 100);

var url = "https://raw.githubusercontent.com/AndrewGHC/kevin-bacon-number/master/kevinBacon.json";

d3.json(url, drawGraph);

// Credit to Trianglify @ https://github.com/qrohlf/trianglify

var pattern = Trianglify({
  height: $(document).height(),
  width: $(document).width(),
  cell_size: 40
});

document.body.style.backgroundImage = "url(" + pattern.png() + ")";

// Create the drawGraph callback

function drawGraph(err, data) {
  if (err) throw err;

  var width = $('#graph').width(),
    height = $('#graph').height();

  // Prepare the data for the force graph, beinning by creating an array of movies (strings)

  var movies = [];
  (function() {
    data.actors.forEach(function(actor) {
      actor.movies.forEach(function(movie) {
        if (movies.indexOf(movie) === -1) {
          movies.push(movie);
        }
      });
    });
  }())

  // Create the links array for the force graph, mapping actors to movies. This will draw a line between the two.

  var links = [];
  (function() {
    data.actors.forEach(function(actor, actorIndex) {
      actor.movies.forEach(function(movie, movieIndex) {
        links.push({
          "source": actorIndex,
          "target": data.actors.length + movies.indexOf(movie)
        });
      });
    });
  }())

  // Now prepare the nodes array, concatenating data.actors and the movies array. The order here is important, and movie indices must be converted into objects.

  var nodes = data.actors;
  movies.forEach(function(movie) {
    nodes.push({
      "movie": movie
    });
  });

  // Create the SVG canvas & force layout

  var canvas = d3.select('#graph')
    .append('svg')
    .attr("height", height)
    .attr("width", width);

  var force = d3.layout.force()
    .size([width, height])
    .nodes(nodes)
    .links(links)
    .linkDistance(50)
    .charge(function(d) {
      if (d.name === "Kevin Bacon") {
        return -1000;
      } else if (d.name) {
        return -(d.weight) * 50;
      }
      return -((d.weight * 50) * 5);
    })
    .gravity(0.1)
    .start();

  // Helper function to remove whitespace, later used for assigning IDs

  function rmWs(string) {
    if (typeof string !== 'string') {
      return false;
    }
    string = string.split(' ').join('');
    return string;
  }

  // Create the links

  var link = canvas.selectAll('.link')
    .data(links)
    .enter().append('line')
    .attr('class', 'link');

  // Create a colour scale for movie nodes. Find the min and max no. of links for the range of the colour domain.

  var arrMax = [];
  links.forEach(function(link) {
    arrMax.push(link.target.weight);
  });

  var colour = d3.scale.linear()
    .domain([1, d3.max(arrMax)])
    .range(["white", "black"])
    .interpolate(d3.interpolateHcl);

  // Set up the pop up on mouse hover



  // Call circles on SVG chart, with colours along a white - black gradient generated based on the max weight & variable sizing. Then place text on these movie elements.

  var circleRadius = 17;

  var circles = canvas.selectAll('.movies')
    .data(nodes)
    .enter()
    .append('circle')
    .attr('r', function(d, i) {
      if (d.name) {
        return circleRadius;
      }
      return circleRadius + (d.weight * 2);
    })
    .attr('stroke', '#777')
    .attr('stroke-width', '2px')
    .attr('fill', function(d, i) {
      return colour(d.weight) || 'black';
    })
    .call(force.drag)

  var text = canvas.selectAll('.moviesText')
    .data(nodes)
    .enter()
    .append('text')
    .attr('text-anchor', 'middle')
    .text(function(d) {
      return d.movie;
    });

  // Set up clip path for each forthcoming image node to clip rectangular images to circles. Then call images on the canvas.

  var clip = canvas.selectAll('clipPath')
    .data(nodes)
    .enter()
    .append('clipPath')
    .attr('id', function(d) {
      return rmWs(d.name) || rmWs(d.movie)
    })
    .append('circle')
    .attr('r', circleRadius);

  var imgWidth = 50,
    imgHeight = 50;

  var node = canvas.selectAll('.node')
    .data(nodes)
    .enter()
    .append('image')
    .attr('xlink:href', function(d) {
      return d.thumbnail;
    })
    .attr("class", "image")
    .attr("width", imgWidth)
    .attr("height", imgHeight)
    .attr("clip-path", function(d) {
      return "url(#" + (rmWs(d.name) || rmWs(d.movie)) + ")"
    })
    .call(force.drag);

  // Handle operations on each tick.

  force.on("tick", function() {

    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("x", function(d) {
        return d.x - (imgWidth / 2);
      })
      .attr("y", function(d) {
        return d.y - (imgHeight / 2);
      });

    clip.attr('cx', function(d) {
        return d.x;
      })
      .attr('cy', function(d) {
        return d.y;
      })

    circles.attr('cx', function(d) {
        return d.x;
      })
      .attr('cy', function(d) {
        return d.y;
      })

    text.attr('x', function(d) {
        return d.x;
      })
      .attr('y', function(d) {
        return d.y - 30;
      })
  });

  // When all initial calculations are done, print title to replace 'Loading . . .'

  force.on('end', function() {
    writeThis('#info', 'D3 Force Graph - Distance from Kevin Bacon', 100);
  })
}

@import url(https://fonts.googleapis.com/css?family=Questrial' rel='stylesheet' type='text/css);
 #header {
  text-align: center;
  font-family: 'Jockey One', sans-serif;
}
#graph {
  margin: 15px auto;
  background: white;
  height: 750px;
  width: 750px;
  -webkit-box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, 0.75);
  -moz-box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, 0.75);
  box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, 0.75);
}
.link {
  stroke: #777;
  stroke-width: 2px;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.1/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="header">
  <h2 id="info"></h2>
</div>
<div id="graph"></div>

推荐答案

我无法完全理解您的问题,但是您似乎希望将每个位置的某些位置固定为每次启动代码时都相同的位置. 一种方法是将数据上的值指定为属性(可能是某些JSON对象?),或使用CSS或javascript对其进行操作.显然,他们随机获得了x和y值.

I didn't understand your problem exactly, but it looks like you want fixed some locations to each circle that will be the same every time you launch the code. One approach is specify the value on data as a attribute (some JSON object maybe?) or manipulate it with CSS or javascript. Apparently they are getting the x and y values at random.

这篇关于如何添加D3 Force的起始位置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆