一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第21天,点击查看活动详情。
开始绘制
引入D3模块
<!-- 选择器模块 -->
<script src="https://cdn.jsdelivr.net/npm/d3-selection@3"></script>
<!-- 比例尺模块 和 依赖 -->
<script src="https://cdn.jsdelivr.net/npm/d3-array@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-color@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-format@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-interpolate@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-time@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-time-format@4"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-scale@4"></script>
<!-- 坐标轴 -->
<script src="https://cdn.jsdelivr.net/npm/d3-axis@3"></script>
<!-- 形状 -->
<script src="https://cdn.jsdelivr.net/npm/d3-path@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-shape@3"></script>
<!-- 动画 -->
<script src="https://cdn.jsdelivr.net/npm/d3-dispatch@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-ease@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-timer@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-transition@3"></script>
数据
- 数据格式随意,方便后面使用。
var dataArr = [
{
label: '1月',
value: 10.5
},
{
label: '2月',
value: 70.5
},
{
label: '3月',
value: 60.5
},
{
label: '4月',
value: 10.5
},
{
label: '5月',
value: 20.5
},
{
label: '6月',
value: 30.5
}
]
添加画布
- 初始化画布。
var svg = d3
.select('.d3Chart')
.append('svg')
.attr('width', width)
.attr('height', height)
.style('background-color', '#1a3055')
// 组
var chart = svg.append('g').attr('transform', `translate(${margin * 2}, ${margin})`)
创建比例尺
- 根据需求创建比例迟。
// 分段比例尺
// 把X轴长度 分成多段
var xScale = d3
.scaleBand()
.range([0, 400])
.domain(dataArr.map((s) => s.label))
// 线性比例尺
// 把Y轴长度 转换为100
var yScale = d3.scaleLinear().range([400, 0]).domain([0, 100])
绘制坐标轴
- 根据定义好的比例尺,使用
axis模块自动绘制坐标轴。 - 绘制好坐标轴后,在坐标轴组中绘制文本标签。
// 坐标轴
const xAxis = d3.axisBottom(xScale)
chart.append('g').attr('class', 'xAxis').attr('transform', `translate(0, ${400})`).call(xAxis)
const yAxis = d3
.axisLeft()
.scale(yScale)
.tickFormat((d) => {
return d + '%'
})
chart.append('g').attr('transform', 'translate(0, 0)').call(yAxis)
// 标签
d3.select('.xAxis')
.append('text')
.attr('x', 400 / 2 - 12)
.attr('y', 0)
.attr('dy', 45)
.style('font-size', '24px')
.text('日期')
d3.selectAll('.d3Chart text').style('fill', '#fff')
d3.selectAll('.d3Chart line').style('stroke', '#fff')
d3.selectAll('.d3Chart path').style('stroke', '#fff')
绘制折线
- 创建线形状计算方法。
let line = d3
.line()
.x(function (d) {
return xScale(d.label)
})
.y(function (d) {
return yScale(d.value)
})
.curve(d3.curveCardinal) // 曲线效果
d3.line()创建线生成器。.x()、.y()设置线生成器中点X、y的坐标。.curve()设置生成曲线。d3.curveCardinal生成一条三次曲线。
var lineChart = chart.append('g')
- 创建绘制组(
g),线和点都绘制在组中。
// 创建 path 路径 设置 d属性 和 线的位置
lineChart
.selectAll()
.data([dataArr])
.enter()
.append('path')
.attr('class', (d, index) => 'path' + index)
.attr('d', function (d) {
return line(d)
})
.attr('stroke', '#2e6be6')
.attr('fill', 'none')
.attr('transform', `translate(${xScale.bandwidth() / 2}, 0)`)
- 绑定数据。一开始只创建了一条折线的数据,这里创建数组放入折线数据。
- 添加标识符,用于后面操作。
- 使用线生成器,生成线的坐标。
// 在点位置 画圆
lineChart
.selectAll()
.data([dataArr])
.enter()
.append('g')
.attr('class', (d, index) => 'circleG' + index)
.selectAll()
.data((d) => d)
.enter()
.append('circle')
.attr('cx', function (d) {
return xScale(d.label)
})
.attr('cy', function (d) {
return yScale(d.value)
})
.attr('r', 4)
.attr('transform', `translate(${xScale.bandwidth() / 2}, 0)`)
.attr('fill', '#fff')
.attr('stroke', 'rgba(56, 8, 228, .5)')
- 绑定了两次数据。因为点数据是分开,我们把一条折线的点放在一组。方便后面操作。
- 给组设置
class标识,用于分组操作点数据。 - 使用比例尺,获取点的坐标。设置点属性。
添加动画
// stroke-dasharray 虚线长度
// stroke-dashoffset 虚线偏移量
svg
.selectAll('path.path0')
.style('stroke-dasharray', function () {
//
return d3.select(this).node().getTotalLength()
})
.style('stroke-dashoffset', function () {
return d3.select(this).node().getTotalLength()
})
.transition()
.duration(2000)
.ease(d3.easeLinear)
.style('stroke-dashoffset', 0)
d3.select(this).node().getTotalLength()选中当前元素获取元素节点对象。使用SVG节点方法,获取路径总长度。.transition()创建平滑的补间动画。简单理解就是,缓慢的把前面设置的属性,修改为后面设置的属性。.duration()设置动画时长。.ease()设置动画过度函数。d3.easeLinear恒等函数,使用相同的速度播放动画。
- 通过选择器,获取折线
path元素。 - 设置虚线长度和偏移量为本身长度。
- 使用
.transition()创建补间动画,修改虚线偏为0,形成线段移动动画。 - 路径描边动画
//圆点
svg
.selectAll('.circleG0 > circle')
.attr('r', 0)
.style('stroke-width', 0)
.transition()
.duration(300)
.delay(function (d, i) {
return (i * 2000) / 5
})
.ease(d3.easeLinear)
.attr('r', 4)
.style('stroke-width', 3)
.delay()动画延迟时间。
- 通过选择器,获取折线的所有点。
- 计算每个动画的,等待时间完善动画。
- 一个基础折线图就完成了。
- 代码地址