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>
    )
}
複製代碼

}

相關文章
相關標籤/搜索