Frontend Development 13 min read

Drawing Arbitrary Angle Arcs in SVG with Text Labels and Gradient Colors

This article explains how to draw SVG arcs of any angle, position and rotate text labels along the arcs, generate gradient colors using both RGBA and HSL methods, and assemble a reusable JavaScript class that creates interactive circular arc charts with click events.

360 Tech Engineering
360 Tech Engineering
360 Tech Engineering
Drawing Arbitrary Angle Arcs in SVG with Text Labels and Gradient Colors

The author, a front‑end engineer, revisits a question from SegmentFault about creating a circular selector with dates, showing both Canvas and SVG solutions and focusing on the SVG implementation to help readers understand SVG path arc commands.

SVG arcs are defined by seven parameters: rx (horizontal radius), ry (vertical radius), x-axis-rotation , large-arc-flag , sweep-flag , x and y (end point). For a circular arc the radii are equal, reducing the required parameters to start/end points, radius, rotation, large‑arc flag, and sweep direction.

The article provides a concise SVG path command template:

M X0,Y0 A rx ry xAxisRotation largeArcFlag sweepFlag X1,Y1

and an example drawing a 30° arc with center (100,100) and radius 50.

To label the arcs, the text element is used. The label’s position is calculated from the midpoint of the arc, and the text is rotated to align with the tangent using transform="rotate(α x y)" . Because rotation defaults to the text’s lower‑left corner, an additional offset moves the text half its width/height to centre it.

A helper method SVG.prototype.appendCircleArcText encapsulates this logic.

For gradient colors, the author first shows an RGBA‑based approach that interpolates opacity, then improves it with an HSL‑based function that varies lightness to produce a smooth gradient. Example code:

function gradientColor(len, color) {
  color = color || [260, 59.8, 64.9];
  const delta = 28 / len;
  return new Array(len).fill(0).map((_, i) => {
    const c = [...color];
    c[2] += i * delta;
    c[1] += '%';
    c[2] += '%';
    return `hsl(${c.join()})`;
  });
}

The final solution combines all pieces into a reusable SVG class that can create groups ( g ) containing an arc and its label, apply click handlers, and render interactive circular charts for different categories (year, month, week, day). The full class definition is provided:

class SVG {
  constructor(width, height) {
    this.width = width;
    this.height = height;
    this.s = SVG.createSVG(width, height);
  }
  appendCircleArc(circle = {cx:100,cy:100,r:100}, angel = {start:0,end:90}, attrs = {fill:"none"}) {
    const largeArcFlag = Number(angel > 180);
    this.appendItem(SVG.arc({
      largeArcFlag,
      rx: circle.r,
      ry: circle.r,
      startX: circle.cx - circle.r * Math.sin(angel.start/180*Math.PI),
      startY: circle.cy - circle.r * Math.cos(angel.start/180*Math.PI),
      endX:   circle.cx - circle.r * Math.sin(angel.end/180*Math.PI),
      endY:   circle.cy - circle.r * Math.cos(angel.end/180*Math.PI)
    }, attrs));
    return this;
  }
  appendCircleArcText(text, circle = {cx:100,cy:100,r:100}, angel = {start:0,end:90}, width = 16, attrs = {}) {
    angel = angel.start + (angel.end - angel.start)/2;
    const posX = circle.cx - circle.r * Math.sin(angel/180*Math.PI);
    const posY = circle.cx - circle.r * Math.cos(angel/180*Math.PI);
    const circleArcText = SVG.text(text, {
      ...attrs,
      x: posX,
      y: posY,
      fontSize: width,
      transform: "rotate(-" + angel + " " + posX + " " + posY + ")"
    });
    this.appendItem(circleArcText);
    return this;
  }
  render() { return this.s; }
  renderTo(DOM = document.body) {
    DOM.innerHTML = this.s.outerHTML;
    const texts = Array.from(DOM.querySelectorAll('text'));
    texts.forEach(text => {
      const transform = text.getAttribute('transform');
      if (transform) text.removeAttribute('transform');
      const {width, height} = text.getBoundingClientRect();
      [['x', text.getAttribute('x')/1 - width/2],
       ['y', text.getAttribute('y')/1 + height/2],
       ['transform', transform || '']].forEach(([name, value]) => text.setAttribute(name, value));
    });
    return this;
  }
  appendItem(item) { this.s.appendChild(item); return this; }
  static arc({rx=50, ry=50, xAxisRotation=0, largeArcFlag=0, sweepFlag=0, startX=0, startY=0, endX=0, endY=0}, attrs = {}) {
    attrs.d = `M ${startX},${startY} A ${rx} ${ry} ${xAxisRotation} ${largeArcFlag} ${sweepFlag} ${endX},${endY}`;
    const path = document.createElement('path');
    for (let i in attrs) {
      path.setAttribute(i.replace(/[A-Z]/g, o => `-${o}`), attrs[i]);
    }
    return path;
  }
  static text(text = '', attrs = {}) {
    const t = document.createElement('text');
    t.innerHTML = text;
    for (let i in attrs) {
      t.setAttribute(i.replace(/[A-Z]/g, o => `-${o}`), attrs[i]);
    }
    return t;
  }
  static g = class extends SVG {
    constructor(attrs = {}) { super(); this.s = document.createElement('g'); for (let i in attrs) { this.s.setAttribute(i.replace(/[A-Z]/g, o => `-${o}`), attrs[i]); } }
  };
  static createSVG(width, height) {
    const s = document.createElement('svg');
    s.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
    s.setAttribute('width', width);
    s.setAttribute('height', height);
    s.setAttribute('viewBox', `0 0 ${width} ${height}`);
    return s;
  }
}

Using the class, the article builds a multi‑layer circular chart for years, months, weeks and days, attaches click events to each segment, and demonstrates the final interactive output via online previews.

frontendgraphicsJavaScriptSVGgradientARCTEXT
360 Tech Engineering
Written by

360 Tech Engineering

Official tech channel of 360, building the most professional technology aggregation platform for the brand.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.