vue项目基于D3js的3D饼图实现(五)

纵向普通图例的实现,效果图如下

一、修改Pie3D.vue文件,config配置

二、pie.js文件创建纵向图例

三、处理纵向图例的位置

 完整代码如下

import * as d3 from 'd3';
import { pieInner, pieTop, pieOuter } from '../utils/renderUtils'
/**
 * 生成3d饼图
 * @param {*} id :id唯一标识
 * @param {*} width :svg的宽
 * @param {*} height :svg的高
 * @param {*} data :要渲染的数据
 * @param {*} x :横向偏移量
 * @param {*} y :纵向偏移量
 * @param {*} rx :饼图的横向半径
 * @param {*} ry :饼图的纵向半径
 * @param {*} h :饼图的高度
 */
export default function pie(id, width, height, data, x, y, rx, ry, h, config,ir=0){
    //先移除所有的svg 
    d3.select(id).selectAll('svg').remove();
    //创建一个svg容器,用来存放饼图
    const pieSvg = d3
                    .select(id)
                    .append('svg')
                    .attr('width','100%')
                    .attr('height','100%');
    pieSvg.append('g').attr('id','pie_chart');
    //生成饼图数据
    const dataset = d3
                    .pie()
                    .sort(null)
                    .value((d) => {
                        return d.value
                    })(data);
    // 获取上面插入的g
    const slices = d3
                    .select('#pie_chart')
                    .append('g')
                    .attr('transform','translate(350,200)')
                    .attr('class','slices')
                    .style('cursor','pointer');
    //生成环形内曲面
    slices
        .selectAll('.innerSlice')
        .data(dataset)
        .enter()
        .append('path')
        .attr('class','innerSlice')
        .style('fill',(d,index) => {
            let colorIndex = index % config.color.length
            return d3.hsl(config.color[colorIndex]).darker(0.7)
        })
        .attr('d',d => {
            return pieInner(d, rx+0.5, ry+0.5, h, ir)
        });
    //上层2D平面
    slices.selectAll('.topSlice')
        .data(dataset)
        .enter()
        .append('path')
        .transition()
        .delay(0)
        .duration(500)
        .attrTween('d',(d) => {
            //动画效果
            let interpolate = d3.interpolate(d.startAngle, d.endAngle);
            return function(t){
                d.endAngle = interpolate(t);
                return pieTop(d, rx, ry, ir)
            }
        })
        .attr('class','topSlice')
        .style('fill',(d,index) => {
            let colorIndex = index % config.color.length
            return config.color[colorIndex]
        })
        .style('stroke',(d,index) => {
            let colorIndex = index % config.color.length
            console.log(config.color[colorIndex],'==config.color[colorIndex]')
            return config.color[colorIndex]
        })
    //侧面曲面
    slices.selectAll('.outerSlice')
    .data(dataset)
    .enter()
    .append('path')
    .transition()
    .delay(0)
    .duration(500)
    .attrTween('d',(d) => {
        //动画效果
        let interpolate = d3.interpolate(d.startAngle, d.endAngle);
        return function(t){
            d.endAngle = interpolate(t);
            return pieOuter(d, rx- 0.5, ry - 0.5, h)
        }
    })
    .attr('class','outerSlice')
    .style('fill',(d,index) => {
        let colorIndex = index % config.color.length
        return d3.hsl(config.color[colorIndex]).darker(0.7)
    });
    //是否显示标签
    if(config.sfShowLabel){
        //创建文本标签引导线
        const line = d3.select('#pie_chart')
            .append('g')
            .attr('transform',`translate(350,200)`)
            .attr('class','line');
        //引导线
        line.selectAll('.line')
            .data(dataset)
            .enter()
            .append('line')
            .attr('stroke',(d,index) => {
                let colorIndex = index % config.color.length
                return config.color[colorIndex]
            })
            .attr('x1',0)
            .attr('y1',0)
            .attr('x2',d => {
                return 1.5 * rx * Math.cos(0.5 * (d.startAngle + d.endAngle))
            })
            .attr('y2',d => {
                return 1.5 * ry * Math.sin(0.5 * (d.startAngle + d.endAngle))
            })
            .style('visibility', d => {
                return 'visible'
            });
        //文本
        const textbox = d3.select('#pie_chart')
            .append('g')
            .attr('transform','translate(350,200)')
            .attr('class','textbox');
        const text = textbox.selectAll('desc')
            .data(dataset)
            .enter()
            .append('text')
            .attr('font-size','14px')
            .attr('text-anchor','middle')
            .style('visibility', d => {
                return 'visible'
            })
            .attr('fill',(d,index) => {
                let colorIndex = index % config.color.length
                return config.color[colorIndex]
            })
            .text(d => {
                return `${d.data.label}:${d.data.value}${d.data.DWMC}`
            })
            .attr('transform', d => {
                let x = 1.5 * (rx + 10) * Math.cos(0.5 * (d.startAngle + d.endAngle))
                let y = 1.5 * (ry + 10) * Math.sin(0.5 * (d.startAngle + d.endAngle))
                return `translate(${x},${y})`
            })

    }
    //是否显示图例
    if(config.sfLegend){
        //此处为了实现横向和纵向图例的效果,必须在外层盒子中创建薪的svg来包裹图例盒子
        const legendSvg = d3.select(id)
            .append('svg')
            .style('position','absolute');
        let legend = null //每一个图例
        let legendBox = null //图例层最外层大盒子
        //判断是否横向图例
        if(config.legendOrient === 'horizontal'){
            //横向
            legendBox = legendSvg.attr('width',700)
                .attr('height',20)
                .style('top',() => {
                    return 400 - 30
                })
                .style('left',7)
                .append('g')
                .attr('id','legend_svg')
            legend = legendBox.selectAll('g')
                .data(data)
                .enter()
                .append('g');
        }else{
            //纵向 vertical
            legendBox = legendSvg.attr('height',400)
            .attr('width',200)
            .style('top',0)
            .style('left',7)
            .append('g')
            .attr('id','legend_svg');
            legend = legendBox.selectAll('g')
                .data(data)
                .enter()
                .append('g')
                .attr('transform',(d,i) => {
                    return `translate(0,${i * 20})`
                });
        }
        //创建图例前的小圆点样式
        let legendItembox = legend.append('circle')
            .attr('cx',10)
            .attr('cy',10)
            .attr('r',7);

        legendItembox.attr('fill',(d,index) => {
            let colorIndex = index % config.color.length
            return config.color[colorIndex]
        });
        //图例旁边的文字
        legend.append('text')
            .text(d => {
                return d.label
            })
            .style('font-size','14px')
            .attr('fill','#000000')
            .attr('y',11)
            .attr('x',30)
            .attr('dy',3);
        //处理横向图例的翻页还是普通
        if(config.legendOrient === 'horizontal'){
            //判断是普通图例还是翻页图例
            if(config.legendType === 'scroll'){
                legendSvg.attr('width',700 - 80);
                    //计算每一个图例的距离
                    let list = []
                    let left = 0
                    legend.each(function(){
                        left += d3.select(this).node().getBBox().width + 10
                        list.push(left)
                    })
                    legend.attr('transform',function(d, i){
                        if(i === 0){
                            return 'translate(0,0)'
                        }else {
                            return `translate(${list[i - 1]},0)`
                        }
                    })
                    //图例宽度大于盒子宽度
                    if(legendSvg.node().getBBox().width > (700 - 80)){
                        //获取总页数
                        let legendNumLength = 0
                        let totalPage = 1 //总页数
                        let curPage = 1 //翻页定义当前页数
                        let transitionX = 0 //需要移动图例整体所占的距离
                        let lineList = [] //如果是普通图例判断一行显示多少个
                        legend.each(function(d,i){
                            legendNumLength += d3.select(this).node().getBBox().width + 10
                            if(legendNumLength > (700 - 80 - (d3.select(this).node().getBBox().width + 10))){
                                legendNumLength = 0
                                //避免移动到最后一页多加一夜
                                if(data.length - 1 !== i){
                                    lineList.push(i)
                                    totalPage++
                                }
                            }
                        })
                        //定义横向翻页的svg
                        const legendPageSvg = d3.select(id)
                            .append('svg')
                            .attr('width',100)
                            .attr('height',20)
                            .style('position','absolute')
                            .style('top',400 - 30)
                            .style('left',700 - 80);
                        let sym = d3.symbol().type(d3.symbolTriangle).size(100);

                        const pageBox = legendPageSvg.append('g')
                            .attr('id','legend_page')
                            .attr('transform','translate(15,8)');

                            //定义左侧翻页符号
                        pageBox.append('path')
                            .attr('d',sym)
                            .attr('fill','green')
                            .attr('transform','rotate(30)')
                            .style('cursor','pointer')
                            .on('click',function(){
                                if(curPage > 1){
                                    let legendNumLength = 0
                                    let curLength = 0
                                    legend.each(function(d,i){
                                        legendNumLength += d3.select(this).node().getBBox().width + 10
                                        if(legendNumLength <= 700 - 80){
                                            curLength = legendNumLength
                                        }
                                    })
                                    transitionX -= curLength
                                    legendBox.transition()
                                        .duration(500)
                                        .attr('transform',`translate(-${transitionX}, 0)`);
                                    curPage--
                                    legendText.text(`${curPage}/${totalPage}`)
                                }
                            });
                        //页码
                        const legendText = pageBox.append('text')
                            .text(`${curPage}/${totalPage}`)
                            .attr('fill','red')
                            .attr('font-size',12)
                            .attr('transform','translate(10,5)');
                        //右侧翻页按钮
                        pageBox.append('path')
                            .attr('d',sym)
                            .attr('fill','green')
                            .attr('transform','translate(40,0) rotate(-30)')
                            .style('cursor','pointer')
                            .on('click',function(){
                                if(curPage < totalPage){
                                    let legendNumLength = 0
                                    let curLength = 0
                                    legend.each(function(d,i){
                                        legendNumLength += d3.select(this).node().getBBox().width + 10
                                        if(legendNumLength <= 700 - 80){
                                            curLength = legendNumLength
                                        }
                                    });
                                    transitionX += curLength
                                    legendBox.transition()
                                        .duration(500)
                                        .attr('transform',`translate(-${transitionX}, 0)`);
                                    curPage++
                                    legendText.text(`${curPage}/${totalPage}`)
                                }
                            });
                    }
            }else{
                //类型是普通图例
                legendSvg.attr('width',700 - 20);
                //获取总页数
                let legendNumLength = 0
                let totalPage = 1 //总页数
                let lineList = [] //如果是普通图例判断一行显示多少个
                legend.each(function(d,i) {
                    legendNumLength += d3.select(this).node().getBBox().width + 10
                    if(legendNumLength > (700 - 20 - (d3.select(this).node().getBBox().width + 10))){
                        legendNumLength = 0
                        //避免移动到最后一页多加一夜
                        if(data.length - 1 !== i){
                            lineList.push(i)
                            totalPage++
                        }
                    }
                })
                let maxWidth = 0 //获取最大的图例宽度
                legend.each(function() {
                    if(maxWidth < d3.select(this).node().getBBox().width){
                        maxWidth = d3.select(this).node().getBBox().width
                    }
                });
                legendSvg.attr('height',20*totalPage)
                    .style('top',450 - 30 - 20*(totalPage - 1));
                let listX = [] //各个图例横向的X
                let listY = [] //图例纵向的Y
                let left = 0
                let top = 0
                legend.each(function(d,i) {
                    left += d3.select(this).node().getBBox().width + 10
                    lineList.forEach(item => {
                        if(item === i){
                            left = 0
                            top += 20
                        }
                    })
                    listX.push(left)
                    listY.push(top)
                });
                legend.attr('transform',(d,i) => {
                    if(i === 0){
                        return 'translate(0,0)'
                    }else{
                        return `translate(${listX[i - 1]},${listY[i - 1]})`
                    }
                })
            }
        }else{
            let maxWidth = 0 //获取最大宽度
                legend.each(function(){
                    if(maxWidth < d3.select(this).node().getBBox().width){
                        maxWidth = d3.select(this).node().getBBox().width
                    }
                })
                if(config.legendType === 'scroll'){
                    console.log('翻页')
                }else{
                    //普通图例
                    //计算分组
                    let totalPage = 1 //总页码
                    let legendNumHeight = 0 //初始高度
                    let rowList = [] //普通图例一列显示多少个图例
                    legend.each(function(d,i){
                        legendNumHeight += 20
                        if(legendNumHeight > 400){
                            legendNumHeight = 0
                            if(data.length - 1 !== i){
                                totalPage ++
                                rowList.push(i)
                            }
                        }
                    })
                    legendSvg.attr('width',(maxWidth + 10) * totalPage);
                    let listX = [] //各个图例的X
                    let listY = [] //各个图例的Y
                    let left = 0
                    let top = 0
                    legend.each(() => {
                        top += 20
                        rowList.forEach(item => {
                            if(item - 1 === i){
                                left += maxWidth + 10
                                top = 0
                            }
                        })
                        listX.push(left)
                        listY.push(top)
                    })
                    legend.attr('transform',(d,i) => {
                        if(i === 0){
                            return 'translate(0,0)'
                        }else{
                            return `translate(${listX[i - 1]},${listY[i - 1]})`
                        }
                    })
                }
        }

    }
}

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值