I’m creating a svg circle that has different segments, which will work like tabs for for showing/hiding content. It’s basically a pie chart. I’m having trouble visually separating the segments I’m creating. I need it to stroke just from the center to the outside of the circle. I feel I need to figure out the stroke-dasharray attribute? Would love some help figuring out how to configure things.
My other ideas were to use a margin to offset the segmentPath function but the client wasn’t loving the look of things. Finally had an idea to just add a separate line element to each segment – traveling from the edge of the circle to the center. Any help would be great.
/**
* Polar to Cartesian.
*
* Håken Lid's SVG circle segments.
*
* @see https://observablehq.com/@haakenlid/svg-circle
* @see https://blog.logrocket.com/interactive-svg-circle-of-fifths/#arcs-circles
*/
function polarToCartesian(x, y, r, degrees) {
const radians = degrees * Math.PI / 180.0;
return [x + (r * Math.cos(radians)), y + (r * Math.sin(radians))]
}
/**
* Segment Path.
*
* Håken Lid's SVG circle segments.
*
* @see https://observablehq.com/@haakenlid/svg-circle
* @see https://blog.logrocket.com/interactive-svg-circle-of-fifths/#arcs-circles
*/
function segmentPath(x, y, r0, r1, d0, d1) {
const arc = Math.abs(d0 - d1) > 180 ? 1 : 0
const point = (radius, degree) =>
polarToCartesian(x, y, radius, degree)
.map(n => n.toPrecision(5))
.join(',')
return [
`M${point(r0, d0)}`,
`A${r0},${r0},0,${arc},1,${point(r0, d1)}`,
`L${point(r1, d1)}`,
`A${r1},${r1},0,${arc},0,${point(r1, d0)}`,
'Z',
].join('')
}
/**
* Segment Function.
*
* Modified version of Håken Lid's SVG circle segments.
*
* @see https://observablehq.com/@haakenlid/svg-circle
* @see https://blog.logrocket.com/interactive-svg-circle-of-fifths/#arcs-circles
*/
function segment(i, segments, size, radius, width, className, fillColor) {
const center = size/2
const degrees = 360 / segments
const start = degrees * i
const end = (degrees * (i + 1) + 1)
const path = segmentPath(center, center, radius, radius-width, start, end);
const el = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' );
el.setAttribute(
'd',
path
);
el.classList.add( 'path-segment', className );
el.style.fill = fillColor;
return el;
}
/**
* Render.
*
* Function that will render the svg circle.
*/
function render() {
// Circle Div.
const circleDiv = document.getElementById( 'interactive-circle-svg' );
if ( null !== circleDiv ) {
// Setup environment.
const svgSize = 580
const segments = 6
const segmentWidth = 60
const outerFill = '#FB8D00'
const middleFill = '#E68E36'
const innerFill = '#F9B46F'
// Set cirlce div attributes.
circleDiv.style.height = svgSize + 'px';
circleDiv.style.width = svgSize + 'px';
circleDiv.style.transform = `rotate(${ 360/2 }deg)`
circleDiv.style.transformOrigin = '50% 50%'
// SVG NS.
const svgns = 'http://www.w3.org/2000/svg';
// Create the <svg>.
const svg = document.createElementNS( svgns, 'svg' )
svg.setAttribute( 'viewbox', `0 0 ${svgSize} ${svgSize}` );
svg.setAttribute( 'overflow', 'visible' );
svg.setAttribute( 'stroke', '#000' );
svg.setAttribute( 'stroke-width', 3 );
// Attach the children nodes.
for ( let i = 0; i < segments; i++ ) {
// Create the first <g>.
let n = i + 1,
radius1 = svgSize/2,
radius2 = svgSize/2-segmentWidth+1,
radius3 = svgSize/2-(segmentWidth*2)+2,
g1 = document.createElementNS( svgns, 'g' );
g1.classList.add( 'segment', `segment-${n}` );
g1.setAttribute( 'data-segment-number', n );
// Attach segments to the <g>.
g1.appendChild( segment( i, segments, svgSize, radius1, segmentWidth, 'outer', outerFill ) );
g1.appendChild( segment( i, segments, svgSize, radius2, segmentWidth, 'middle', middleFill ) );
g1.appendChild( segment( i, segments, svgSize, radius3, segmentWidth, 'inner', innerFill ) );
// Attach the <g> to the <svg>
svg.appendChild( g1 );
// Create event handlers with the g1.
g1.addEventListener( 'click', function(e){
const segNumber = this.getAttribute( 'data-segment-number' );
// Hide bodies.
const bodies = document.getElementsByClassName( 'segment-body' )
for ( let i = 0; i < bodies.length; i++ ) {
if ( ! bodies[i].classList.contains( 'hide' ) ) {
bodies[i].classList.add( 'hide' );
}
}
// Show correct body.
const body = document.getElementById( 'segment-body-' + segNumber);
body.classList.remove( 'hide' );
});
}
// Attach to div.
circleDiv.append( svg );
}
}
// Attach render to onload.
window.onload = render;
<div id="interactive-circle-svg"></div>
2
Answers
My answer ended up being using separate line's to show the difference of the sections. I had to add an extra line at the end to be the first, as there is no z-index in svg-land and there were siblings covering it up.
According to provided code, to implement required functionality, to add line element to each segment from center of circle to edge of circle, this code,
should be replaced with this code,
It can be checked, if it works.