代码之家  ›  专栏  ›  技术社区  ›  serge

圆形SVG菜单中的水平文本

  •  0
  • serge  · 技术社区  · 6 年前

    我要做一份圆形菜单 未知/变量 元素的数量(我在这一点下划线,因为我对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复杂的图形库。

    2 回复  |  直到 6 年前
        1
  •  2
  •   Paul LeBeau    6 年前

    像这样的?

    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>
        2
  •  1
  •   enxaneta    6 年前

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

    见A codepen demo