D3 二维图表的绘制系列(十八)漏斗图

上一篇: 树图 https://blog.csdn.net/zjw_python/article/details/98492510

下一篇: 封闭图 https://blog.csdn.net/zjw_python/article/details/98591118

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

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

1 图表效果

在这里插入图片描述

2 数据

action,number
访问,60
咨询,40
订单,20
点击,80
展现,100

3 关键代码

导入数据

d3.csv('./data.csv', function(d){
    return {
        action: d.action,
        number: +d.number
    };
}).then(function(data){
....

一些样式配置参数,例如梯形间距等

const config = {
        margins: {top: 80, left: 80, bottom: 50, right: 80},
        textColor: 'black',
        title: '基础漏斗图',
        animateDuration: 1000,
        trapezoidPadding: 3,
        hoverColor: 'white',
    }

尺度转换,这里根据数据数值大小来决定漏斗图梯形的宽度

/* ----------------------------尺度转换------------------------  */
    chart.scale = d3.scaleLinear()
                        .domain([0, d3.max(data, (d) => d.number)])
                        .range([0, chart.getBodyWidth()*0.8]);

处理数据,转换数据结构,方便绘制

/* ----------------------------数据处理------------------------  */
    const handleData = data.sort((a,b) => b.number - a.number).map(
        (d,i,array) => {
            if (i !== array.length-1){
                d.nextNum = array[i+1].number;
            }else{
                d.nextNum = 0;
            }
            return d;
        }
    );

漏斗图可以看作由一个个梯形组成,也就是多边形,因此我们可以使用polygon元素来绘制漏斗图

/* ----------------------------渲染梯形------------------------  */
    chart.renderTrapezoid = function(){

        let trapezoids = chart.body()
                                .append('g')
                                .attr('class', 'traps')
                                .attr('transform', 'translate(' + chart.getBodyWidth()/2 + ',0)')
                                .selectAll('.trap')
                                .data(handleData);
            
            trapezoids.enter()
                        .append('polygon')
                        .attr('class', (d,i) => 'trap + trap-' + i)
                      .merge(trapezoids)
                        .attr('points', (d) => getPoints(chart.scale(d.number), chart.scale(d.nextNum), trapezoidsHeight))
                        .attr('transform', (d,i) => 'translate(0,' + i*(config.trapezoidPadding + trapezoidsHeight) + ')')
                        .attr('fill', (d,i) => chart._colors(i))

            trapezoids.exit()
                        .remove();
                                          

        //计算梯形的点坐标
        function getPoints(topWidth, bottomWidth, height){
            const points = [];

            points.push(-topWidth/2 + ',' + 0);
            points.push(topWidth/2 + ',' + 0);

            if (bottomWidth === 0){
                points.push(0 + ',' + height);
            }else{
                points.push(bottomWidth/2 + ',' + height);
                points.push(-bottomWidth/2 + ',' + height);
            }
            
            return points.join(" ");
        }
    }

在梯形的中心点渲染文本标签

/* ----------------------------渲染文本标签------------------------  */
    chart.renderText = function(){
        let texts = d3.select('.traps')
                            .selectAll('.label')
                            .data(handleData);
            
              texts.enter()
                     .append('text')
                     .attr('class','label')
                   .merge(texts)
                     .text((d) => d.action)
                     .attr('text-anchor', 'middle')
                     .attr('x', 0)
                     .attr('y', (d,i) => i * (config.trapezoidPadding + trapezoidsHeight) + trapezoidsHeight/2)
                     .attr('stroke', config.textColor);

              texts.exit()
                     .remove();
    }

最后绑定鼠标交互事件,鼠标悬停在梯形时,梯形颜色改变

/* ----------------------------绑定鼠标交互事件------------------------  */
    chart.addMouseOn = function(){

        d3.selectAll('.trap')
            .on('mouseover', function(){
                const e = d3.event;

                d3.select(e.target)
                    .attr('fill', config.hoverColor);
            })
            .on('mouseleave', function(d,i){
                const e = d3.event;

                d3.select(e.target)
                    .attr('fill', chart._colors(i));
            })
    }

大功告成!!!

发布了250 篇原创文章 · 获赞 88 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/zjw_python/article/details/98497967