Ваша функция wrap
в настоящее время разделяется на пробелы (/\s+/
) и оборачивает эти части в свои собственные элементы .
Чтобы иметь возможность переносить слова, она должна быть умнее.
Простое решение - обернуть каждый символ в свой .
var margin = {
top: 10,
right: 0,
bottom: 58,
left: 40
};
var width = 400 - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;
var barWidth = 40;
var graph;
var xScale;
var yScale;
var dataSet;
dataSet = [{
desc: '即使句子没有空格',
val: 20
},
{
desc: 'Sample text.即使句子没有空格',
val: 40
},
{
desc: 'test3',
val: 60
},
{
desc: 'test4',
val: 80
},
{
desc: 'some dummy text here',
val: 120
}
];
xScale = d3.scaleBand()
.domain(dataSet.map(function(d) {
return d.desc;
}))
.range([0, width]);
yScale = d3.scaleLinear()
.range([height, 0])
.domain([0, 1.15 * d3.max(dataSet, function(d) {
return d.val;
})]);
graph = d3.select("#graph")
.append("svg")
.attr("class", "bar-chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
graph.append("g")
.attr("class", "x-scale")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale))
.selectAll(".tick text")
.call(wrap, xScale.bandwidth());
graph.append("g")
.attr("class", "y-scale")
.call(d3.axisLeft(yScale).tickPadding(10));
graph
.append("g")
.attr('class', 'graph-placeholder')
.selectAll("rect")
.data(dataSet)
.enter()
.append("rect")
.attr("class", "bar1")
.attr("height", height)
.attr("width", barWidth)
.attr('x', d => xScale(d.desc) + (xScale.bandwidth() - barWidth) / 2);
graph
.append("g")
.attr('class', 'graph-main')
.selectAll("bar1")
.data(dataSet)
.enter()
.append("rect")
.attr("class", "bar2")
.attr('x', d => xScale(d.desc) + (xScale.bandwidth() - barWidth) / 2)
.attr("y", function(d) {
return yScale(d.val);
})
.attr("height", function(d) {
return height - yScale(d.val);
})
.attr("width", barWidth);
graph
.append("g")
.attr('class', 'bar-label')
.selectAll("text")
.data(dataSet)
.enter()
.append("text")
.text(d => d.val + '%')
.attr("y", function(d) {
return yScale(d.val) - 5;
}).attr('x', function(d) {
return xScale(d.desc) + ((xScale.bandwidth() - barWidth) / 2);
});
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
// split on each character
words = text.text().split('').reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1,
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);
// join with empty string
tspan.text(line.join(""));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
// join with empty string
tspan.text(line.join(""));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
.bar-chart {
background-color: #ccc;
}
.bar2 {
fill: steelblue;
}
.bar1 {
fill: #f2f2f2;
}
text {
font-size: 12px;
/* text-anchor: middle; */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="container">
<div id="graph"></div>
</div>
Но при этом мы имеем то же поведение, что и word-break: break-all
, то есть оно будет нарушать даже тексты не CJK.
Чтомы хотим это поведение word-break: normal
.И для этого лучше всего использовать HTML и CSS.
Создав фиктивный элемент HTML, мы можем проверить, где должен отображаться каждый символ, благодаря Range.getBoundingClientRect метод:
var margin = {
top: 10,
right: 0,
bottom: 58,
left: 40
};
var width = 400 - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;
var barWidth = 40;
var graph;
var xScale;
var yScale;
var dataSet;
dataSet = [{
desc: '即使句子没有空格',
val: 20
},
{
desc: 'Sample text.即使句子没有空格',
val: 40
},
{
desc: 'test3',
val: 60
},
{
desc: 'test4',
val: 80
},
{
desc: 'some dummy text here',
val: 120
}
];
xScale = d3.scaleBand()
.domain(dataSet.map(function(d) {
return d.desc;
}))
.range([0, width]);
yScale = d3.scaleLinear()
.range([height, 0])
.domain([0, 1.15 * d3.max(dataSet, function(d) {
return d.val;
})]);
graph = d3.select("#graph")
.append("svg")
.attr("class", "bar-chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
graph.append("g")
.attr("class", "x-scale")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale))
.selectAll(".tick text")
.call(wrap, xScale.bandwidth());
graph.append("g")
.attr("class", "y-scale")
.call(d3.axisLeft(yScale).tickPadding(10));
graph
.append("g")
.attr('class', 'graph-placeholder')
.selectAll("rect")
.data(dataSet)
.enter()
.append("rect")
.attr("class", "bar1")
.attr("height", height)
.attr("width", barWidth)
.attr('x', d => xScale(d.desc) + (xScale.bandwidth() - barWidth) / 2);
graph
.append("g")
.attr('class', 'graph-main')
.selectAll("bar1")
.data(dataSet)
.enter()
.append("rect")
.attr("class", "bar2")
.attr('x', d => xScale(d.desc) + (xScale.bandwidth() - barWidth) / 2)
.attr("y", function(d) {
return yScale(d.val);
})
.attr("height", function(d) {
return height - yScale(d.val);
})
.attr("width", barWidth);
graph
.append("g")
.attr('class', 'bar-label')
.selectAll("text")
.data(dataSet)
.enter()
.append("text")
.text(d => d.val + '%')
.attr("y", function(d) {
return yScale(d.val) - 5;
}).attr('x', function(d) {
return xScale(d.desc) + ((xScale.bandwidth() - barWidth) / 2);
});
function getLines(text, width) {
// create a dummy element
var dummy = d3.select('body')
.append('p')
.classed('dummy-text-wrapper', true)
// set its size to the one we want
.style('width', width + 'px')
.text(text);
var textNode = dummy.node().childNodes[0];
var lines = [''];
var range = document.createRange();
var current = 0;
// get default top value
range.setStart(textNode, 0);
var prevTop = range.getBoundingClientRect().top;
var nextTop = prevTop;
// iterate through all characters
while (current < text.length) {
// move the cursor
range.setStart(textNode, current+1);
// check top position
nextTop = range.getBoundingClientRect().top;
if(nextTop !== prevTop) {
// new line
lines.push("");
}
// add the current character to the last line
lines[lines.length - 1] += text[current++];
prevTop = nextTop;
}
// clean up the DOM
dummy.remove();
return lines;
}
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text(),
lines = getLines(words, width),
line = [],
lineHeight = 1,
y = text.attr("y"),
dy = parseFloat(text.attr("dy"));
text.text('');
lines.forEach(function(words, lineNumber) {
text.append("tspan")
.attr("x", 0)
.attr("y", y)
.attr("dy", lineNumber * lineHeight + dy + "em")
.text(words);
});
});
}
.bar-chart {
background-color: #ccc;
}
.bar2 {
fill: steelblue;
}
.bar1 {
fill: #f2f2f2;
}
text {
font-size: 12px;
/* text-anchor: middle; */
}
.dummy-text-wrapper {
word-break: normal;
display: inline-block;
opacity: 0;
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="container">
<div id="graph"></div>
</div>