79602855

Date: 2025-05-02 05:27:50
Score: 2
Natty:
Report link

Using the @mplungjan idea with some code:

Html:

<svg id="svg0" viewBox="0 0 250 250"></svg>
<input style="font-size:100px" type="textbox" id="tbName"></input>
<button style="font-size:100px" onclick=add()>Insert</button>

Javascript:

const startFontSize=30;
const minFontSize=1;
const fontSizeUnit="px";
const fontName="courier";
const fontStyle="";

var startRadius=100;//biggest circle radius
const rate=7/9;//font-size decreasing rate
gap=-2;//space between circles
let names=[['Mary','John','Paul','Lisa','Richard','Steve'],['Cristhian','Alex']];

const tsvg="http://www.w3.org/2000/svg";
const txlink='http://www.w3.org/1999/xlink';
const circlePath="M0 100 C 6 -33 194 -33 200 100 C 194 233 6 233 0 100 Z";//circle of radius 100 centered at (100,100)

function buildFontString(fontSize){
return fontStyle+ ' ' +fontSize +fontSizeUnit+' '+fontName; //font string
}

function calcStringHeight(fontString) {
  let text = document.createElementNS(tsvg,"text");
  text.style.setProperty("font", fontString);
  text.textContent = "AAA";
  document.getElementById("svg0").appendChild(text);
  let bb=text.getBBox();
  document.getElementById("svg0").removeChild(text);
  return bb.height;
}

function calcStringWidth(str, fontString) {
  let text = document.createElementNS(tsvg,"text");
  text.style.setProperty("font", fontString);
  text.textContent = str;
  document.getElementById("svg0").appendChild(text);
  let bb=text.getBBox();
  document.getElementById("svg0").removeChild(text);
  return bb.width;
}

const pad=calcStringHeight(buildFontString(startFontSize));//space required by text outside circle

//calcs all possible circle radius
function calcRadius(){
  let radius=[];
  let fontSize=startFontSize;
  let currRadius=startRadius;
  while(fontSize>minFontSize&&currRadius>gap) {
    radius.push(currRadius);
    fontSize*=rate;
    currRadius-=calcStringHeight(buildFontString(fontSize))+gap;
  }
  return radius;
}

const radius=calcRadius();

//draw one text circle
function drawTextCircle(str,index){
  const scale=startRadius/radius[index];
  const fontSize=startFontSize*Math.pow(rate,index);
  const path=document.createElementNS(tsvg,"path");
  path.id="circle"+index;
  path.setAttribute("fill","white");
  path.setAttribute("d",circlePath);
  path.setAttribute("transform",`translate(${pad+radius[index]*(scale-1)},${pad+radius[index]*(scale-1)}),scale(${1/scale})`);
  document.getElementById("svg0").appendChild(path);
  const text=document.createElementNS(tsvg,"text");
  text.style.setProperty("font", buildFontString(fontSize));
  const textPath=document.createElementNS(tsvg,"textPath");
  textPath.id="tp"+index;
  textPath.setAttributeNS(txlink,"xlink:href","#circle"+index);
  textPath.setAttribute("textLength",2*Math.PI*radius[index]-calcStringWidth(" ",buildFontString(fontSize)));
  textPath.textContent=str;
  text.appendChild(textPath);
  document.getElementById("svg0").appendChild(text); 
}

function insertName(name){
  const len=names.length;
  const maxCircles=radius.length;
  const currRadius=radius[len-1];
  const fontSize=startFontSize*Math.pow(rate,len-1);
  let str="",strWidth=0;
  //try to add in the current circle
  if(len>0){//one circle already exist
    for(var i=0;i<names[len-1].length;i++)
      str+=names[len-1][i]+" ";
    str+=name;
    strWidth=calcStringWidth(str,buildFontString(fontSize));
    if(strWidth<=2*Math.PI*currRadius)
      names[len-1].push(name);//add to the last
    else if(len<maxCircles)
      names.push([name]);//create new circle
    else return false;
  }
  else if(len<maxCircles)
    names.push([name]);//create new circle
  else return false;
  return true;
}

function drawNames(){
  let str="";
  for(var i=0;i<names.length;i++){
    str="";
    for(var j=0;j<names[i].length;j++)
      str+=names[i][j]+" ";
    drawTextCircle(str,i);
  }
}

function drawName(name){
  const tp=document.getElementById("tp"+(names.length-1));
  if(tp){
    const clone=tp.cloneNode(true);
    clone.textContent+=name+" ";
    tp.parentNode.replaceChild(clone,tp);
  }
  else
    drawTextCircle(name+" ",names.length-1);
}

function add(){
  const name=document.getElementById("tbName").value;
  if (insertName(name))
  drawName(name);
}

drawNames();

One thing that I dont understand is why the space between circles are greater than the calculated font-height... Thanks to SVG, Text does not render on textPath when I create it dynamically

More information in textPath could be found in https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/textPath

Reasons:
  • Blacklisted phrase (0.5): Thanks
  • Probably link only (1):
  • Long answer (-1):
  • Has code block (-0.5):
  • User mentioned (1): @mplungjan
  • Low reputation (1):
Posted by: Eddi