圆形SVG菜单中的水平文本



我需要做一个循环菜单,包含未知/变量数量的元素(我强调这一点,因为我对3或4个元素的静态解决方案不感兴趣(。

我决定使用SVG。当我使用服务器端代码构建HTML/SVG时,有一个SVG路径,我可以使用startOffset = "@(100/items.Count)%"将文本放在该路径上

.container { width: 300px; }
svg { border: 1px solid; }
a:hover { fill: red; }
<div class="container">
<svg viewBox="0 0 200 200">
<defs>
<desc>The path used for the text</desc>
<path id="c" d="M150,100 A50,50 0 1 1 150,99.99z" />
</defs>
<use xlink:href="#c" stroke="#d9d9d9" fill="none"/>
<text font-size="20" >
<textPath xlink:href="#c" startOffset="33%">
<a xlink:href="https://stackoverflow.com">Our products</a>
</textPath>
</text>

<text font-size="20" text-anchor="middle">
<textPath xlink:href="#c" startOffset="66%">
<a xlink:href="https://stackoverflow.com">Services</a>
</textPath>
</text>

<text font-size="20" text-anchor="end">
<textPath xlink:href="#c" startOffset="99%">
<a xlink:href="https://stackoverflow.com">Achievements</a>
</textPath>
</text>
</svg>
</div>

我的问题是如何让文本对用户可读(而不是叠加、倒置、难以阅读(?具体的问题是如何使文本水平,保持其在圆路径上的位置/"基点"?之后我会使用类似的东西

.textbox    { 
max-width: 200px; 
white-space: nowrap; 
overflow: hidden;
text-overflow: ellipsis;
}

PS我更感兴趣的是SVG/CSS/HTML简单的解决方案,而不是JS复杂的绘图库。

类似的东西?

let links = [
{
text: "Our products",
url: "https://stackoverflow.com"
},
{
text: "Services",
url: "https://stackoverflow.com"
},
{
text: "Achievements",
url: "https://stackoverflow.com"
},
{
text: "something else",
url: "https://stackoverflow.com"
},
{
text: "test",
url: "https://stackoverflow.com"
},
{
text: "more stuff",
url: "https://stackoverflow.com"
}
];
const RADIUS_PADDING = 10;
function makeMenu(circleElementId, linksData)
{
var circle = document.getElementById(circleElementId);
var svg = circle.ownerSVGElement;
var r = circle.r.baseVal.value + RADIUS_PADDING;
var cx = circle.cx.baseVal.value;
var cy = circle.cx.baseVal.value;
for (var i = 0; i < linksData.length; i++)
{
var angle = i * 2 * Math.PI / linksData.length;
var o = {'x': cx + r * Math.sin(angle),
'y': cy - r * Math.cos(angle),
'text-anchor': (angle <= Math.PI) ? "start" : "end"};
// Make a link (a) element
var aLink = addLink(linksData[i].url, svg);

// Make a text element for the link text
addText(o, linksData[i].text, aLink);

}
}
function addLink(url, parent)
{
var link = document.createElementNS(parent.namespaceURI, "a");
link.setAttributeNS("http://www.w3.org/1999/xlink", "href", url);
parent.appendChild(link);
return link;
}
function addText(o, txt, parent)
{
var text = document.createElementNS(parent.namespaceURI, "text");
for (var name in o) {
if (o.hasOwnProperty(name)) {
text.setAttribute(name, o[name]);
}
text.textContent = txt;
}
parent.appendChild(text);
return text;
}
makeMenu("menu-circle", links)
svg {
border: 1px solid;
width:90vh;
}
circle {
fill: none;
stroke: #d9d9d9;
}
text {
font-size: 8px;
font-family:consolas;
dominant-baseline: middle;
}
a:hover{fill:red}
<div class="container">
<svg viewBox="-10 0 220 200">
<circle id="menu-circle" r="60" cx="100" cy="100" stroke="black" fill="none"/>
</svg>
</div>

没有复杂的JS库,但有一些JS用于计算文本的位置。文本是水平的。我真的不喜欢这个结果(从美学角度来说(。对于圆形菜单,我会使用图标。

const SVG_NS = "http://www.w3.org/2000/svg";
const SVG_XLINK = "http://www.w3.org/1999/xlink";
let links = [
{
text: "Our products",
parent: "_a"
},
{
text: "Services",
parent: "_b"
},
{
text: "Achievements",
parent: "_c"
},
{
text: "something else",
parent: "_d"
},
{
text: "test",
parent: "_e"
}
];
let R = 60;
let center = { x: 100, y: 100 };
for (let i = 0; i < links.length; i++) {
let angle = i * 2 * Math.PI / links.length;
let o = {};
o.x = center.x + R * Math.cos(angle);
o.y = center.y + R * Math.sin(angle);
let theparent = document.querySelector("#" + links[i].parent);
drawText(o, links[i].text, theparent);
}
function drawText(o, txt, parent) {
var text = document.createElementNS(SVG_NS, "text");
for (var name in o) {
if (o.hasOwnProperty(name)) {
text.setAttributeNS(null, name, o[name]);
}
text.textContent = txt;
}
parent.appendChild(text);
return text;
}
svg {
border: 1px solid;
width:90vh;
}
circle {
fill: none;
stroke: #d9d9d9;
}
text {
font-size: 12px;
font-family:consolas;
dominant-baseline: middle;
text-anchor: middle;
}
a:hover{fill:red}
<div class="container">
<svg viewBox="-10 0 220 200">
<circle r="60" cx="100" cy="100" stroke="black" fill="none"/>
<a xlink:href="https://stackoverflow.com" id="_a"></a>
<a xlink:href="https://stackoverflow.com" id="_b"></a>
<a xlink:href="https://stackoverflow.com" id="_c"></a>
<a xlink:href="https://stackoverflow.com" id="_d"></a>
<a xlink:href="https://stackoverflow.com" id="_e"></a>
</svg>
</div>

查看代码笔演示

最新更新