携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情
引入D3模块
- 引入整个D3模块。
<!-- D3模块 -->
<script src="https://d3js.org/d3.v7.min.js"></script>
数据
- 仪表盘只需要传入一个值。
const data = 95
添加画布
- 初始化画布。
// 画布
const width = 500
const height = 500
const margin = 30
const svg = d3
.select('.d3Chart')
.append('svg')
.attr('width', width)
.attr('height', height)
.style('background-color', '#1a3055')
// 图
const chart = svg.append('g')
比例尺和配置信息
const colorScale = d3.scaleOrdinal(d3.schemeSet3)
- 颜色比例尺。
const radius = d3.min([width - 4 * margin, height - 4 * margin]) / 2
- 仪表盘半径。
// 分段值
const nums = [0, 50, 90, 100]
const numLength = 100 // 仪表盘总长度
const anglelength = 280 // 仪表盘 度数
let angleDraw = []
nums.forEach((item, i) => {
if (i !== nums.length - 1) {
const angleS = (item / numLength) * anglelength
const angleE = (nums[i + 1] / numLength) * anglelength
angleDraw.push({
startAngle: (angleS - anglelength / 2) * (Math.PI / 180),
endAngle: (angleE - anglelength / 2) * (Math.PI / 180)
})
}
})
- 设置分段值,计算出仪表盘的分段的弧度值。
let innerArc = 25 // 仪表盘 条形宽度
- 仪表盘宽度。
绘制仪表盘弧形
// 创建 扇形绘制器
const arc = d3
.arc()
.outerRadius(radius)
.innerRadius(radius - innerArc)
- 创建扇形绘制器,设置内半径(
.innerRadius
)值为半径减去仪表盘宽度。只绘制最外层的一条。
chart
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')')
.selectAll()
.data(angleDraw)
.enter()
.append('path')
.attr('class', (d, i) => ' arc-' + i)
.attr('d', arc)
.attr('fill', (d, i) => colorScale(i))
- 绑定弧形分段信息,分段绘制出一个仪表盘弧形。
const kedu = []
for (let i = -anglelength / 2; i <= anglelength / 2; i += anglelength / 100) {
kedu.push((i * Math.PI) / 180)
}
- 使用和上面相同的弧度计算方法并分成100份,得到100个刻度的弧度的位置。
const ticks = chart
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')')
.selectAll()
.data(kedu)
.enter()
.append('g')
.attr('class', 'ticks')
.each(drawTicks)
.each(drawLabels)
function drawTicks(d, i) {
if (i === 0 || i === 100) return
const innerRadius = i % 10 === 0 ? radius - innerArc : radius - innerArc / 3
d3.select(this)
.append('line')
.attr('stroke', '#000000')
.attr('x1', Math.sin(d) * radius)
.attr('y1', -Math.cos(d) * radius)
.attr('x2', Math.sin(d) * innerRadius)
.attr('y2', -Math.cos(d) * innerRadius)
}
function drawLabels(d, i) {
let textAnchor = 'end'
if (i === 50) {
textAnchor = 'middle'
}
if (i % 10 === 0) {
const textRadius = radius - innerArc - 15
d3.select(this)
.append('text')
.attr('class', 'label')
.attr('x', Math.sin(d) * textRadius)
.attr('y', -Math.cos(d) * textRadius)
.attr('dy', 5.5)
.attr('stroke', '#ffffff')
.attr('text-anchor', d < -0.01 ? 'start' : textAnchor)
.text((i / 100) * 100)
}
}
.each()
对集合中的单个元素依次进行操作,并获取DOM元素的上下文。
- 绑定刻度信息,为每个刻度创建绘制组
- 因为绘制刻度和标签都需要进行一些计算不能直接绘制。
- 使用
.each()
获取DOM元素,定制在不同的刻度下进行不同的绘制。
绘制指针
// 指针位置点 计算
function points() {
// 顶点
const upper = Math.floor(radius - innerArc - 50)
// 两边 中间点
const short = Math.floor(upper * 0.13)
// 两边点
const both = Math.floor(short * 0.6)
return ['0,' + short, both + ',0', '0,' + -upper, -both + ',0']
}
const pointer = chart
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')')
.selectAll()
.data([data])
.enter()
.append('polygon')
.attr('class', 'pointer')
.attr('points', points)
.attr('fill', colorScale(0))
.attr('transform', 'rotate(' + -0.5 * anglelength + ')')
- 使用多边形绘制指针,先计算出指针个点坐标位置,通过旋转修改指针指向 0 点。
pointer.transition().duration(1000).attrTween('transform', rotateTween).attrTween('fill', fillTween)
function rotateTween(d) {
return function (t) {
return 'rotate(' + ((d * t) / numLength - 0.5) * anglelength + ')'
}
}
function fillTween(d) {
return function (t) {
let i = 0
while (i < nums.length - 1 && nums[i] < d * t) {
i++
}
return colorScale(i - 1)
}
}
- 通过
.attrTween()
制作指针旋转动画和根据刻度修改指针颜色的动画。