console
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
body {
width: 100%;
height: 100%;
display: flex;
background: linear-gradient(180deg,rgba(3, 9, 37, 1),rgba(3,9, 37, 1),rgba(3, 9, 37, 1),rgba(12, 27, 66, 1));
}
#radial-bar-chart {
margin: auto;
position: relative;
box-shadow: 0 0 10px 0 #eee;
border: 1px solid;
}
.tooltip {
position: absolute;
left: 0;
top: 0;
box-shadow: 0 0 10px 0 #ccc;
width: 200px;
height: 100px;
border-radius: 4px;
box-sizing: border-box;
padding: 15px 10px;
color: #606060;
opacity: 0;
background-color: #fff;
pointer-events: none;
}
.tooltip>.x {
margin-bottom: 15px;
}
</style>
</head>
<body>
<div id="radial-bar-chart"></div>
<script>
const provinces = [
'北京市',
'天津市',
'上海市',
'重庆市',
'河北省',
'河南省',
'云南省',
'辽宁省',
'辽省',
'',
'',
'',
'',
'',
'',
'',
'',
'',
]
const objectId = 'chart-pie-bar'
const series = [{
"color": "#156dff"
},
{
"color": "#00eaeb"
}]
const PadAngle = Math.PI * 2 / 180;
const width = 800
const height = 600
const innerRadius = 90
const outerRadius = 400
const data = randomData()
const chartColor = 'rgba(7, 92, 214, .7)'
const fontColor = '#606060'
const gridLineColor = 'rgb(210, 214, 218)'
const radiusScale = d3.scaleLinear()
.range([innerRadius, outerRadius - 15])
.domain([0, d3.max(data, d => d.y)])
const arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius)
.padAngle(PadAngle)
const svg = d3.select('#radial-bar-chart')
.append('svg')
.attr('width', width)
.attr('height', height)
.html(`
<style>
.arc-bar {
cursor: pointer;
}
</style>
<filter id="chart-inset-shadow1" x="-50%" y="-50%" width="200%" height="200%">
<feComponentTransfer in="SourceAlpha">
<feFuncA type="table" tableValues="1 0" />
</feComponentTransfer>
<feGaussianBlur stdDeviation="${5}" />
<feOffset dx="0" dy="0" result="offsetblur" />
<feFlood flood-color="${series[0].color}" result="color" />
<feComposite in2="offsetblur" operator="in" />
<feComposite in2="SourceAlpha" operator="in" />
<feMerge>
<feMergeNode in="SourceGraphic" />
<feMergeNode />
</feMerge>
</filter>
<filter id="chart-inset-shadow" width="200%" height="200%" x="-50%" y="-50%">
<feGaussianBlur in="SourceGraphic" result="gass" stdDeviation="${5}" />
<feMerge>
<feMergeNode in="gass" />
</feMerge>
</filter>
<defs>
<linearGradient id="linear-${objectId}" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="${series[1].color}" />
<stop offset="100%" stop-color="${series[0].color}" />
</linearGradient>
</defs>
`)
.append('g')
.attr('transform', `translate(${width / 2}, ${height - 42})`)
drawGrid()
drawChart()
svg.append('g')
.attr('class', 'x-axis')
.call(xAxis)
renderToolTip()
let scale = d3.scaleBand().domain([1,2,3,4]).range([0,100])
function drawChart() {
const pieData = d3.pie().value(d => 10)(data)
const arcs = svg
.append('g')
.attr('class', 'arc-bar')
.attr('transform', 'rotate(-90)')
.selectAll('g')
.data(pieData);
arcs.enter()
.append('clipPath')
.attr('id', d => {
if (d.data.y) {
return `${d.data.y}-${d.index}`
}
})
.append('path');
arcs.enter()
.append('path')
.attr('class', 'arc')
.attr('fill', 'transparent')
.attr("stroke", series[0].color)
.attr('stroke-width', 15)
.attr("filter", `url(#chart-inset-shadow)`)
.attr('clip-path', (d) => {
return `url(#${d.data.y}-${d.index})`
});
arcs.enter()
.append('path')
.attr('class', 'arc')
.attr('fill', 'transparent')
.attr("stroke", `url(#linear-${objectId})`)
.attr('stroke-width', 2)
.attr('clip-path', (d) => {
return `url(#${d.data.y}-${d.index})`
});
arcs.enter()
.selectAll('path')
.attr('d', function (d) {
if (d.data.y > 0) {
arc.outerRadius(radiusScale(0.0001))
return arc(d)
}
})
.transition()
.delay((d, i) => {
return (i % data.length) *500
})
.duration(800)
.ease(d3.easeLinear)
.attr('d', function (d, i) {
if (d.data.y > 0) {
arc.outerRadius(radiusScale(d.data.y))
return arc(d)
}
});
}
function xAxis(g) {
const xData = data.filter(d => d && d.x !== '' && d.x).map(d => d.x)
const interval = Math.PI / (provinces.length)
const xLabel = d3.scaleBand()
.domain(xData)
.range([-0.5 * Math.PI + 1 * interval , 0.5 * Math.PI + 1 * interval])
g.attr('class', 'x-axis')
.attr('text-anchor', 'start')
.attr('dominant-baseline', 'middle')
.attr('stroke', fontColor)
.attr('stroke-width', 1)
.style('font-size', 14)
.style('font-weight', 100)
.call(g => {
g.selectAll('g')
.data(xData)
.enter()
.append('g')
.attr('transform', d => {
if (d) {
const rotateAngle = tsRadian2angle(xLabel(d) - xLabel.bandwidth() * 0 +
PadAngle * 0 + interval * 0) - 90 * 1
return `
rotate(${ (rotateAngle) })
translate(${ outerRadius + 30 }, 0)
`
}
})
.call(g => {
g.append('text')
.text(d => d)
.attr('transform', function (d, i) {
const val =
`rotate(90) translate(${ -this.getBBox().width * 0.5 }, 0)`
return val
})
})
})
}
function drawGrid() {
const gridWrapper = svg.append('g')
.attr('class', 'grid-wrapper')
.attr('fill', 'transparent')
.attr('text-anchor', 'end')
.attr('dominant-baseline', 'middle')
.style('font-size', 12)
.style('font-weight', 500)
const xLineOverflow = 5;
const maxPopulation = d3.max(data, d => d.y) * (outerRadius - innerRadius) / ( outerRadius - innerRadius - xLineOverflow )
const arcCount = Math.ceil(outerRadius / innerRadius);
const unitValue = maxPopulation / (arcCount - 1);
for (let i = 0; i < arcCount; i++) {
const n = Math.round(unitValue* i)
const r = i === 0 ? innerRadius :
(outerRadius - innerRadius - xLineOverflow) / (arcCount - 1) * i + innerRadius
const semicircle = d3.path();
semicircle.arc(
0,
0,
r,
0,
Math.PI,
true
);
gridWrapper.append('path')
.attr('d', semicircle)
.attr('stroke-width', 1)
.attr('stroke', gridLineColor)
gridWrapper.append('text')
.text(n)
.attr('transform', `translate(${-r}, 10)`)
.attr('fill', fontColor)
.style('font-size', 12)
gridWrapper.append('text')
.text(n)
.attr('text-anchor', 'start')
.attr('transform', `translate(${r}, 10)`)
.attr('fill', fontColor)
.style('font-size', 12)
}
const bandWidth = 2 * Math.PI / data.length
for (let i = 0; i < Math.floor(data.length / 2); i++) {
const x1 = innerRadius * Math.sin( bandWidth * i + bandWidth / 2)
const y1 = innerRadius * Math.cos( bandWidth * i + bandWidth / 2)
const x2 = outerRadius * Math.sin( bandWidth * i + bandWidth / 2)
const y2 = outerRadius * Math.cos( bandWidth * i + bandWidth / 2)
gridWrapper.append('line')
.attr('x1', x1)
.attr('y1', y1)
.attr('x2', x2)
.attr('y2', y2)
.attr('transform', 'rotate(-90)')
.attr('stroke', gridLineColor)
.attr('stroke-width', 1)
.attr('stroke-dasharray', '5,5')
}
gridWrapper.append('line')
.attr('x1', 0)
.attr('y1', innerRadius)
.attr('x2', 0)
.attr('y2', outerRadius)
.attr('transform', 'rotate(-90)')
.attr('stroke', gridLineColor)
.attr('stroke-width', 1)
.attr('stroke-dasharray', '5,5')
gridWrapper.append('line')
.attr('x1', 0)
.attr('y1', innerRadius)
.attr('x2', 0)
.attr('y2', outerRadius)
.attr('transform', 'rotate(90)')
.attr('stroke', gridLineColor)
.attr('stroke-width', 1)
.attr('stroke-dasharray', '5,5')
}
function renderToolTip() {
const toolTip = d3.select('#radial-bar-chart')
.append('div')
.attr('class', 'tooltip')
toolTip.html(
`
<div class="x">
</div>
<div class="y">
<span>y</span>
<span class="value"></span>
</div>
`
)
d3.selectAll('.arc')
.on('mouseenter', e => {
console.log(e)
const svg = document.querySelector('svg')
const [x, y] = d3.mouse(svg)
toolTip.style('opacity', 1)
toolTip.style('left', `${ x + 50 }px`)
toolTip.style('top', `${ y + 50 }px`)
toolTip.select('.x').text(e.data.x)
toolTip.select('.y .value').text(e.data.y)
})
svg.on('mouseleave', () => {
toolTip.style('opacity', 0)
})
}
function tsRadian2angle(radian) {
return radian * 180 / Math.PI
}
function randomData() {
var data = []
for (const p of provinces) {
data.push({
x: p,
y:p ? Math.random() * 400 + 100 >>> 0 : 0
})
}
data.sort((a, b) => b.y - a.y)
data = [{"x":"辽宁省","y":493},{"x":"云南省","y":457},{"x":"河北省","y":403},{"x":"天津市","y":325},{"x":"上海市","y":313},{"x":"辽省","y":293},{"x":"北京市","y":253},{"x":"河南省","y":219},{"x":"重庆市","y":177},{"x":"","y":0},{"x":"","y":0},{"x":"","y":0},{"x":"","y":0},{"x":"","y":0},{"x":"","y":0},{"x":"","y":0},{"x":"","y":0},{"x":"","y":0}]
return data
}
</script>
</body>
</html>