2016-11-14 71 views
1

我用这个例子:https://codepen.io/AndrewGHC/pen/mPXjKr如何限制D3 Force中元素的移动?

我有2个问题:

1)我怎样才能限制拖和下降?例如,我想限制运动到围绕圆圈50px。它不应该离开圆圈的起始位置超过50px。

2)刷新页面时,会看到不同位置的元素。 一个圆圈可能是正确的,下一次是左边。这怎么可以避免?我想我需要设置首发位置。

// 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>

回答

0

这里是你如何限制你拖动:

首先,设置你的拖动功能:

var drag = force.drag() 
    .on("dragstart", dragstarted) 
    .on("drag", dragged); 

然后,在dragstarted,获取当前xy职位:

function dragstarted(d) { 
    currentX = d.x; 
    currentY = d.y; 
} 

最后,在dragged功能,设置您的规则:

function dragged(d) { 
    d.px = (d.px > currentX + 50) ? currentX + 50 : d.px; 
    d.py = (d.py > currentY + 50) ? currentY + 50 : d.py; 
    d.px = (d.px < currentX - 50) ? currentX - 50 : d.px; 
    d.py = (d.py < currentY - 50) ? currentY - 50 : d.py; 
} 

这里是笔:https://codepen.io/anon/pen/eBzyYd?editors=0010

PS:这里在SO是一个很好的做法在时间1个处理问题,每个问题有一个问题。所以,我建议你将第二个问题作为一个单独的问题发布。

+0

谢谢!有用。 – user3896538

+0

你能帮我解决第二个问题吗? http://stackoverflow.com/questions/40616162/how-can-i-add-starting-positions-for-d3-force – user3896538