Javascript 尝试使用d3.js创建曲线内带有标签的圆环图
我正在尝试找出如何将文本放入弧中,并自动调整标签的字体大小以适应弧,从而避免文本溢出 我用D3.js创建了一个形状,然后我试着把文本放在弧上,调整我的值,得到了甜甜圈形状里面的文本 问题是文本没有从适当的位置开始,我希望文本也要集中 以下是我一直使用的代码和当前输出:Javascript 尝试使用d3.js创建曲线内带有标签的圆环图,javascript,html,css,d3.js,Javascript,Html,Css,D3.js,我正在尝试找出如何将文本放入弧中,并自动调整标签的字体大小以适应弧,从而避免文本溢出 我用D3.js创建了一个形状,然后我试着把文本放在弧上,调整我的值,得到了甜甜圈形状里面的文本 问题是文本没有从适当的位置开始,我希望文本也要集中 以下是我一直使用的代码和当前输出: 任何帮助都会很好,请提前感谢:)以下是我通过拆分长标签和计算每个部分的文本路径来解决问题的尝试: const数据=[ {值:30,文本:'First',颜色:'red'}, {值:40,文本:'Second',颜色:'gree
任何帮助都会很好,请提前感谢:)以下是我通过拆分长标签和计算每个部分的文本路径来解决问题的尝试:
const数据=[
{值:30,文本:'First',颜色:'red'},
{值:40,文本:'Second',颜色:'green'},
{值:60,文本:'Third',颜色:'blue'},
{值:50,文本:'非常长的文本',颜色:'黄色'},
];
常量splitLongString=(str,count)=>{
const partLength=数学圆(str.length/count);
const words=str.split(“”);
常量部分=[];
str.split(“”).forEach(部分=>{
如果(!parts.length){
零件。推动(零件);
}
否则{
const last=零件[零件长度-1];
if(零件[parts.length-1]。长度>=零件长度)
零件。推动(零件);
其他的
零件[零件长度-1]+=''+零件;
}
});
返回部件;
};
const svg=d3.select('svg');
const width=parseInt(svg.attr('width'));
const height=parseInt(svg.attr('height'));
常数裕度=10;
常数弧宽=50;
常数半径=数学最小值(宽度/2-边距,高度/2-边距)-弧宽/2;
常数中心={x:width/2,y:height/2};
设anglePos=0;
常数角偏移=0.025;
const sum=data.reduce((s,{value})=>s+value,0);
data.forEach({value,text,color},index)=>{
const angle=Math.PI*2*值/和;
常数startAngle=角度位置+角度偏移;
角度pos+=角度;
const endAngle=anglePos-angleOffset;
常数开始={
x:中心.x+半径*Math.sin(startAngle),
y:中心y+半径*-数学cos(startAngle),
};
常数结束={
x:中心x+半径*数学sin(端角),
y:中心y+半径*-数学cos(端角),
};
const flags=value/sum>=0.5?'1':'0 01';
const pathId=`my pie chart path-${index}`;
const path=svg.append('path')
.attr('id',pathId)
.attr('d','M${start.x},${start.y}A${radius},${radius}${flags}${end.x},${end.y})
.style('笔划',颜色)
.style('填充','无')
.样式(“笔划宽度”,弧宽度);
const len=path.node().getTotalLength();
const textElement=svg.append('text')
.文本(文本)
.attr('dy',0)
.attr('text-anchor','middle');
常量宽度=textElement.node().getBBox().width;
设text=[text];
如果(宽度>透镜)
text=splitLongString(text,Math.ceil(宽度/长度));
textElement.text(空);
//const midAngle=角度pos-角度/2;
text.forEach((t,i)=>{
const textPathId=`我的饼图路径-${index}-${i}`;
const textRadius=半径-i*12;
常量textStart={
x:center.x+textRadius*Math.sin(startAngle),
y:center.y+textRadius*-Math.cos(startAngle),
};
常量textEnd={
x:center.x+textRadius*Math.sin(端角),
y:center.y+textRadius*-Math.cos(endAngle),
};
const path=svg.append('path')
.attr('id',textPathId)
.attr('d','M${textStart.x},${textStart.y}A${textraidius},${textraidius}${flags}${textEnd.x},${textEnd.y})
.style('笔划','无')
.style('fill','none');
textElement.append('textPath')
.文本(t)
.attr('startOffset',(端角-startAngle)*文本半径/2)
.attr('href',`${textPathId}`)
});
});代码>
文本{
字体系列:Calibri;
字体大小:12px;
字体大小:粗体;
填充:黑色;
}
一些文本在弧内,其他文本在弧外?是故意的吗?实际上是故意的,但现在所有东西都可以在拱门内移动,如果出现文本溢出,应该是多行的。答案是,它很好地打破了这些行,但我要寻找的是,如果文本无法放入容器中,那么字体大小应该是合适的自动调整大小简而言之,每当出现文本溢出时,字体大小应动态更改以适应容器。我认为在同一图表上使用不同字体大小的文本不是一个好主意(从UI角度)。你能做的是把长单词分开,而不仅仅是空格或者省略号(…)对于超过一定限制的文本,这是正确的,但问题是我正在尝试创建的图形可以有40个左右的分区,以便用户至少了解它是什么…我一直在寻找动态更改字体大小,如果面积不够40弧从视觉角度看太多。我怀疑piechart是一个很好的数字可视化模型。。。
function wrap(text, width) {
let lineNumbers = 1;
text.each(function () {
let text = d3.select(this),
line = [],
lineNumbers = 1,
lineNumber = 0,
words = text.text().split(/\s+/).reverse();
words2 = text.text().split(/\s+/).reverse();
console.log("text", text);
while ((word = words.pop())) {
line.push(word);
current_line = line.join(" ");
console.log("current_line", current_line.length);
if (current_line.length > width) {
line.pop();
current_line = line.join(" ");
line = [word];
lineNumbers += 1;
}
}
console.log("lineNumbers", lineNumbers);
append_line = [];
(lineHeight = 1), // ems
(x = text.attr("x")),
(y = text.attr("y")),
(dy = 1), //parseFloat(text.attr("dy")),
(tspan = text
.text(null)
.append("tspan")
.attr("x", x)
.attr("y", y)
.attr("dy", dy + "em"));
while ((word = words2.pop())) {
line = 0;
append_line.push(word);
tspan.text(append_line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
append_line.pop();
line = 1;
tspan.text(append_line.join(" "));
append_line = [word];
if (line == 0) {
tspan = text
.append("tspan")
.attr("x", x)
.attr("y", y)
.attr("dy", ++lineNumber * lineHeight + dy + "em")
.attr("dx", -9 + "em")
.text(word);
}
if (line == 1) {
tspan = text
.append("tspan")
.attr("x", x)
.attr("y", y)
.attr("dy", ++lineNumber * lineHeight + dy + "em")
.attr("dx", -10 + "em")
.text(word);
}
}
}
});
}
let screenWidth = window.innerWidth;
let margin = { left: 20, top: 20, right: 20, bottom: 20 },
width = Math.min(screenWidth, 500) - margin.left - margin.right,
height = Math.min(screenWidth, 500) - margin.top - margin.bottom;
let svg = d3
.select("#chart")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "wrapper")
.attr(
"transform",
"translate(" +
(width / 2 + margin.left) +
"," +
(height / 2 + margin.top) +
")"
);
//////////////////////////////////////////////////////////////
///////////////////// Data & Scales /////////////////////////
//////////////////////////////////////////////////////////////
//Some random data
let donutData = [
{ name: "Antelope sjvadknk saoindosa savasa slahslas sasas", value: 15 },
{ name: "Bear fhfxhxfhxgxhg hgxhx", value: 9 },
{ name: "Cheetah", value: 19 },
{ name: "Dolphin", value: 12 },
{ name: "Elephant", value: 14 },
{ name: "Flamingo", value: 21 },
{ name: "Giraffe", value: 18 },
{ name: "Other", value: 8 },
];
//Create a color scale
let colorScale = d3.scale
.linear()
.domain([1, 3.5, 6])
.range(["#2c7bb6", "#ffffbf", "#d7191c"])
.interpolate(d3.interpolateHcl);
//Create an arc function
let arc = d3.svg
.arc()
.innerRadius((width * 0.75) / 2)
.outerRadius((width * 0.75) / 2 + 30);
//Turn the pie chart 90 degrees counter clockwise, so it starts at the left
let pie = d3.layout
.pie()
.startAngle((-90 * Math.PI) / 180)
.endAngle((-90 * Math.PI) / 180 + 2 * Math.PI)
.value(function (d) {
return d.value;
})
.padAngle(0.01)
.sort(null);
//////////////////////////////////////////////////////////////
//////////////////// Create Donut Chart //////////////////////
//////////////////////////////////////////////////////////////
//Create the donut slices and also the invisible arcs for the text
svg
.selectAll(".donutArcs")
.data(pie(donutData))
.enter()
.append("path")
.attr("class", "donutArcs")
.attr("d", arc)
.style("fill", function (d, i) {
if (i === 7) return "#CCCCCC";
//Other
else return colorScale(i);
})
.each(function (d, i) {
//Search pattern for everything between the start and the first capital L
let firstArcSection = /(^.+?)L/;
//Grab everything up to the first Line statement
let newArc = firstArcSection.exec(d3.select(this).attr("d"))[1];
//Replace all the comma's so that IE can handle it
newArc = newArc.replace(/,/g, " ");
//If the end angle lies beyond a quarter of a circle (90 degrees or pi/2)
//flip the end and start position
if (d.endAngle > (90 * Math.PI) / 180) {
let startLoc = /M(.*?)A/, //Everything between the first capital M and first capital A
middleLoc = /A(.*?)0 0 1/, //Everything between the first capital A and 0 0 1
endLoc = /0 0 1 (.*?)$/; //Everything between the first 0 0 1 and the end of the string (denoted by $)
//Flip the direction of the arc by switching the start en end point (and sweep flag)
//of those elements that are below the horizontal line
let newStart = endLoc.exec(newArc)[1];
let newEnd = startLoc.exec(newArc)[1];
let middleSec = middleLoc.exec(newArc)[1];
//Build up the new arc notation, set the sweep-flag to 0
newArc = "M" + newStart + "A" + middleSec + "0 0 0 " + newEnd;
} //if
//Create a new invisible arc that the text can flow along
svg
.append("path")
.attr("class", "hiddenDonutArcs")
.attr("id", "donutArc" + i)
.attr("d", newArc)
.style("fill", "none");
});
//Append the label names on the outside
svg
.selectAll(".donutText")
.data(pie(donutData))
.enter()
.append("text")
.attr("class", "donutText")
//Move the labels below the arcs for those slices with an end angle greater than 90 degrees
.attr("dy", 20)
.append("textPath")
.attr("startOffset", "50%")
.style("text-anchor", "middle")
.attr("xlink:href", function (d, i) {
return "#donutArc" + i;
})
.attr("font-size", function (d, i) {
return 10;
})
.text(function (d) {
return d.data.name;
})
.call(wrap, 100);