如何用D3.js实现仅顶部带圆角的环形图?
实现仅外侧分段带圆角的D3.js环形图
要实现仅外侧边角带圆角的环形图,核心是绕过D3默认cornerRadius全局生效的限制,手动构建扇形路径,只在外侧的两个角添加圆角,内侧保持直角。下面是具体实现方案:
核心实现逻辑
- 用D3的
pie()生成器处理数据,得到每个扇形的起始/结束角度 - 自定义路径生成函数,拆分扇形为「外侧圆角弧段」+「内侧直角弧段」+「连接直线」三部分,手动拼接成完整路径
- 处理小角度扇形的边界问题,避免圆角重叠导致的路径异常
完整代码示例
// 基础配置 const width = 400; const height = 400; const innerRadius = 100; const outerRadius = 180; const cornerRadius = 12; const data = [30, 50, 20, 40]; const color = d3.scaleOrdinal(d3.schemeCategory10); // 创建SVG容器 const svg = d3.select('body') .append('svg') .attr('width', width) .attr('height', height) .append('g') .attr('transform', `translate(${width/2}, ${height/2})`); // 生成饼图数据(包含startAngle/endAngle) const pieGenerator = d3.pie(); const arcs = pieGenerator(data); // 自定义带外侧圆角的弧生成函数 function customArc(d) { const start = d.startAngle; const end = d.endAngle; const angleDiff = end - start; const halfPi = Math.PI / 2; // 最小角度阈值:避免圆角重叠 const minAngle = 2 * cornerRadius / outerRadius; // 角度过小时,回退到普通环形图 if (angleDiff < minAngle) { return d3.arc() .innerRadius(innerRadius) .outerRadius(outerRadius) (d); } // 计算外侧圆角的起始/结束锚点 const outerAnchorStartX = Math.cos(start - halfPi) * (outerRadius - cornerRadius); const outerAnchorStartY = Math.sin(start - halfPi) * (outerRadius - cornerRadius); const outerAnchorEndX = Math.cos(end - halfPi) * (outerRadius - cornerRadius); const outerAnchorEndY = Math.sin(end - halfPi) * (outerRadius - cornerRadius); // 外侧圆角的圆弧段 const outerCornerStart = `A ${cornerRadius} ${cornerRadius} 0 0 1 ${Math.cos(start - halfPi) * outerRadius} ${Math.sin(start - halfPi) * outerRadius}`; const outerCornerEnd = `A ${cornerRadius} ${cornerRadius} 0 0 1 ${outerAnchorEndX} ${outerAnchorEndY}`; // 外侧中间弧段(跳过圆角区域) const outerMiddleArc = d3.arc() .innerRadius(outerRadius) .outerRadius(outerRadius) .startAngle(start + cornerRadius / outerRadius) .endAngle(end - cornerRadius / outerRadius) ({}) .replace(/^M[^ ]+ [^ ]+ /, '') // 移除默认起始点 .replace(/ Z$/, ''); // 移除闭合命令 // 内侧直角弧段 const innerArc = d3.arc() .innerRadius(innerRadius) .outerRadius(innerRadius) .startAngle(end) .endAngle(start) ({}) .replace(/^M[^ ]+ [^ ]+ /, '') .replace(/ Z$/, ''); // 拼接完整路径 return `M ${outerAnchorStartX} ${outerAnchorStartY} ${outerCornerStart} ${outerMiddleArc} ${outerCornerEnd} L ${Math.cos(end - halfPi) * innerRadius} ${Math.sin(end - halfPi) * innerRadius} ${innerArc} L ${outerAnchorStartX} ${outerAnchorStartY} Z`; } // 绘制环形图 svg.selectAll('.arc') .data(arcs) .enter() .append('path') .attr('class', 'arc') .attr('d', customArc) .attr('fill', (d, i) => color(i));
关键细节说明
- 手动拆分路径:将扇形拆分为外侧圆角锚点、圆角弧、中间弧、内侧弧,确保只有外侧两个角有圆角
- 角度阈值判断:当扇形角度小于圆角对应的弧度时,自动回退到普通环形图,避免路径渲染异常
- 坐标转换:D3的角度基准是y轴正方向,通过减去
Math.PI/2转换为标准笛卡尔坐标计算
内容的提问来源于stack exchange,提问作者FxckDead
相关产品推荐
相关产品推荐

