Wrap text to a circle shape in svg or canvas

前端 未结 1 1658
南笙
南笙 2020-12-01 22:27

What would be a good solution for fitting text to a circle on a website, so it flows with the curves of the circle, instead of a rectangular bounding box?

Here\'s wh

相关标签:
1条回答
  • 2020-12-01 23:12

    There is a proposed CSS feature call "exclusions" that would make it possible to flow text inside defined areas: http://www.w3.org/TR/css3-exclusions/

    This means that SVG and Canvas paths would likely be defined as containers and text would flow/wrap inside the containers.

    I did say "proposed" -- it's a ways off from being a reality in browsers.

    However...

    You can fairly easily wrap text inside a circle using html canvas

    enter image description here

    The width available to display text on any line changes as you move down the circle.

    Here’s how to determine the maximum available width of any horizontal line on a circle

    // var r is the radius of the circle
    // var h is the distance from the top of the circle to the horizontal line you’ll put text on
    
    var maxWidth=2*Math.sqrt(h*(2*r-h));
    

    You fit text to the line by measuring the width of text—adding one word at a time, until you’ve used up all the available width of that line.

    Here’s how to use canvas to measure any text using the current context.font:

    var width=ctx.measureText(“This is some test text.”).width;
    

    The rest is just adding text to each line up to the maximum line width and then starting a new line.

    If you prefer SVG, you can do similar in SVG using the element.getComputedTextLength method for text metrics.

    Here is code and a Fiddle: http://jsfiddle.net/m1erickson/upq6L/

    <!doctype html>
    <html lang="en">
    <head>
    
      <style>
          body{ background-color: ivory; padding:20px; }
          canvas{ border:1px solid red;}
      </style>
      <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
    
      <script>
    
      $(function() {
    
          var canvas=document.getElementById("canvas");
          var ctx=canvas.getContext("2d");
    
          var text = "'Twas the night before Christmas, when all through the house,  Not a creature was stirring, not even a mouse.  And so begins the story of the day of Christmas";
          var font="12pt verdana";
          var textHeight=15;
          var lineHeight=textHeight+5;
          var lines=[];
    
          var cx=150;
          var cy=150;
          var r=100;
    
          initLines();
    
          wrapText();
    
          ctx.beginPath();
          ctx.arc(cx,cy,r,0,Math.PI*2,false);
          ctx.closePath();
          ctx.strokeStyle="skyblue";
          ctx.lineWidth=2;
          ctx.stroke();
    
    
          // pre-calculate width of each horizontal chord of the circle
          // This is the max width allowed for text
    
          function initLines(){
    
              for(var y=r*.90; y>-r; y-=lineHeight){
    
                  var h=Math.abs(r-y);
    
                  if(y-lineHeight<0){ h+=20; }
    
                  var length=2*Math.sqrt(h*(2*r-h));
    
                  if(length && length>10){
                      lines.push({ y:y, maxLength:length });
                  }
    
              }
          }
    
    
          // draw text on each line of the circle
    
          function wrapText(){
    
              var i=0;
              var words=text.split(" ");
    
              while(i<lines.length && words.length>0){
    
                  line=lines[i++];
    
                  var lineData=calcAllowableWords(line.maxLength,words);
    
                  ctx.fillText(lineData.text, cx-lineData.width/2, cy-line.y+textHeight);
    
                  words.splice(0,lineData.count);
              };
    
          }
    
    
          // calculate how many words will fit on a line
    
          function calcAllowableWords(maxWidth,words){
    
              var wordCount=0;
              var testLine="";
              var spacer="";
              var fittedWidth=0;
              var fittedText="";
    
              ctx.font=font;
    
              for(var i=0;i<words.length; i++){
    
                  testLine+=spacer+words[i];
                  spacer=" ";
    
                  var width=ctx.measureText(testLine).width;
    
                  if(width>maxWidth){ 
                      return({
                          count:i, 
                          width:fittedWidth, 
                          text:fittedText
                      }); 
                  }
    
                  fittedWidth=width;
                  fittedText=testLine;
    
              }
    
          }
    
    
      });   // end $(function(){});
    
      </script>
    </head>
    <body>
        <p>Text wrapped and clipped inside a circle</p>
        <canvas id="canvas" width=300 height=300></canvas>
    </body>
    </html>
    
    0 讨论(0)
提交回复
热议问题