SantiSori
SantiSori

Reputation: 411

SVG put circular text over a line

I has this clock and I need to put the numbers above as you can see:

enter image description here

I know the position of each line but if I create a text with x, y and angle like:

<text *ngFor="let line of lines; let index = i" [attr.rotate]="line.angle"
      [attr.x]="line.x1" [attr.y]="line.y1" [id]="'text'+index">
  <tspan class="number">20</tspan>
</text>

I get this: enter image description here

How could I put the text exactly in middle of line and has circle text instead of this?

Upvotes: 1

Views: 104

Answers (2)

chrwahl
chrwahl

Reputation: 13110

In this example each <text> is the child of a <g> and all the <g>s are children of a container <g>. The container is translated to the center and each <text> has a negative value of -44 that moves the text out to the circle. All the <g>s are then rotated. The container is also rotated (-20) to rotate the entire thing in place.

const container = document.getElementById('container');

Object.keys([...Array(13)]).forEach(i => {
  let t = document.createElementNS('http://www.w3.org/2000/svg', 'text');
  t.setAttribute('y', '-44');
  t.textContent = i*5;
  let g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
  g.setAttribute('transform', `rotate(${i*20})`);
  g.append(t);
  container.append(g);
});
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="250">
  <circle cx="50" cy="50" r="40" fill="none" stroke="black" stroke-width="1" />
  <g id="container" transform="translate(50 50) rotate(-20)"
     font-size="8" text-anchor="middle" dominant-baseline="text-bottom">
  </g>
</svg>

The <text>s can also be horizontal by rotating them "back" after rotating the parent <g> (but you can see that they are more difficult to place with an equal distance from the circle):

const container = document.getElementById('container');

Object.keys([...Array(13)]).forEach(i => {
  let t = document.createElementNS('http://www.w3.org/2000/svg', 'text');
  t.setAttribute('transform', `translate(0 -46) rotate(${-i*20+20})`);
  t.textContent = i*5;
  let g2 = document.createElementNS('http://www.w3.org/2000/svg', 'g');
  g2.setAttribute('transform', `rotate(${i*20-20})`);
  g2.append(t);
  container.append(g2);
});
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="250">
  <circle cx="50" cy="50" r="40" fill="none" stroke="black" stroke-width="1" />
  <g id="container" transform="translate(50 50)"
     font-size="8" text-anchor="middle" dominant-baseline="middle">
  </g>
</svg>

Upvotes: 1

Michael Rovinsky
Michael Rovinsky

Reputation: 7210

For each <text> set x = 0, y = -radius and transform='rotate(index * angle)':

const SECTORS = 16;
const RADIUS = 70;

const g = d3.select('g');
for (let i = 0; i < SECTORS; i++) {
    g.append('text')
    .text('20')
    .attr('x', 0)
    .attr('y', -RADIUS)
    .attr('transform', `rotate(${i * 360 / SECTORS})`)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="200" height="200">
  <g transform="translate(100,100)"/>
</svg>

Upvotes: 1

Related Questions