React+d3(基础)

D3的全称是 Data-Driven Documents (简单说意思就是:数据驱动的文档)java

D3是一个javaScript的函数库,是用来作数据可视化的。node

文档指DOM,即文档对象模型(Document Object Model)react

D3容许用户绑定任意数据到DOM,而后根据数据来操做文档,建立可交互式的图表。npm

因为做者自身水平有限D3也是最近才学的,文中不免出现错误,恳请你们批评指正,我这边也会跟进修改的。数据结构

那,直接开始吧

主要为你们说 3个种类的图 app

分别是 1.饼图 2.散点图 3.力学图 dom

1:饼图 svg

↓全↓部↓代↓码↓(后面有拆解)函数

import React from 'react';
import * as d3 from 'd3';
export default class Map extends React.Component{
    constructor(props){
        super(props);
        this.state={}
    }
    componentDidMount(){
        var width = 400;
        var height = 400;
        var dataset = [ 1 , 2 , 3 , 4 , 5 ];
        var svg = d3.select("#body")
            .append("svg")
            .attr("width", width)
            .attr("height", height);
        var pie = d3.layout.pie();
        var piedata = pie(dataset);
        var outerRadius = 150;    //外半径
        var innerRadius = 0;    //内半径,为0则中间没有空白
        var arc = d3.svg.arc()    //弧生成器
            .innerRadius(innerRadius)    //设置内半径
            .outerRadius(outerRadius);    //设置外半径
        var color = d3.scale.category10();
        var arcs = svg.selectAll("g")
            .data(piedata)
            .enter()
            .append("g")
            .attr("transform","translate("+ (width/2) +","+ (width/2) +")");
        arcs.append("path")
            .attr("fill",function(d,i){
                return color(i);
            })
            .attr("d",function(d){
                return arc(d);
            });
        arcs.append("text")
            .attr("transform",function(d){
                return "translate(" + arc.centroid(d) + ")";
            })
            .attr("text-anchor","middle")
            .text(function(d){
                return d.data;
            });
    }

    render(){
        return(
            <div id="body"></div>
        )
    }
}
复制代码

详解(饼图)

首先咱们必须先必须布局

在cmd内
npm install --save d3
以后在咱们react页面头部中
import * as d3 from 'd3';
复制代码

在咱们的componentDidMount中书写代码(由于饼图我这个饼图代码十分简洁/简单

var width = 400;
//定义宽度
var height = 400;
//定义高度
var dataset = [ 1 , 2 , 3 , 4 , 5 ];
//定义饼图内的data数据
var svg = d3.select("#body")//把d3代码生成到#body内
    .append("svg")//添加的svg
    .attr("width", width)//添加的宽度是咱们定义的宽度/高度也是这样
    .attr("height", height);

var pie = d3.layout.pie();
    //构建一个饼图布局,使用默认的方法访问数据,默认不排序。起始弧度为0,结束弧度为2π,返回的layout能够是对象,也能够是函数。
    也就是你能够调用这个函数,这个函数也有额外的方法改变自身的行为,就像其它的d3类同样
    var piedata = pie(dataset);
    var outerRadius = 150;    //外半径
    var innerRadius = 0;    //内半径,为0则中间没有空白

var arc = d3.svg.arc()    //弧生成器
    //建立一个arc生成器,也就是扇形。使用默认的内部半径,外部半径,
    起始角度,结束角度 访问数据的函数。返回的函数生成path数据来造成闭合的solid arc。
    .innerRadius(innerRadius)    //设置内半径
    .outerRadius(outerRadius);    //设置外半径

var color = d3.scale.category10();//颜色比例尺
复制代码

//颜色比例尺

var colors = d3.scale.category10();

console.log(colors.range());

// -> ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"]

var arcs = svg.selectAll("g")
        .data(piedata)
        .enter()
        .append("g")
        .attr("transform","translate("+ (width/2) +","+ (width/2) +")");
arcs.append("path")
        .attr("fill",function(d,i){
            return color(i);
        })
        .attr("d",function(d){
            return arc(d);
        });
arcs.append("text")
        .attr("transform",function(d){
            return "translate(" + arc.centroid(d) + ")";
        })
        .attr("text-anchor","middle")
        .text(function(d){
            return d.data;
        });        
复制代码

详解(散点图)

↓全↓部↓代↓码↓(后面有拆解)

import React from 'react';
import * as d3 from 'd3';
export default class Index extends React.Component{
constructor(props){
    super(props)
    this.state={}
}
componentDidMount(){
    //圆心数据
    var center = [
        [0.5,0.5],[0.7,0.8],[0.4,0.9],
        [0.11,0.32],[0.88,0.25],[0.75,0.12],
        [0.5,0.1],[0.2,0.3],[0.4,0.1],[0.6,0.7]
    ]
    //定义一个svg的绘制区域。
    var width = 600; //svg绘制区域的宽度
    var height = 500; //svg绘制区域的高度
    var svg = d3.select("#body")   //选择id为body的div
    .append("svg")   //在<body>中添加<avg>
    .attr("width",width)  //设定<svg>的宽度属性
    .attr("height",height)  //设定<svg>的高度属性
    
    //定义比例尺
    //x轴宽度
    var xAxisWidth = 300;
    //y轴宽度
    var yAxisWidth = 300;
    //x轴比例尺
    var xScale = d3.scale.linear()    //建立一个线性比例尺
    .domain([0,1.2*d3.max(center,function(d){ //设定定义域
    return d[0]
    })])
    .range([0,xAxisWidth])   //设定值域
    //y轴比例尺
    var yScale = d3.scale.linear()    //建立一个线性比例尺
    .domain([0,1.2*d3.max(center,function(d){ //设定定义域
    return d[1]
    })])
    .range([0,yAxisWidth])   //设定值域

    //在svg中绘制图形,先绘制圆
    //外边框
    var padding = {top:30,right:30,bottom:100,left:100};
    //绘制圆
    var circle = svg.selectAll("circle")
    .data(center)   //绑定数据
    .enter()   //获取enter部分
    .append("circle")  //
    
    .attr("fill","goldEnrod") //设置颜色
    .attr("cx",function(d){  //设置圆心的x坐标
        return padding.left + xScale(d[0])
    })
    .attr("cy",function(d){  //设置圆心的y坐标
        return height-padding.bottom-yScale(d[1])
    })
    .attr("r",5)   //设置圆的半径

    //定义坐标轴
    //x轴
    var xAxis = d3.svg.axis()   //建立一个默认的新坐标轴
    .scale(xScale)   //设定坐标轴的比例尺
    .orient("bottom")  //设定坐标轴的方向


    yScale.range([yAxisWidth,0])   //从新设置y轴比例尺的值域,与原来的相反

    //y轴
    var yAxis = d3.svg.axis()   //建立一个默认的新坐标轴
    .scale(yScale)   //设定坐标轴的比例尺
    .orient("left")   //设定坐标轴的方向

    //添加x轴和平移
    svg.append("g")     //在svg中添加一个包含坐标轴各元素的g元素
    .attr("class","axis")   //定义class名
    .attr("transform","translate("+padding.left+","+(height-padding.bottom)+")") //将x轴进行平移
    .call(xAxis)    //将自身做为参数传递给xAxis函数

    //设置y轴和平移
    svg.append("g")     //在svg中添加一个包含坐标轴各元素的g元素
    .attr("class","axis")   //定义class名
    .attr("transform","translate("+padding.left+","+(height-padding.bottom-yAxisWidth+")")) //将y轴进行平移
    .call(yAxis)
}

render(){
    return(
        <div id="body" ></div>
    )
}
}
复制代码

详解(力学图)

↓全↓部↓代↓码↓(后面有拆解)

import React from "react"
import * as d3 from 'd3';
export default class Main extends React.Component {
    constructor(props) {
        super(props);
        this.state={}
    }
componentDidMount(){   
    var that = this;
    this.linkList = [];//连线数据
    this.nodeList = [];//节点数据
    this.linkTemp = [];
    this.width = 1000,
    this.height = 500,   // svg的宽和高
    this.xDistance = 100,
    this.yDistance = 100;     // x轴方向节点间距。y轴方向加点间距
    this.colorPalette = ["#1836A0", "#00A4FF"]    // 色板
    this.rectWidth = 70;
    this.paddingLeft = 25 + this.rectWidth / 2; // d3 svg 图距离左侧边框的距离
    this.svg = d3
        .select("#dthree")
        .append("svg")
        .attr('id','svg')
        .attr("width", this.width)
        .attr("height", this.height);
    let data={
        "code":1,
        "message":"success",
        data:[
            {
                "source":"ESRBMD",
                "target":"QPWWIH"
            },
            {
                "source": "ESRBMD",
                "target": "APWWIH"
            },
            {
                "source": "ESRBMD",
                "target": "BPWWIH"
            },
            {
                "source": "ESRBMD",
                "target": "CPWWIH"
            },
            {
                "source": "PZ9SPU",
                "target": "9EWIUJ"
            },
            {
                "source":     "PZ9SPU",
                "target": "YO9EVH"
            },
            {
                "source": "QPWWIH",
                "target": "PZ9SPU"
            }
        ]
    }
    that.linkList = data.data;
    that.linkTemp = _.clone(that.linkList)//此处理解为替版
    //获取节点标签列表
    that.linkList.forEach( function (item,index){
        that.nodeList.push(item.source)
        that.nodeList.push(item.target)
    })//循环判断并放入nodeList
    that.nodeList = _.uniq(that.nodeList)//此处去重
    //修改数据结构
    that.nodeList.forEach( function (item,index) {
        that.nodeList[index] = { "label":item,"index":index}
    })//转换成{label: "ESRBMD", index: 0}一类
    that.generateNodesList(null)
    // 生成符合d3力布局接口的连线数据
    that.generateLinkList()
    //生成图
    that.updateGraph(that.nodeList , that.linkList)
}

generateNodesList(parentnode) {
    var that=this;
    if(!parentnode){//第一次调用的时候,父节点null,须要找出第一层的界点.
        //找第一层的界点
        var firstNode = this.getFirstNode()
        if(firstNode){
            //迭代
            this.generateNodesList(firstNode)
        }
        else{
            console.log('Data error: cannot find first node.')
        }
    }else if(this.linkTemp.length > 0){//指定了父节点时,若是 linktemp空了,表示已完成便利.
        let countSubsequence = 0//用于表示同级的节点垂直方向的顺序
        let links = _.clone(this.linkTemp)
        links.forEach( function (item){
            if(item.source === parentnode.label){
                let node = null
                countSubsequence++;
                //从linksTemp中移除已便利的数据
                _.remove(that.linkTemp,function(n){
                    return n.source == item.source && n.target == item.target
                })
                //更新子节点的层级和坐标
                node = that.updateNodeLevel(item.target,parentnode.level + 1,countSubsequence)
                that.generateNodesList(node)
            }
        })
    }else{
        console.log("Generated nodes finished.")
    }
}

getFirstNode(){
    var that=this;
    let sources = [];
    let targets = [];
    let firstNodeLabel = ""
    let firstNode = null
    that.linkList.forEach( function (item){
        sources.push(item.source)
    })
    that.linkList.forEach( function (item){
        targets.push(item.target)
    })
    //找出在source中但不在target中的界点,即为第一层级的界点
    sources=_.uniq(sources)
    sources.forEach(function (value,i){
        if(targets.indexOf(value)==-1){
            firstNodeLabel = value
            return false
        }
    })
    // console.log('第一层:'+firstNodeLabel)
    //根据节点名称.更新第一层级节点的数据
    that.nodeList.forEach(function (item,i){
        if(item.label === firstNodeLabel){
            that.nodeList[i].level=1
            that.nodeList[i].x = that.paddingLeft
            that.nodeList[i].y = that.yDistance
            firstNode = that.nodeList[i];
            return false
        }
    })
    return firstNode
}

/**
 * 更新指定名称的节点的层级,和坐标
 * @param {*} label 指定的节点名称
 * @param {*} colIndex 列索引
 * @param {*} rowIndex 行索引
 */
updateNodeLevel(label,colIndex,rowIndex){
    var that=this;
    let node=null
    this.nodeList.forEach(function(item,i){
        if(item.label===label){
            that.nodeList[i].level = colIndex
            that.nodeList[i].x = that.xDistance * (colIndex-1) + that.paddingLeft
            that.nodeList[i].y = that.yDistance * rowIndex
            node = that.nodeList[i]
            return false    //跳出遍历
        }
    })
    return node
}

generateLinkList(){
    var that=this;
    that.nodeList.forEach(function(n){
        that.linkList.forEach(function(l,j){
            if(l.source===n.label){
                that.linkList[j].source=n.index
            }
            if(l.target===n.label){
                that.linkList[j].target=n.index
            }
        })
    })
    console.log("Generated link finished.")
}

updateGraph(nodes, links){
    var that=this;
    //初始化力布局
    let force = d3.layout
        .force()
        .size([this.width,this.height]);
    let link=that.svg.selectAll('.link');
    let node=that.svg.selectAll('.node');
    force
        .nodes(nodes)
        .links(links)
        .start();
    link = link
        .data(links)
        .enter()
        .append("line")
        .attr("class","link")
        .attr("stroke","#CBD0D3")
        .attr("strokeWidth","1px")
        //copy
        .attr("x1", function (d) {
            return d.source.x;
        })
        .attr("y1", function (d) {
            return d.source.y;
        })
        .attr("x2", function (d) {
            return d.target.x;
        })
        .attr("y2", function (d) {
            return d.target.y;
        });
    var nodecontainer = node.data(nodes)
        .enter().append("g")
        .attr({
            "class": "nodes",
            "cx": function (d) {
                return d.x;
            },
            "cy": function (d) {
                return d.y;
            }
        });        
    
    // 节点方块
    node = nodecontainer.append("rect")
        .attr({ // 位置。大小
            "class": "square",
            "x": function (d) {
                return d.x - that.rectWidth/2;
            },
            "y": function (d) {
                return d.y - 12;
            },
            "width": that.rectWidth,
            "height": 24,
            "rx":"4",
            "ry":"4"
        })
        .attr("fill", function (d) {    // 填充色
            return that.colorPalette[(d.level - 1) % that.colorPalette.length]
        })
        .on("click", this.onClickNode);  // 监听 click 事件    
    // 节点文字
    let text = nodecontainer
        .append('text')
        .attr({
            "text-anchor": "middle",
            "fill": "#ffffff",
            "class": "text",
            "x": function (d) {
                return d.x;
            },
            "y": function (d) {
                return d.y + 5;
            },
        })
        .text(function (d) { return d.label })
        .on("click", this.onClickNode);        
}

onClickNode(d) {
    console.log("You just select node " + d.label)
}

render() {
    return (
        <div id="dthree" ref='dthree'></div>
    )
}
复制代码

}

相关文章
相关标签/搜索