Reputation: 142
i am kinda new in using d3 as visualization. Here is my program to visualize document cluster.
the titles of the documents are so long that the screen cannot show the whole titles, so i want to make the title label of each members wrapped.
here is the d3 code:
var CodeFlower = function(selector, w, h) {
this.w = w;
this.h = h;
d3.select(selector).selectAll("svg").remove();
this.svg = d3.select(selector).append("svg:svg")
.attr('width', w)
.attr('height', h);
this.svg.append("svg:rect")
.style("stroke", "#6BB6CF")
.style("fill", "#6BB6CF")
.attr('width', "615px")
.attr('height', h);
this.force = d3.layout.force()
.on("tick", this.tick.bind(this))
.charge(function(d) { return d._children ? -d.size / 100 : -40; })
.linkDistance(function(d) { return d.target._children ? 80 : 25; })
.size([h, w]);
};
CodeFlower.prototype.update = function(json) {
if (json) this.json = json;
this.json.fixed = true;
this.json.x = this.w / 2;
this.json.y = this.h / 2;
var nodes = this.flatten(this.json);
var links = d3.layout.tree().links(nodes);
var total = nodes.length || 1;
// remove existing text (will readd it afterwards to be sure it's on top)
this.svg.selectAll("text").remove();
// Restart the force layout
this.force
.gravity(Math.atan(total / 50) / Math.PI * 0.4)
.nodes(nodes)
.links(links)
.start();
// Update the links
this.link = this.svg.selectAll("line.link")
.data(links, function(d) { return d.target.name; });
// Enter any new links
this.link.enter().insert("svg:line", ".node")
.attr("class", "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; });
// Exit any old links.
this.link.exit().remove();
// Update the nodes
this.node = this.svg.selectAll("circle.node")
.data(nodes, function(d) { return d.name; })
.classed("collapsed", function(d) { return d._children ? 1 : 0; });
this.node.transition()
.attr("r", function(d) { return d.children ? 3.5 : Math.pow(d.size, 2/5) || 1; });
// Enter any new nodes
this.node.enter().append('svg:circle')
.attr("class", "node")
.classed('directory', function(d) { return (d._children || d.children) ? 1 : 0; })
.attr("r", function(d) { return d.children ? 3.5 : Math.pow(d.size, 2/5) || 1; })
.style("fill", function color(d) {
return "hsl(" + parseInt(360 / total * d.id, 10) + ",90%,70%)";
})
.call(this.force.drag)
.on("click", this.click.bind(this))
.on("mouseover", this.mouseover.bind(this))
.on("mouseout", this.mouseout.bind(this));
// Exit any old nodes
this.node.exit().remove();
this.text = this.svg.append('svg:text')
.attr('class', 'nodetext')
.attr('dy', 0)
.attr('dx', 0)
.attr('text-anchor', 'middle');
return this;
};
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
function type(d) {
d.value = +d.value;
return d;
}
CodeFlower.prototype.flatten = function(root) {
var nodes = [], i = 0;
function recurse(node) {
if (node.children) {
node.size = node.children.reduce(function(p, v) {
return p + recurse(v);
}, 0);
}
if (!node.id) node.id = ++i;
nodes.push(node);
return node.size;
}
root.size = recurse(root);
return nodes;
};
CodeFlower.prototype.click = function(d) {
// Toggle children on click.
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
this.update();
};
CodeFlower.prototype.mouseover = function(d) {
this.text.attr('transform', 'translate(' + d.x + ',' + (d.y - 5 - (d.children ? 3.5 : Math.sqrt(d.size) / 2)) + ')')
.text(d.name)
.style('display', null);
};
CodeFlower.prototype.mouseout = function(d) {
this.text.style('display', 'none');
};
CodeFlower.prototype.tick = function() {
var h = this.h;
var w = this.w;
this.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; });
this.node.attr("transform", function(d) {
return "translate(" + Math.max(5, Math.min(w - 5, d.x)) + "," + Math.max(5, Math.min(h - 5, d.y)) + ")";
});
};
CodeFlower.prototype.cleanup = function() {
this.update([]);
this.force.stop();
};
i have tried wrapping text function in some sites but it doesn't work. Any help would be appreciated, many thanks :)
Upvotes: 1
Views: 3512
Reputation: 15992
You can use html elements inside SVG to take advantage of automatic line wrapping, provided you set a width and height.
https://jsfiddle.net/guanzo/evs6xsw3/1/
First you need to append a <foreignObject>
tag into the SVG. Then you can start appending html elements into the <foreignObject>
, while specifying the html namespace .append("xhtml:div")
Upvotes: 0
Reputation: 102174
If you want to use Mike Bostock's wrap
function, you have to call it:
var foo = svg.append("text")
.attr("y", someValue)
.attr("dy", 0)
.text("foo bar baz")
.call(wrap, textWidth);//some value here
You can set the y position directly, and the x position using a "transform" (unless you change the "x" attribute of the <tspan>
inside the wrap
function).
Here is a demo:
var svg = d3.select("svg");
var text = svg.append("text")
.attr("y", 20)
.attr("dy", 0)
.attr("transform", "translate(10,0)")
.text("this pure SVG text element is a very long long long long long long long long long long long long long long long long long long long long long long long long text indeed.")
.call(wrap, 280);
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg style="background-color:wheat"></svg>
Upvotes: 3