【问题标题】:Horizontal text in a circular SVG menu圆形 SVG 菜单中的水平文本
【发布时间】:2019-05-08 01:45:08
【问题描述】:

我需要做一个圆形菜单,其中包含 未知/可变 数量的元素(我强调这一点是因为对 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 复杂的绘图库。

【问题讨论】:

  • 谁的投票范围太广了?

标签: html css svg


【解决方案1】:

这样的?

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>

【讨论】:

  • ) 也许只是将顶部和底部元素居中? ) 伟大的工作
  • 'text-anchor': (angle % Math.PI == 0) ? "middle" : (angle &lt; Math.PI) ? "start" : "end"};
  • 我知道你能做到;)
  • 如果我需要在圆圈上添加一些小点怎么办? )
  • 该列表中的所有 CSS 属性(fill 除外)都是纯 HTML 属性。它们在 SVG 中没有任何意义。 SVG 没有自动换行。
【解决方案2】:

没有复杂的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>

查看codepen demo

【讨论】:

  • 自动化这个有点困难...我的意思是,如果我在 JS 中有 100 个链接,我需要分别在 html 中生成 100 个 a 标签并生成 ids a, b c... z... aa bb?为什么不使用 startOffset 代替?
  • 因为 startOffeset 是一个&lt;textPath&gt; 属性。
  • 为什么不使用文本路径?
  • 当您使用&lt;textPath&gt; 时,文本沿路径呈现。你想要to make the text horizontal, keeping its position/"base point" on the circle's path。可能我没看懂你的问题。也许您应该通过添加具有所需输出的图像来编辑它。
  • 我希望文本与路径链接...更多的是,如果图片被重新调整(例如,窗口变小),文本应该遵循路径...它的基点应该在路上。只需强制 textPath 水平绘制
猜你喜欢
  • 1970-01-01
  • 2019-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多