我一直在尝试使用D3.js创建一个力导向布局。在下面的代码中,我填充了一系列对象,这些对象稍后会渲染为文本元素。我希望避免单词之间的冲突,并相应地调整它们在网格上的表示。
我在这里使用了Eric Dobbs编写的代码http://bl.ocks.org/dobbs/1d353282475013f5c156 但它仍然不适合我。物体最终在整个屏幕上飞舞。我花了很多时间对此感到困惑,我非常感谢任何可用的帮助。
这是我的代码
<!DOCTYPE html>
<html>
<head>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<script>
//arguments passed in when this class is modularised
var metaDataObject = {"type":"wordcloud","label":"data1","data":"data2","color":"color"};
var dataObject = {"data1":["apple","orange","pear","grapes","mango","papaya","kiwi","banana", "watermelon","strawberry","honeydew","dragonfruit","durian","pineapple","jackfruit","lychee","mangosteen","passionfruit","raspberry","blueberry","rockmelon","coconut","lemon","lime","pomelo","rambutan","aguave","longan","mandarin","calamansi","sugarcane","avocado","bittergourd","wintermelon","dates"],"data2":[100,50,150,40,70,60,30,35,95,120,60,70,80,15,30,140,100,170,200,40,90,20,180,99,66,55,130,20,50,55,100,120,30,20,90],"color":["#23af50"]};
//transform raw data
var frequency_list = transformData(metaDataObject, dataObject);
frequency_list.sort(function(a, b) {
return b.size-a.size;
});
//set cloud container variables
var cloudWidth = 600;
var cloudHeight = 400;
var cloudContainer = d3.select("body").append("svg")
.attr("width", cloudWidth)
.attr("height", cloudHeight);
//set approximate scaling
var largestQty = d3.max(dataObject[metaDataObject.data]);
var rangeCap = cloudWidth*cloudHeight/7500;
var scale = d3.scale.linear()
.domain([0, largestQty])
.range([5, rangeCap]);
var color = d3.scale.linear()
.domain([0,1,2,3,4,5,6,10,15,20,30,largestQty])
.range(["#ddd", "#ccc", "#bbb", "#aaa", "#999", "#888", "#777", "#666", "#555", "#444", "#333", "#222"]);
var words = createText(frequency_list);
var rendered = cloudContainer.selectAll("node")
.data(words)
.enter()
.append("text")
.attr("id", function(d, i) {
return "t"+i;
})
.text(function(d) {
return d.text;
})
.attr("font-family", "sans-serif")
.attr("fill", function(d, i) {
return color(i);
})
.attr("font-size", function(d) {
return scale(d.size)+"px";
})
.on("mouseover", handleMouseOver)
.on("mouseout", handleMouseOut);
var force = d3.layout.force()
.nodes(words)
.size([cloudWidth, cloudHeight])
.charge(-50)
.gravity(0.1)
.on("tick", tick)
.start();
rendered.call(force.drag);
function tick(e) {
var q = d3.geom.quadtree(words);
for(var i=0; i<words.length; i++) {
var word = words[i];
q.visit(collide(word));
}
// console.log(d.x2);
rendered
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
}
function collide(node) {
var nx1, nx2, ny1, ny2, padding;
padding = 32;
nx1 = node.x - padding;
nx2 = node.x2 + padding;
ny1 = node.y - padding;
ny2 = node.y2 + padding;
return function(quad, x1, y1, x2, y2) {
var dx, dy;
console.log(node.x2);
if (quad.point && (quad.point !== node)) {
if (overlap(node, quad.point)) {
dx = Math.min(node.x2 - quad.point.x, quad.point.x2 - node.x) / 2;
node.x -= dx;
quad.point.x += dx;
dy = Math.min(node.y2 - quad.point.y, quad.point.y2 - node.y) / 2;
node.y -= dy;
quad.point.y += dy;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
};
};
function overlap(a, b) {
return !(a.x2 < b.x ||
a.x > b.x2 ||
a.y2 < b.y ||
a.y > b.y2);
}
function createText(frequency_list) {
var words = [];
for(var wordIndex=0; wordIndex<frequency_list.length; wordIndex++) {
var word = {
x: Math.random() * (cloudHeight - 40) +20,
y: Math.random() * (cloudWidth -40) +20,
text: frequency_list[wordIndex].text,
size: frequency_list[wordIndex].size,
// x2: word.x + word.text.length * word.size /1.5,
// y2: word.y + scale(word.size) * 1.1
};
word.x2 = word.x + word.text.length * word.size /1.5;
word.y2 = word.y + scale(word.size) * 1.1;
words.push(word);
}
return words;
}
function transformData(metaDataObject, dataObject) {
//To transform data to this format:
// var frequency_list = [{"text":"apple","size":100}, {"text":"orange","size":100}, {"text":"pear","size":25}, {"text":"grapes","size":301}, {"text":"mango","size":56}];
var frequency_list = [];
var wordFieldName = metaDataObject.label;
var valuesFieldName = metaDataObject.data;
var wordList = dataObject[wordFieldName];
var valuesList = dataObject[valuesFieldName];
for(var itemIndex=0; itemIndex<wordList.length; itemIndex++) {
var item = {
text: wordList[itemIndex].toUpperCase(),
size: valuesList[itemIndex]
}
frequency_list.push(item);
}
return frequency_list;
}
function handleMouseOver(d, i) {
d3.select(this).transition().attr({
fill: "black",
"font-size": scale(d.size) + 5 + "px"
});
cloudContainer.append("text").attr({
id: "t" + d.text + "-" + d.size,
x: 10,
y:20
})
.text(function() {
return ["weight: " + d.size];
// return [""+occupiedSpaces[1].top];
})
}
function handleMouseOut(d, i) {
d3.select(this).transition().attr({
fill: color(i),
"font-size": scale(d.size) + "px"
});
d3.select("#t" + d.text + "-" + d.size).remove();
}
</script>
</body>
</html>