D3 二维图表的绘制系列(四)堆叠柱状图

上一篇: 多列柱状图 https://blog.csdn.net/zjw_python/article/details/98205614

下一篇: 横向柱状图 https://blog.csdn.net/zjw_python/article/details/98209333

代码结构和初始化画布的Chart对象介绍,请先看 https://blog.csdn.net/zjw_python/article/details/98182540

本图完整的源码地址: https://github.com/zjw666/D3_demo/tree/master/src/barChart/stackedBarChart

1 图表效果

在这里插入图片描述

2 数据

date,food,transportation,education
Mon,30,40,50
Tue,20,80,100
Wed,20,50,80
Thu,10,30,40
Fri,15,20,35
Sat,10,30,70
Sun,20,50,60

3 关键代码

导入数据

d3.csv('./data.csv', function(d){
    return {
        date: d.date,
        food: +d.food,
        transportation: +d.transportation,
        education: +d.education
    };
}).then(function(data){
.....

配置一些样式参数

/* ----------------------------配置参数------------------------  */
const config = {
        barPadding: 0.15,
        margins: {top: 80, left: 80, bottom: 50, right: 80},
        textColor: 'black',
        gridColor: 'gray',
        tickShowGrid: [60, 120, 180],
        title: '堆叠直方图',
        hoverColor: 'white',
        animateDuration: 1000
    }

尺度转换和布局函数定义,堆叠柱状图使用d3.stack布局,其能自动布局计算,返回各分块上下边界的y

/* ----------------------------尺度转换------------------------  */
    chart.scaleX = d3.scaleBand()
                    .domain(data.map((d) => d.date))
                    .range([0, chart.getBodyWidth()])
                    .padding(config.barPadding);
    
    chart.scaleY = d3.scaleLinear()
                    .domain([0, d3.max(data.map((d) => d.food + d.transportation + d.education))])
                    .range([chart.getBodyHeight(), 0])
    
    chart.stack = d3.stack()
                    .keys(['food', 'transportation', 'education'])
                    .order(d3.stackOrderAscending)
                    .offset(d3.stackOffsetNone);

渲染柱形,经过stack函数处理后,就可以根据返回的布局信息,确定矩形的坐标和高度

/* ----------------------------渲染柱形------------------------  */
    chart.renderBars = function(){

        let groups = chart.body().selectAll('.g')
                        .data(chart.stack(data));
                    
        let bars = groups.enter()
                    .append('g')
                  .merge(groups)
                    .attr('class', (d) => 'g ' + d.key)
                    .attr('fill', (d,i) => chart._colors(i))
                    .selectAll('.bar')
                    .data((d)=>{
                        return d.map((item) => {
                            item.index = d.index;
                            item.name = d.key;
                            return item;
                        });
                    });
            
            groups.exit()
                    .remove();

            bars.enter()
                    .append('rect')
                    .attr('class', 'bar')
                .merge(bars)
                    .attr('x', (d) => chart.scaleX(d.data.date))
                    .attr('y', (d) => chart.scaleY(d[0]))
                    .attr('width', chart.scaleX.bandwidth())
                    .attr('height', 0)
                    .transition().duration(config.animateDuration)
                    .attr('height', (d) => chart.scaleY(d[0]) - chart.scaleY(d[1]))
                    .attr('y', (d) => chart.scaleY(d[1]));
            
            bars.exit()
                    .remove();
    }

之后,渲染坐标轴、文本标签、网格线,其与基础柱状图相似,几乎没有变化

/* ----------------------------渲染坐标轴------------------------  */
    chart.renderX = function(){
        chart.svg().insert('g','.body')
                .attr('transform', 'translate(' + chart.bodyX() + ',' + (chart.bodyY() + chart.getBodyHeight()) + ')')
                .attr('class', 'xAxis')
                .call(d3.axisBottom(chart.scaleX));
    }

    chart.renderY = function(){
        chart.svg().insert('g','.body')
                .attr('transform', 'translate(' + chart.bodyX() + ',' + chart.bodyY() + ')')
                .attr('class', 'yAxis')
                .call(d3.axisLeft(chart.scaleY));
    }

    chart.renderAxis = function(){
        chart.renderX();
        chart.renderY();
    }

    /* ----------------------------渲染文本标签------------------------  */
    chart.renderText = function(){
        d3.select('.xAxis').append('text')
                            .attr('class', 'axisText')
                            .attr('x', chart.getBodyWidth())
                            .attr('y', 0)
                            .attr('fill', config.textColor)
                            .attr('dy', 30)
                            .text('日期');

        d3.select('.yAxis').append('text')
                            .attr('class', 'axisText')
                            .attr('x', 0)
                            .attr('y', 0)
                            .attr('fill', config.textColor)
                            .attr('transform', 'rotate(-90)')
                            .attr('dy', -40)
                            .attr('text-anchor','end')
                            .text('每日支出(元)');
    }

    /* ----------------------------渲染网格线------------------------  */
    chart.renderGrid = function(){
        d3.selectAll('.yAxis .tick')
            .each(function(d, i){
                if (config.tickShowGrid.indexOf(d) > -1){
                    d3.select(this).append('line')
                        .attr('class','grid')
                        .attr('stroke', config.gridColor)
                        .attr('x1', 0)
                        .attr('y1', 0)
                        .attr('x2', chart.getBodyWidth())
                        .attr('y2', 0);
                }
            });
    }

最后绑定鼠标交互事件

/* ----------------------------绑定鼠标交互事件------------------------  */
    chart.addMouseOn = function(){
        //防抖函数
        function debounce(fn, time){
            let timeId = null;
            return function(){
                const context = this;
                const event = d3.event;
                timeId && clearTimeout(timeId)
                timeId = setTimeout(function(){
                    d3.event = event;
                    fn.apply(context, arguments);
                }, time);
            }
        }

        d3.selectAll('.bar')
            .on('mouseover', function(d){
                const e = d3.event;
                const position = d3.mouse(chart.svg().node());

                d3.select(e.target)
                    .attr('fill', config.hoverColor);
                
                chart.svg()
                    .append('text')
                    .classed('tip', true)
                    .attr('x', position[0]+5)
                    .attr('y', position[1])
                    .attr('fill', config.textColor)
                    .text( d.name + ':' + d.data.food + '元');
            })
            .on('mouseleave', function(d){
                const e = d3.event;
                
                d3.select(e.target)
                    .attr('fill', chart._colors(d.index));
                    
                d3.select('.tip').remove();
            })
            .on('mousemove', debounce(function(){
                    const position = d3.mouse(chart.svg().node());
                    d3.select('.tip')
                    .attr('x', position[0]+5)
                    .attr('y', position[1]-5);
                }, 6)
            );
    }

大功告成!!!


如果觉得这篇文章帮助了您,请打赏一个小红包鼓励作者继续创作哦!!!

在这里插入图片描述

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
 你手头有一些数据,想做成漂亮的图表放到网站上?好主意,通过浏览器来跨平台实现数据可视化是正确的选择。什么,你还想让图表能够响应用户操作?没问题,交互式图表比静态片更能吸引人去探究本源。好啦,要生成通过浏览器展示的动态图表,目前热门的Web数据可视化库——D3。   《灵程序设计丛书·数据可视化实战:使用D3设计交互式图表》这本书很有意思,而且对读者要求不高。不需要知道什么是数据可视化,也不用有太多Web开发背景就能看懂它。不信?翻一翻就知道这是一本既好玩又实用的动手指南啦!看完这本书你会怎么样呢?   掌握必要的HTML、CSS、JavaScript和SVG基础知识;   学会基于数据在网页里生成元素和为它们设置样式的技巧;   能够生成条形、散点、饼堆叠条形和力导向;   使用平滑的过渡动画来展示数据的变化;   赋予图表动态交互能力,响应用户从不同角度探索数据的请求;   收集数据和创建自定义的地;   另外,《灵程序设计丛书·数据可视化实战:使用D3设计交互式图表100多个代码示例都可以在线浏览! 【电子版来自互联网,仅供预览及学习交流 使用,不可用于商业用途,如有版权问题,请联系删除,支持正版,喜欢的 请购买正版书籍: https://e.jd.com/30336473.html】
以下是用d3.js画堆叠柱状图的代码示例: ```javascript // 设置数据 var data = [ {category: 'A', value1: 10, value2: 20, value3: 30}, {category: 'B', value1: 15, value2: 25, value3: 35}, {category: 'C', value1: 20, value2: 30, value3: 40}, {category: 'D', value1: 25, value2: 35, value3: 45}, ]; // 设置画布大小和边距 var margin = {top: 20, right: 20, bottom: 30, left: 40}, width = 600 - margin.left - margin.right, height = 400 - margin.top - margin.bottom; // 创建 SVG 元素 var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // 设置 x 和 y 轴的比例尺 var x = d3.scaleBand() .range([0, width]) .padding(0.1) .domain(data.map(function(d) { return d.category; })); var y = d3.scaleLinear() .range([height, 0]) .domain([0, d3.max(data, function(d) { return d.value1 + d.value2 + d.value3; })]); // 定义颜色比例尺 var color = d3.scaleOrdinal() .range(["#98abc5", "#8a89a6", "#7b6888"]); // 定义堆叠函数 var stack = d3.stack() .keys(["value1", "value2", "value3"]) .order(d3.stackOrderNone) .offset(d3.stackOffsetNone); // 堆叠数据 var series = stack(data); // 添加 x 轴 svg.append("g") .attr("class", "axis") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(x)); // 添加 y 轴 svg.append("g") .attr("class", "axis") .call(d3.axisLeft(y).ticks(null, "s")) .append("text") .attr("x", 2) .attr("y", y(y.ticks().pop()) + 0.5) .attr("dy", "0.32em") .attr("fill", "#000") .attr("font-weight", "bold") .attr("text-anchor", "start") .text("Value"); // 添加堆叠柱状图 svg.selectAll("g.layer") .data(series) .enter().append("g") .attr("class", "layer") .style("fill", function(d, i) { return color(i); }) .selectAll("rect") .data(function(d) { return d; }) .enter().append("rect") .attr("x", function(d) { return x(d.data.category); }) .attr("y", function(d) { return y(d[1]); }) .attr("height", function(d) { return y(d[0]) - y(d[1]); }) .attr("width", x.bandwidth()); // 添加例 var legend = svg.selectAll(".legend") .data(color.domain().slice().reverse()) .enter().append("g") .attr("class", "legend") .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; }); legend.append("rect") .attr("x", width - 18) .attr("width", 18) .attr("height", 18) .style("fill", color); legend.append("text") .attr("x", width - 24) .attr("y", 9) .attr("dy", ".35em") .style("text-anchor", "end") .text(function(d) { return d; }); ``` 这段代码会生成一个堆叠柱状图,其中每个类别有三个值,分别用不同的颜色表示。你可以根据需要修改数据和样式。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值