I have a gauge chart that currently has a dial - how do I modify this to arc it back further and round the edges, and create a filled circle at the tip of the arc?
https://codesandbox.io/s/blissful-framework-chlptz?file=/src/GaugeChart.js
currently the chart I have looks like this with a dial. Restyling the current chart code.
import React from 'react'
import * as d3 from 'd3'
class GaugeChart extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
this.state = {
data: [],
theme: this.props.theme
? this.props.theme
: ['#bde0fe', '#2698f9', '#71bcfd', '#f1f8fe'],
}
}
componentDidMount() {
var $this = this.myRef.current
d3.select($this).selectAll('svg').remove()
const value = this.props.value
const data = [
{
label: 'Completed',
value: value,
},
{
label: 'Remaining',
value: 100 - value,
},
]
const width = parseInt(this.props.width, 10),
height = parseInt(this.props.height, 10),
radius = parseInt(this.props.r, 10),
innerradius = parseInt(this.props.ir, 10)
var color = d3.scaleOrdinal().range(this.state.theme)
var arc = d3.arc().outerRadius(radius).innerRadius(innerradius)
data.forEach(function (d) {
d.total = +d.value
})
var pie = d3
.pie()
.startAngle(-90 * (Math.PI / 180))
.endAngle(90 * (Math.PI / 180))
.padAngle(0.02) // some space between slices
.sort(null)
.value(function (d) {
return d.total
})
var svg = d3
.select($this)
.append('svg')
.attr('width', width)
.attr('height', height + 5)
.append('g')
.attr('class', 'piechart')
.attr('transform', 'translate(' + width / 2 + ',' + height + ')')
var segments = svg.append('g').attr('class', 'segments')
var slices = segments
.selectAll('.arc')
.data(pie(data))
.enter()
.append('g')
.attr('class', 'arc')
slices
.append('path')
.attr('d', arc)
.attr('fill', function (d, i) {
return color(i)
})
/*.transition()
.attrTween('d', function(d) {
var i = d3.interpolate(d.startAngle + 0.1, d.endAngle);
return function(t) {
d.endAngle = i(t);
return arc(d);
}
} )*/
var arrow = svg.append('g').attr('transform', 'rotate( 0 )')
arrow
.append('path')
.attr('fill', '#ef7e60')
.attr(
'd',
`M -${innerradius - 5},0 -${innerradius - 12},-5 -${
innerradius - 12
},5`,
) // draw triangle
var text = svg
.append('text')
.attr('text-anchor', 'middle')
.attr('dy', 0)
.attr('fill', '#ef7e60')
function update(data) {
text.text(`${Math.round(data[0].value)}%`) // set % text
arrow.transition().attr('transform', `rotate( ${data[0].value * 1.8} )`) //rotate to desired qantity ( i put to 60% of 180 deg )
}
update(data)
}
render() {
return <div ref={this.myRef} className="GaugeChart" />
}
}
export default GaugeChart
latest fork https://codesandbox.io/s/reverent-hodgkin-z3dn4y
https://codesandbox.io/p/sandbox/hardcore-pare-nl95p4?file=%2Fsrc%2FGaugeChart.js%3A1%2C1-160%2C26 latest fork 1st December
import React from 'react'
import * as d3 from 'd3'
class GaugeChart extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
this.state = {
data: [],
theme: this.props.theme
? this.props.theme
: ['#bde0fe', '#2698f9', '#71bcfd', '#f1f8fe'],
}
}
componentDidMount() {
var $this = this.myRef.current
d3.select($this).selectAll('svg').remove()
const value = this.props.value
const data = [
{
sortIndex: 0,
value: value,
fill: 'blue',
},
{
sortIndex: 0,
value: 100 - value,
fill: '#EAEDF6',
},
]
let arcThickness = 4
let size = 50
let transitionDuration = 750
const t = d3.transition().duration(350)
const arc = d3
.arc()
.innerRadius(size / 2 - arcThickness)
.outerRadius(size / 2)
.cornerRadius(arcThickness)
const pie = d3
.pie()
.value((d) => d.value)
.startAngle(-Math.PI / 1.45)
.endAngle(Math.PI / 1.45)
.sort((a, b) => d3.ascending(a.sortIndex, b.sortIndex))
const initData = pie(JSON.parse(JSON.stringify(data)))
const arcs = pie(data)
function arcTween(d, bIsLastArc) {
var interpolateStart = d3.interpolate(-Math.PI / 1.45, d.startAngle)
var interpolateEnd = d3.interpolate(
bIsLastArc ? d.endAngle : -Math.PI / 1.45,
d.endAngle,
)
return function (t) {
d.startAngle = interpolateStart(t)
d.endAngle = interpolateEnd(t)
return arc(d)
}
}
const svg = d3
.select($this)
.append('svg')
.attr('viewBox', [-size / 2, -size / 2, size, size])
// add def and drop shadow for the value dot
svg.append('defs')
.html(`<filter id="dot-shadow" x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox">
<feDropShadow dx="3" dy="3" stdDeviation="8" flood-color="${initData[0].data.fill}" flood-opacity="0.25" />
</filter>`)
// gauge chart skeleton
const arcSkeleton = d3
.arc()
.innerRadius(size / 2 - arcThickness)
.outerRadius(size / 2)
.startAngle(-90 * (Math.PI/131))
.cornerRadius(arcThickness)
.endAngle(90 * (Math.PI/131))
svg
.append('path')
.attr('class', 'arc-skeleton')
.attr('d', arcSkeleton)
.attr('fill', '#EAEDF6')
const gauges = svg
.selectAll('path.gaugechart')
.data(arcs)
.join('path')
.attr('class', 'gaugechart')
.attr('fill', (d) => d.data.fill)
.attr('d', arc)
gauges
.transition()
.duration(transitionDuration)
.attrTween('d', (d, i) => arcTween(d, i == arcs.length - 1))
// Add a dot to mark the end of the first arc
const dot = svg
.selectAll('circle')
.data([
Object.assign(initData[0], {
startAngle: initData[0].endAngle - 0.2,
endAngle: initData[1].startAngle,
stroke: initData[initData.length - 1].data.fill,
}),
])
.join('circle')
.attr('fill', '#fff')
.attr('r', arcThickness * 0.3)
.attr('stroke', (d) => d.stroke)
.attr('stroke-width', 0.1)
.attr('style', 'filter:url(#dot-shadow)')
.attr('transform', (d) => `translate(${arc.centroid(d)})`)
dot
.transition()
.duration(transitionDuration)
.attrTween('transform', (d) => {
const interpolateStart = d3.interpolate(-Math.PI / 1.45, d.startAngle)
const interpolateEnd = d3.interpolate(-Math.PI / 1.45, d.endAngle)
return function (t) {
d.startAngle = interpolateStart(t)
d.endAngle = interpolateEnd(t)
return `translate(${arc.centroid(d)})`
}
})
svg
.append('text')
.attr('transform', `translate(0, ${(0.1 * size) / 2})`)
.attr('font-size', '0.7rem')
.attr('text-anchor', 'middle')
.attr('fill', 'black')
.text(data[0].value)
svg
.append('text')
.attr('transform', `translate(0, ${0.15 * size})`)
.attr('font-size', '0.2rem')
.attr('text-anchor', 'middle')
.attr('fill', 'grey')
.text('Volts')
}
render() {
return <div ref={this.myRef} className="GaugeChart" />
}
}
export default GaugeChart
import React from 'react'
import * as d3 from 'd3'
class GaugeChart extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
this.state = {
data: [],
theme: this.props.theme
? this.props.theme
: ['#bde0fe', '#2698f9', '#71bcfd', '#f1f8fe'],
}
}
componentDidMount() {
var $this = this.myRef.current
d3.select($this).selectAll('svg').remove()
const value = this.props.value
const data = [
{
sortIndex: 0,
value: value,
fill: 'blue',
},
{
sortIndex: 0,
value: 100 - value,
fill: '#EAEDF6',
},
]
let arcThickness = 4
let size = 50
let transitionDuration = 750
const t = d3.transition().duration(350)
const arc = d3
.arc()
.innerRadius(size / 2 - arcThickness)
.outerRadius(size / 2)
.cornerRadius(arcThickness)
const pie = d3
.pie()
.value((d) => d.value)
.startAngle(-Math.PI / 1.45)
.endAngle(Math.PI / 1.45)
.sort((a, b) => d3.ascending(a.sortIndex, b.sortIndex))
const initData = pie(JSON.parse(JSON.stringify(data)))
const arcs = pie(data)
function arcTween(d, bIsLastArc) {
var interpolateStart = d3.interpolate(-Math.PI / 1.45, d.startAngle)
var interpolateEnd = d3.interpolate(
bIsLastArc ? d.endAngle : -Math.PI / 1.45,
d.endAngle,
)
return function (t) {
d.startAngle = interpolateStart(t)
d.endAngle = interpolateEnd(t)
return arc(d)
}
}
const svg = d3
.select($this)
.append('svg')
.attr('viewBox', [-size / 2, -size / 2, size, size])
// add def and drop shadow for the value dot
svg.append('defs')
.html(`<filter id="dot-shadow" x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox">
<feDropShadow dx="3" dy="3" stdDeviation="8" flood-color="${initData[0].data.fill}" flood-opacity="0.25" />
</filter>`)
// gauge chart skeleton
const arcSkeleton = d3
.arc()
.innerRadius(size / 2 - arcThickness)
.outerRadius(size / 2)
.startAngle(-90 * (Math.PI/131))
.cornerRadius(arcThickness)
.endAngle(90 * (Math.PI/131))
svg
.append('path')
.attr('class', 'arc-skeleton')
.attr('d', arcSkeleton)
.attr('fill', '#EAEDF6')
const gauges = svg
.selectAll('path.gaugechart')
.data(arcs)
.join('path')
.attr('class', 'gaugechart')
.attr('fill', (d) => d.data.fill)
.attr('d', arc)
gauges
.transition()
.duration(transitionDuration)
.attrTween('d', (d, i) => arcTween(d, i == arcs.length - 1))
// Add a dot to mark the end of the first arc
const dot = svg
.selectAll('circle')
.data([
Object.assign(initData[0], {
startAngle: initData[0].endAngle - 0.2,
endAngle: initData[1].startAngle,
stroke: initData[initData.length - 1].data.fill,
}),
])
.join('circle')
.attr('fill', '#fff')
.attr('r', arcThickness * 0.3)
.attr('stroke', (d) => d.stroke)
.attr('stroke-width', 0.1)
.attr('style', 'filter:url(#dot-shadow)')
.attr('transform', (d) => `translate(${arc.centroid(d)})`)
dot
.transition()
.duration(transitionDuration)
.attrTween('transform', (d) => {
const interpolateStart = d3.interpolate(-Math.PI / 1.45, d.startAngle)
const interpolateEnd = d3.interpolate(-Math.PI / 1.45, d.endAngle)
return function (t) {
d.startAngle = interpolateStart(t)
d.endAngle = interpolateEnd(t)
return `translate(${arc.centroid(d)})`
}
})
svg
.append('text')
.attr('transform', `translate(0, ${(0.1 * size) / 2})`)
.attr('font-size', '0.7rem')
.attr('text-anchor', 'middle')
.attr('fill', 'black')
.text(data[0].value)
svg
.append('text')
.attr('transform', `translate(0, ${0.15 * size})`)
.attr('font-size', '0.2rem')
.attr('text-anchor', 'middle')
.attr('fill', 'grey')
.text('Volts')
}
render() {
return <div ref={this.myRef} className="GaugeChart" />
}
}
export default GaugeChart
Update your GaugeChart component according to above code snippets