React+Dva使用d3绘图

本文详细介绍了如何在React和Dva的环境下使用D3.js进行数据可视化。首先,通过Dva获取数据,然后在组件中导入并使用D3绘制图表,包括设置缩放、坐标轴等。同时,提供了下拉框选择排序方式的功能,通过改变排序算法动态更新图表。代码示例清晰,适合有一定D3和React基础的开发者参考。
摘要由CSDN通过智能技术生成

React+Dva环境中使用d3绘图

本文以D3中的Example为例,将其导入本项目中,项目基于React和Dva,作者假设读者已经能够通过Dva异步获取D3所需的数据。
文件代码结构如下:

// index.jsx
import { connect } from 'dva';
import React, { Component } from 'react';
import * as d3 from "d3";
class index extends Component {
	// 构造函数
    constructor(props) {
        super(props)
    }
    // 使用D3js绘制
    print = (data) => {}
    // 处理下拉框事件响应
    handleSelect({ target }) {}
    // 绘制函数
    render() {}
}
// --------------------------------------------------------------- 
// 下面通过Dva的connect方法将视图与数据绑定,因此在index中可以通过props来获取
// mapStateToProps中的数据,即 const { alphabet } = this.props;
const mapStateToProps = state => {
    return {
        alphabet: state.demoPage.alphabet
    };
};
const mapStateToDispatch = dispatch => ({});

export default connect(mapStateToProps, mapStateToDispatch)(index);
    

其中print方法是绘图的核心部分,参考D3例子,直接将代码Copy进来即可。需要注意的是官网例子中chart = {}需要改写成chart = () => {} 方法,变量名前加上const或者let声明。print方法如下:

print = (data) => {
    if (!data) {
    	// 数据为空时,需要返回空div
        return document.createElement('div')
    }
    // 在Dva中的effects里通过异步请求已经拿到了数据,因此这里只需要将数组转为json即可
    const _data = data.map(line => ({ name: line[0], value: line[1] }))
    const margin = ({ top: 20, right: 0, bottom: 30, left: 40 }),
        height = 150,
        width = 300
	// 注意假设变量声明标识符
    const yAxis = g => g
        .attr("transform", `translate(${margin.left},0)`)
        .call(d3.axisLeft(y))
        .call(g => g.select(".domain").remove())

    const xAxis = g => g
        .attr("transform", `translate(0,${height - margin.bottom})`)
        .call(d3.axisBottom(x).tickSizeOuter(0))

    const y = d3.scaleLinear()
        .domain([0, d3.max(_data, d => d.value)]).nice()
        .range([height - margin.bottom, margin.top])

    const x = d3.scaleBand()
        .domain(_data.map(d => d.name))
        .range([margin.left, width - margin.right])
        .padding(0.1)

    const chart = () => {
        const svg = d3.create("svg")
            .attr("viewBox", [0, 0, width, height]);

        const bar = svg.append("g")
            .attr("fill", "steelblue")
            .selectAll("rect")
            .data(_data)
            .join("rect")
            .style("mix-blend-mode", "multiply")
            .attr("x", d => x(d.name))
            .attr("y", d => y(d.value))
            .attr("height", d => y(0) - y(d.value))
            .attr("width", x.bandwidth());

        const gx = svg.append("g")
            .call(xAxis);

        const gy = svg.append("g")
            .call(yAxis);

        return Object.assign(svg.node(), {
            update(order) {
                x.domain(_data.sort(order).map(d => d.name));

                const t = svg.transition()
                    .duration(750);

                bar.data(_data, d => d.name)
                    .order()
                    .transition(t)
                    .delay((d, i) => i * 20)
                    .attr("x", d => x(d.name));

                gx.transition(t)
                    .call(xAxis)
                    .selectAll(".tick")
                    .delay((d, i) => i * 20);
            }
        });
    }
    return chart()
}

可以看出代码除了少部分改动外,与官网案例区别不大。

接下来在构造函数中添加下拉框选项及order函数

constructor(props) {
    super(props)
    this.options = [
        { label: "Alphabetical", value: (a, b) => a.name.localeCompare(b.name) },
        { label: "Frequency, ascending", value: (a, b) => a.value - b.value },
        { label: "Frequency, descending", value: (a, b) => b.value - a.value }
    ]
}

handleSelect方法设置选择响应事件,即调用print方法返回的对象中的updata方法,参数为排序算法order

handleSelect({ target }) {
    this.chartEl.update(
        this.options.filter(op => target.value === op.label)[0].value)
}

最后实现render方法,注意ref={(ref) => { ref && ref.appendChild(chartEl) }}chartEl作为子元素插入到了div中,而不是使用JSX插入。

render() {
    const { alphabet } = this.props;
    const chartEl = this.chartEl = this.print(alphabet)
    return (
        <div>
            <select onChange={this.handleSelect.bind(this)}>
                {
                    this.options.map(op => (<option key={op.label} value={op.label}>{op.label}</option>))
                }
            </select>
            <div ref={(ref) => { ref && ref.appendChild(chartEl) }}></div>
        </div>
    )
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值