用d3.js画一个柱状图

1. 前言

涉及到的比较重要的点

  • Scale(比例尺)
  • Update、Enter、Exit

2. 比例尺

重点在于比例尺的应用

线性比例尺

domain是定义域,range是值域

可以将定义域放大或者缩写

什么意思呢? 假设我们的数据长度是[0, 1000],但是1000这个高度太高了,放不下

我们就可以将其缩小5倍

var y = d3.scaleLinear()
    .domain([0, 1000])
    .range([0, 200])

y(5) // 1
y(500) // 100
y(1000) // 200

序数比例尺

有时候定义域和值域不一定是连续的,所以可以使用这个

var index = [0, 1, 2, 3, 4];
var color = ["red", "blue", "green", "yellow", "black"];

var ordinal = d3.scaleOrdinal()
        .domain(index)
        .range(color);

ordinal(0); //返回 red
ordinal(2); //返回 green
ordinal(4); //返回 black

3. Update、Enter、Exit

可以看到代码里会反复出现这一段

svg.selectAll("rect")   //选择svg内所有的矩形
    .data(dataset)      //绑定数组
    .enter()            //指定选择集的enter部分
    .append("rect")     //添加足够数量的矩形元素

这段代码出现了一个enter,为什么呢?

这就要讲到d3的机制了

假设在body中有3个p元素,有一个数组[3, 6, 9],现在我们把3个dom元素和我们的js数组绑定到一起

但是有两个个问题

  • 如果数组有五个元素(此时数组长度 > DOM元素数量),那应该怎么映射关系?
  • 如果有5个p元素(此时数组长度 > DOM元素数量),那应该怎么映射关系?

于是就提出了三个概念update, enter, exit

  • dom元素与js数据对应的部分叫做update部分
  • 多余的js数据的部分叫做enter部分(此时d3会建立空dom元素与之对应上)
  • 多余的dom元素的部分叫做exit部分(此时d3会建立空js数据与之对应上)

如下图所示
在这里插入图片描述

2. 代码

const width = 400
const height = 400
const dataset = [50, 43, 120, 87, 99, 167, 142]
const color = d3.schemeCategory10
const padding = {
    top: 20,
    bottom: 20,
    left: 30,
    right: 30
}
const xAxisWidth = width - padding.left - padding.right
const yAxisWidth = height - padding.top - padding.bottom
const rectStep = 35
const rectWidth = 30

// x轴比例尺
const xScale = d3
    .scaleBand()
    .domain(d3.range(dataset.length))
    .range([0, xAxisWidth])
    .padding(0.1)

// y轴比例尺
const yScale = d3
    .scaleLinear()
    .domain([0, d3.max(dataset)])
    .rangeRound([yAxisWidth, 0]) // 注意这里写反是因为Y轴的0需要从下面开始

// x轴
const xAxis = d3.axisBottom(xScale)
// y轴
const yAxis = d3.axisLeft(yScale)

// 插入svg
const svg = d3
    .select('#svg')
    .append('svg')
    .attr('width', width)
    .attr('height', height)

const genRect = obj => {
    obj.attr('width', xScale.bandwidth())
        .attr('height', d => yScale(0) - yScale(d))
        .attr('x', (d, i) => {
            return padding.left + xScale(i)
        })
        .attr('y', (d, i) => {
            return height - padding.bottom - (yScale(0) - yScale(d))
        })
        .attr('fill', (d, i) => {
            return color[i]
        })
}

const genText = obj => {
    obj.attr('fill', '#fff')
        .attr('class', 'number')
        .attr('font-size', '14px')
        .attr('x', (d, i) => {
            return padding.left + xScale(i)
        })
        .attr('y', (d, i) => {
            return height - padding.bottom - (yScale(0) - yScale(d))
        })
        .attr('dy', 20)
        .attr('dx', xScale.bandwidth() / 2)
        .attr('text-anchor', 'middle')
        .text(d => d)
}

const genAxis = (xAxis, yAxis) => {
    const gX = svg
        .append('g')
        .attr(
            'transform',
            `translate(${padding.left}, ${height - padding.bottom})`
        )
    gX.call(xAxis)

    const gY = svg
        .append('g')
        .attr('transform', `translate(${padding.left}, ${padding.top})`)
    gY.call(yAxis)
}

const init = dataset => {
    genRect(
        svg
            .selectAll('rect')
            .data(dataset)
            .enter()
            .append('rect')
    )
    genText(
        svg
            .selectAll('text')
            .data(dataset)
            .enter()
            .append('text')
    )
}

const update = dataset => {
    genRect(svg.selectAll('rect').data(dataset))
    genText(svg.selectAll('text').data(dataset))
}

const __main = () => {
    init(dataset)
    genAxis(xAxis, yAxis)
}

__main()

3. 效果图

在这里插入图片描述

参考文章:
Learning D3.js
从零开始画图表

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值