如何将线性渐变实现的连续图例转为离散阶梯式图例?
将连续渐变图例转为离散阶梯图例
我目前用线性渐变实现了连续图例,现在想把它改成具有恒定阶梯值的离散图例。以下是当前代码、目标效果示意图:
当前代码
const legendColor = [{ offset: 0.0, color: "#ff0000" }, { offset: 0.2, color: "#ffff00" }, { offset: 0.4, color: "#00ff00" }, { offset: 0.6, color: "#00ffff" }, { offset: 0.8, color: "#0000ff" }, { offset: 1.0, color: "#ff00ff" } ]; const svg = d3.select("svg"); const colorScale2 = d3.scaleLinear().domain([0, 1]).range([0, 400]); const id = "linear-gradient-0"; const linearGradient2 = svg.append("defs") .append("linearGradient") .attr("id", "linear-gradient-1") .attr("x1", "100%") .attr("x2", "0%") .attr("y1", "0%") .attr("y2", "0%"); // append the color linearGradient2 .selectAll("stop") .data(legendColor) .enter() .append("stop") .attr("offset", function(data) { return colorScale2(data.offset) / 4 + "%"; //return data.offset + "%"; }) .attr("stop-color", function(data) { return data.color; }); // draw the rectangle and fill with gradient svg.append("rect") .attr("x", 10) .attr("y", 88) .attr("width", 400) .attr("height", 20) .style("fill", "url(#linear-gradient-1)");
目标效果

解决方案
实现离散阶梯图例,最直观的方式是直接为每个颜色绘制独立矩形块,无需使用线性渐变,代码修改如下:
const legendColor = [{ offset: 0.0, color: "#ff0000" }, { offset: 0.2, color: "#ffff00" }, { offset: 0.4, color: "#00ff00" }, { offset: 0.6, color: "#00ffff" }, { offset: 0.8, color: "#0000ff" }, { offset: 1.0, color: "#ff00ff" } ]; const svg = d3.select("svg"); const totalWidth = 400; const startX = 10; const startY = 88; const blockHeight = 20; const blockWidth = totalWidth / legendColor.length; // 绘制每个颜色块 let currentX = startX; legendColor.forEach(colorItem => { svg.append("rect") .attr("x", currentX) .attr("y", startY) .attr("width", blockWidth) .attr("height", blockHeight) .style("fill", colorItem.color); currentX += blockWidth; }); // 添加色块分隔线,还原目标图效果 currentX = startX; for (let i = 1; i < legendColor.length; i++) { currentX += blockWidth; svg.append("line") .attr("x1", currentX) .attr("y1", startY) .attr("x2", currentX) .attr("y2", startY + blockHeight) .style("stroke", "#000") .style("stroke-width", 1); }
方案说明
- 移除原线性渐变相关代码,直接遍历颜色数组绘制独立矩形,每个色块宽度均分总宽度
- 添加黑色竖线作为色块分隔,匹配目标图的阶梯视觉效果
- 如果需要按
offset间隔比例分配色块宽度,可将blockWidth的计算逻辑改为基于相邻offset的差值
如果一定要通过线性渐变模拟阶梯效果,可通过重复设置渐变断点实现:
const legendColor = [{ offset: 0.0, color: "#ff0000" }, { offset: 0.2, color: "#ffff00" }, { offset: 0.4, color: "#00ff00" }, { offset: 0.6, color: "#00ffff" }, { offset: 0.8, color: "#0000ff" }, { offset: 1.0, color: "#ff00ff" } ]; const svg = d3.select("svg"); const totalWidth = 400; const gradientId = "linear-gradient-1"; // 生成阶梯渐变断点:每个颜色的起始和结束位置 const stopPoints = []; legendColor.forEach((item, idx) => { stopPoints.push({offset: item.offset, color: item.color}); if (idx < legendColor.length - 1) { stopPoints.push({offset: legendColor[idx+1].offset, color: item.color}); } }); // 创建渐变 const linearGradient = svg.append("defs") .append("linearGradient") .attr("id", gradientId) .attr("x1", "0%") .attr("x2", "100%") .attr("y1", "0%") .attr("y2", "0%"); linearGradient .selectAll("stop") .data(stopPoints) .enter() .append("stop") .attr("offset", d => `${d.offset * 100}%`) .attr("stop-color", d => d.color); // 绘制渐变矩形 svg.append("rect") .attr("x", 10) .attr("y", 88) .attr("width", totalWidth) .attr("height", 20) .style("fill", `url(#${gradientId})`); // 添加分隔线 let currentX = 10; for (let i = 1; i < legendColor.length; i++) { currentX += (legendColor[i].offset - legendColor[i-1].offset) * totalWidth; svg.append("line") .attr("x1", currentX) .attr("y1", 88) .attr("x2", currentX) .attr("y2", 88 + 20) .style("stroke", "#000") .style("stroke-width", 1); }
内容的提问来源于stack exchange,提问作者Eric
相关产品推荐
相关产品推荐

