其他分享
首页 > 其他分享> > d3.js data-join(03)

d3.js data-join(03)

作者:互联网

前言:本篇主要是d3.js里面的datajoin

一、什么是Data-join?

本质上是将数据与图元进行绑定

例如:

为什么要使用data-join?

二、用函数设置图元属性

selection.attr('attrbuteName',value)

selection.attr('attrbuteName',(d,i) = >{...})

三、数据与图元的绑定

data(dataArray)

(1)dataArray在保证是一个数组的前提下可以是任何形式

(2)先考虑数据和图元数目相同的情况

四、d3.jd绑定数据的三个状态

在这里插入图片描述

Updata:图元和数据条目相同

Enter:数据的条目多于图元甚至没有图元,常用于第一次绑定数据

Exit:数据的条目少于图元甚至没有数据,常用于结束可视化

4.1Updata

有图元有数据

const p = maingroup.selectAll('.dataourve').data(data).attr(...).attr(...)
  1. Updata作为实际可视化任务最常用的状态,经常被单独封装成一个函数
  2. UpdataSelection.merge(enterSelection).attr(…).attr(…)
    • 将两个Selection合并到一起操作
    • enterSelection在与updataSelectio n merge之前要至少已经调用了append(…)语句添加好图元。
  3. Updata经常与d3.js的动画一起使用

4.2Enter

有数据没图元,d3.jd会自动搞清楚哪些数据是新增的,根据新增的数据生成相应的图元。

生成图元的占位,占位内容需要编程者自行添加(append)

const p = maingroup.selectAll('.class').data(data).enter().append('').attr(...)

enter本质上生成指向父节点的指针,而append操作相当于在父节点后添加指针数量的图元并将其余多出的数据绑定。

4.3Exit

有图元没有数据,d3.js会自动“搞清楚”哪些图元是不绑定数据的

const p= maingroup.selectAll('.class').data(data).exit().remove()

4.4data-join的简洁形式

五、让数据动起来

Updata经常与d3,js的动画一起使用

.transition().duration();
d3.selectAll('rect').data(data2,d=>d.name)
.transition().duration(3000).attr('width',d=>xScale(d.age))
//duration(...)中为毫秒

六、关于Key

.data(data,keyFunction)

在绑定数据给图元时:

如果图元之前没有绑定任何数据,则keyFunction会报错

七、数据的读取

常见的是CSV数据。第一行为属性列表,后续每一行对应一条数据,CSV本质上是纯文本,区别于EXCEL的格式

d3.csv('path/to/data.csv').then(data=>{...})

八、一个栗子(画动态散点图)

数据要从服务器端获取,还涉及到跨域的问题。目前bug还没完全找出来

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>用d3的data-join散点图</title>
    <!-- <script src="./data/hubeinxt.csv"></script> -->
    <script src='https://d3js.org/d3.v5.min.js'></script>
</head>
<body style="text-align: center;">
    <svg width="1650" height="920" id="mainsvg" class="svgs" style="background-color: #ffffff;"></svg>
    <script>
        const svg = d3.select('#mainsvg');
        const width = +svg.attr('width');
        const height = +svg.attr('height');
        const margin = {top:100,right:120,bottom:100,left:120};
        const innerWidth = width - margin.left - margin.right;
        const innerHeight = height - margin.top - margin.bottom;

        const xValue = d=>{return Math.log(d['确诊人数'] +1)};
        const yValue = d=>{return Math.log([d['新增确诊'] +1 ])};
        const rValue = d=>{return Math.log(d['感染率']*500)*0.8};
        const keyHint = '地区';
        let xScale,yScale;
        let maxX,maxY;
        let datas;
        let aduration = 1000;
        let metapop;

        const xAxisLabel = '累计确诊人数(对数)';
        const yAxisLabel = '新增人数(对数)';

        var color = {
            "武汉":"#ff1c12",
            "黄石": "#de5991",
            "十堰": "#759AA0",
            "荆州": "#E69D87",
            "宜昌": "#be3259",
            "襄阳": "#EA7E53",
            "鄂州": "#EEDD78",
            "荆门": "#9359b1",
            "孝感": "#47c0d4",
            "黄冈": "#F49F42",
            "咸宁": "#AA312C",
            "恩施州": "#B35E45",
            "随州": "#4B8E6F",
            "仙桃": "#ff8603",
            "天门": "#ffde1d",
            "潜江": "#1e9d95",
            "神农架": "#7289AB"
        }

        const renderinit = function(data,seq){
            xScale = d3.scaleLinear()
            .domain(d3.extent(data,xValue))
            .range([0,innerWeight])
            .nice();

            yScale = d3.scaleLinear()
            .domain(d3.extent(data,yValue).reverse())
            .range([0,innerHeight])
            .nice();

            maxX = xScale(d3.max(data,xValue));
            maxY = yScale(d3.max(data,yValue));
            //添加组
            const g = svg.append('g')
            .attr('transform',`translate(${margin.left},${margin.top})`)
            .attr('id','maingroup');

            const yAxis = d3.axisLeft(yScale)
            .tickSize(-innerWidth)
            .tickPadding(10);
            const xAxis = d3.axisBottom(xScale)
            .tickSize(-innerHeight)
            .tickPadding(10);

            let yAxisGroup = g.append('g').call(yAxis)
            .attr('id','yaxis');
            yAxisGroup.append('text')
            .attr('font-size','2em')
            .attr('transform',`rotate(-90)`)
            .attr('x',-innerHeight/2)
            .attr('y',-60)
            .attr('fill','#333333')
            .text(yAxisLabel)
            .attr('text-anchor','middle')
            yAxisGroup.selectAll('.domain').remove();

            let xAxisGroup = g.append('g').call(xAxis)
            .attr('transform', `translate(${0}, ${innerHeight})`)
            .attr('id', 'xaxis');
            xAxisGroup.append('text')
            .attr('font-size', '2em')
            .attr('y', 60)
            .attr('x', innerWidth / 2)
            .attr('fill', '#333333')
            .text(xAxisLabel);
            xAxisGroup.selectAll('.domain').remove();

            var legend_color = [
                "#ff1c12",
                "#de5991",
                "#759AA0",
                "#E69D87",
                "#be3259",
                "#EA7E53",
                "#EEDD78",
                "#9359b1",
                "#47c0d4",
                "#F49F42",
                "#AA312C",
                "#B35E45",
                "#4B8E6F",
                "#ff8603",
                "#ffde1d",
                "#1e9d95",
                "#7289AB"
            ]

            var legend_name = ["武汉市",
                "黄石市",
                "十堰市",
                "荆州市",
                "宜昌市",
                "襄阳市",
                "鄂州市",
                "荆门市",
                "孝感市",
                "黄冈市",
                "咸宁市",
                "恩施州",
                "随州市",
                "仙桃市",
                "天门市",
                "潜江市",
                "神农架",
            ];

            //下面是复制粘贴的
            var legend = d3.select('#maingroup').selectAll(".legend")
                .data(legend_name)
                .enter().append("g")
                .attr("class", "legend")
                .attr("transform", function(d, i) { return "translate(" + (innerWidth + 10) + "," + (i * 25 + 300) + ")"; });
			
            // draw legend colored rectangles
            legend.append("rect")
            .data(legend_name) 
            .attr("x", 0)
            .attr("y", 0)
            .attr("width", 30)
            .attr("height", 20)
            .style("fill", function (d,i) { 
                return legend_color[i];});
			
            // draw legend text
            legend.append("text")
            .data(legend_name) 
            .attr('class', 'legend_text')
            .attr("x", 40)
            .attr("y", 9)
            .attr("dy", ".5em")
            .style("text-anchor", "start")
            .text(function (d,i) {return legend_name[i];}); 
        };

        const renderupdate = function(seq){
            const g = d3.select('#maingroup');
            time = seq[0]['日期'];
            g.selectAll('.date_text').remove();
            g.append("text")
            .data(['seq']) 
            .attr('class', 'date_text')
            .attr("x", innerWidth / 4 + 30)
            .attr("y", innerHeight / 10 - 20)
            .attr("dy", ".5em")
            .style("text-anchor", "end")
            .attr("fill", "#504f4f")
            .attr('font-size', '6em')
            .attr('font-weight', 'bold')
            .text(time);

            circleupdates = g.selectAll('circle').data(seq, d => d[keyHint]);
            circleenter = circleupdates.enter().append('circle')
            .attr('cy', (datum) => { return yScale(yValue(datum)) })
            .attr('cx', (datum) => { return xScale(xValue(datum)) }) // use xSacle to re-scale data space (domain) and return the rescaled population; 
            .attr('r', datum => rValue(datum))
            .attr('fill', function(d,i) { return color[d[keyHint]] })
            .attr('opacity', .8);
            circleupdates.merge(circleenter).transition().ease(d3.easeLinear).duration(aduration)
            .attr('cy', (datum) => { return yScale(yValue(datum)) })
            .attr('cx', (datum) => { return xScale(xValue(datum)) }) // use xSacle to re-scale data space (domain) and return the rescaled population; 
            .attr('r', datum => rValue(datum));

            textupdates = g.selectAll('.province_text').data(seq);
            textenter = textupdates.enter().append('text')
            .attr("class", "province_text")
            .attr("x", (datum) => { return xScale(xValue(datum)); })
            .attr("y", (datum) => { return yScale(yValue(datum)); })
            .attr("dy", "1em")
            .style("text-anchor", "middle")
            .attr("fill", "#333333")
            //.attr('opacity', 0)
            .text(function (d,i) { 
                return d[keyHint];
            });

            textupdates.merge(textenter).transition().ease(d3.easeLinear).duration(aduration)
            .attr('x', (datum) => { 
                return xScale(xValue(datum)); })
            .attr('y', (datum) => { return yScale(yValue(datum)); });
        };

        d3.csv('hubeipop.csv').then(data => {
            data.forEach(datum => {
                datum['人口(万人)'] = +(datum['人口(万人)']);
            })
            metapop = data;
        });

        d3.csv('hubei_day14.csv').then(function(data){
            data = data.filter(datum => {return datum[keyHint] !== '总计'});
            data.forEach(datum => {
                // pre-process the data; 
                datum['确诊人数'] = +(datum['确诊人数']);
                datum['治愈人数'] = +(datum['治愈人数']);
                datum['死亡人数'] = +(datum['死亡人数']);
                datum['新增确诊'] = +(datum['新增确诊']);
                if(datum['新增确诊'] < 0){
                    datum['新增确诊'] = 0;
                }
                datum['感染率'] = datum['确诊人数'] / 
                (metapop.find(x => x[keyHint] === datum[keyHint])['人口(万人)']);
            });

            // remove duplicated items; 
            alldates = Array.from(new Set(data.map( datum => datum['日期'])));

            // make sure dates are listed according to real time order; 
            alldates = alldates.sort(function(a,b){
                return new Date(b.date) - new Date(a.date);
            });
            dates = alldates;

            // re-arrange the data sequentially; 
            sequential = []; 
            alldates.forEach(datum => {
                sequential.push([]);
            });
            data.forEach(datum => {
                sequential[alldates.indexOf(datum['日期'])].push(datum);
            });

            renderinit(data, sequential[0]);

            // set the animation interval; 
            let c = 0; 
            intervalId = setInterval(function(){
                if(c >= alldates.length){
                    console.log('time to close this animation');
                    clearInterval(intervalId); 
                }else{
                    renderupdate(sequential[c]);
                    c = c + 1;
                }
            }, aduration); 
        });
    </script>
</body>
</html>

标签:join,attr,text,datum,图元,data,d3
来源: https://blog.csdn.net/weixin_48931875/article/details/113139419