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{ //纵向 console.log('纵向') } //创建图例前的小圆点样式 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]})` } }) } } } }