fdkgfosfskjdlsjdlkfsf
fdkgfosfskjdlsjdlkfsf

Reputation: 3301

JavaScript function drawing arc at incorrect angle

I'm trying to create an arc that points to a specific angle, width (in degrees), and distance from center (so that the arcs don't overlap).

The included snippet calls JavaScript function DrawLine that draws the line, but at the incorrect angle. Also, the arc changes its angle when the DISTANCE_FROM_CENTER variables change, and that shouldn't happen.

I'm including a screenshot of what the angles should look like.

enter image description here

I'm also including how we interpret angles in a circle, and 310 degrees should point to the upper left. The javascript draws them pointing to the right.

enter image description here

let svg = document.getElementById("icon");
function drawLine(azimuth, radius, length)
{
  let circumference = radius * 2 * Math.PI;
  // Create an SVG <circle> element
  let line = document.createElementNS(svg.namespaceURI, "circle");
  line.setAttribute("r", radius);
  line.setAttribute("stroke-dasharray", length + ' ' + circumference);
  line.setAttribute("transform", "rotate(" + azimuth + ")");
  // Add it to the <svg> element
  svg.appendChild(line);
}

let DISTANCE_FROM_CENTER1 = 93,
DISTANCE_FROM_CENTER2 = 65,
DISTANCE_FROM_CENTER3 = 30,
DISTANCE_FROM_CENTER4 = 9;

drawLine(312, DISTANCE_FROM_CENTER1, 110);

drawLine(312, DISTANCE_FROM_CENTER2, 95);

drawLine(312, DISTANCE_FROM_CENTER3, 55);

drawLine(312, DISTANCE_FROM_CENTER4, 15);
svg {
  width: 400px;
}

circle {
  fill: none;
  stroke: black;
  stroke-width: 14;
}
<svg id="icon" viewBox="-100 -100 200 200">
</svg>

Upvotes: 1

Views: 257

Answers (1)

Chris Strickland
Chris Strickland

Reputation: 3490

If you're trying to line up the middles of the segments, you'll have to calculate the angle. The segment starts at 0 and the length is based on that starting point, and then when you rotate the segment the entire thing turns that amount. Since you're passing the same transform value (200) to all the segments, they all turn the same amount, and so all of their starting points are still aligned.

So try this (I'm just using the values you were using in your snippet, not sure how these relate to the picture, which has a different number of segments and lengths and angles). Calculate the percentage of arc that your segment comprises, and offset the rotation by half of that.

let svg = document.getElementById("icon");
function drawLine(azimuth, radius, length) {
  
  let circumference = radius * 2 * Math.PI;
  let arc = length / circumference * 360;
  
  let line = document.createElementNS(svg.namespaceURI, "circle");
  line.setAttribute("r", radius);
  line.setAttribute("stroke-dasharray", `${length} ${circumference}`);
  line.setAttribute("transform", `rotate(${azimuth-(arc/2)})`);
  
  svg.appendChild(line);
}

const arcs = [
  {azimuth: 200, dist: 93, length: 110},
  {azimuth: 200, dist: 65, length:  95},
  {azimuth: 200, dist: 30, length:  55},
  {azimuth: 200, dist:  9, length:  15},
  //{azimuth: 200, dist: 1,  length: 1.5}
];

arcs.forEach(el=>{drawLine(el.azimuth, el.dist, el.length)});
svg {
  width: 400px;
}

circle {
  fill: none;
  stroke: black;
  stroke-width: 14;
}
<svg id="icon" viewBox="-100 -100 200 200">
</svg>

SVG thinks 0 is dead right, and that's where the path is going to start, so if you expect 312 to point toward the upper left, like your picture, and you also want to keep passing it 312, etc, I think you can just subtract 90 from the azimuth inside your function. Like this:

let svg = document.getElementById("icon");
function drawLine(azimuth, radius, length) {
  
  let circumference = radius * 2 * Math.PI;
  let arc = length / circumference * 360;
  
  let line = document.createElementNS(svg.namespaceURI, "circle");
  line.setAttribute("r", radius);
  line.setAttribute("stroke-dasharray", `${length} ${circumference}`);
  line.setAttribute("transform", `rotate(${azimuth-(arc/2)-90})`);
  
  svg.appendChild(line);
}

const arcs = [
  {azimuth: 312, dist: 93, length: 110},
  {azimuth: 312, dist: 65, length:  95},
  {azimuth: 312, dist: 30, length:  55},
  {azimuth: 312, dist:  9, length:  15},
  //{azimuth: 312, dist: 1,  length: 1.5}
];

arcs.forEach(el=>{drawLine(el.azimuth, el.dist, el.length)});
svg {
  width: 400px;
}

circle {
  fill: none;
  stroke: black;
  stroke-width: 14;
}
<svg id="icon" viewBox="-100 -100 200 200">
</svg>

Upvotes: 2

Related Questions