D3.js实现可拖拽柱状图按高度动态变色(高红低绿)
问题描述
基于D3.js实现了支持鼠标拖拽交互的柱状图:可通过拖拽操作拉高或压低任意柱体的高度,拖拽完成后柱体对应的数据值会根据拖拽后的高度自动更新。需要新增柱体填充色动态变化效果:拖拽拉高柱体高度时柱体颜色向红色偏移,拖拽压低柱体高度时柱体颜色向绿色偏移。
原有可运行代码如下:
<!DOCTYPE html> <html> <style> .selection { fill: steelblue; fill-opacity: 1; } body { width: 80%; margin: auto; } </style> <body > <script src="https://d3js.org/d3.v4.min.js"></script> <script> var data = [ { index: 0, value: 18 }, { index: 1, value: 20 }, { index: 2, value: 19 }, ]; var widthY = 550, heightY = 600, delim = 4; var scaleY = d3.scaleLinear().domain([0, 21]).rangeRound([heightY, 0]); var x = d3.scaleLinear().domain([0, data.length]).rangeRound([0, widthY]); var svgY = d3 .select("body") .append("svg") .attr("width", widthY) .attr("height", heightY) .attr("fill", "green") .append("g"); var brushY = d3 .brushY() .extent(function (d, i) { return [ [x(i) + delim / 2, 0], [x(i) + x(1) - delim / 2, heightY], ]; }) .on("brush", brushmoveY) .on("end", brushendY); var svgbrushY = svgY .selectAll(".brush") .data(data) .enter() .append("g") .attr("class", "brush") .append("g") .call(brushY) .call(brushY.move, function (d) { return [d.value, 0].map(scaleY); }); svgbrushY .append("text") .attr("y", function (d) { return scaleY(d.value) + 25; }) .attr("x", function (d, i) { return x(i) + x(0.5); }) .attr("dx", "-.60em") .attr("dy", -5) .style("fill", "white") .text(function (d) { return d3.format(".2")(d.value); }); function brushendY() { if (!d3.event.sourceEvent) return; if (d3.event.sourceEvent.type === "brush") return; if (!d3.event.selection) { svgbrushY.call(brushY.move, function (d) { return [d.value, 0].map(scaleY); }); } } function brushmoveY() { if (!d3.event.sourceEvent) return; if (d3.event.sourceEvent.type === "brush") return; if (!d3.event.selection) return; var d0 = d3.event.selection.map(scaleY.invert); var d = d3.select(this).select(".selection"); d.datum().value = d0[0]; update(); } function update() { svgbrushY .call(brushY.move, function (d) { return [d.value, 0].map(scaleY); }) .selectAll("text") .attr("y", function (d) { return scaleY(d.value) + 25; }) .text(function (d) { return d3.format(".2")(d.value); }); } </script> </body> </html>
实现方案
核心逻辑分三步:
- 给每个数据项存储初始值作为颜色判断基准,避免拖拽过程中基准值错乱
- 定义线性颜色比例尺:以初始的钢蓝色为中间色,柱体值低于初始值时向绿色过渡,高于初始值时向红色过渡,超出最大可调整范围时直接取边界色
- 在原有高度更新逻辑中,同步根据当前值和初始值的差值计算对应填充色,赋值给柱体元素
修改后完整代码
<!DOCTYPE html> <html> <style> .selection { fill-opacity: 1; } body { width: 80%; margin: auto; } </style> <body > <script src="https://d3js.org/d3.v4.min.js"></script> <script> var data = [ // 新增originalValue字段存储每个柱体的初始基准值 { index: 0, value: 18, originalValue: 18 }, { index: 1, value: 20, originalValue: 20 }, { index: 2, value: 19, originalValue: 19 }, ]; var widthY = 550, heightY = 600, delim = 4; var scaleY = d3.scaleLinear().domain([0, 21]).rangeRound([heightY, 0]); // 定义颜色比例尺:差值范围对应绿-钢蓝-红的渐变 // 差值=当前值-初始值,负为压低、0为初始状态、正为拉高 var colorScale = d3.scaleLinear() .domain([-21, 0, 21]) .range(['#2ca02c', 'steelblue', '#d62728']) .clamp(true); var x = d3.scaleLinear().domain([0, data.length]).rangeRound([0, widthY]); var svgY = d3 .select("body") .append("svg") .attr("width", widthY) .attr("height", heightY) .append("g"); var brushY = d3 .brushY() .extent(function (d, i) { return [ [x(i) + delim / 2, 0], [x(i) + x(1) - delim / 2, heightY], ]; }) .on("brush", brushmoveY) .on("end", brushendY); var svgbrushY = svgY .selectAll(".brush") .data(data) .enter() .append("g") .attr("class", "brush") .append("g") .call(brushY) .call(brushY.move, function (d) { return [d.value, 0].map(scaleY); }); svgbrushY .append("text") .attr("y", function (d) { return scaleY(d.value) + 25; }) .attr("x", function (d, i) { return x(i) + x(0.5); }) .attr("dx", "-.60em") .attr("dy", -5) .style("fill", "white") .text(function (d) { return d3.format(".2")(d.value); }); function brushendY() { if (!d3.event.sourceEvent) return; if (d3.event.sourceEvent.type === "brush") return; if (!d3.event.selection) { svgbrushY.call(brushY.move, function (d) { return [d.value, 0].map(scaleY); }); } } function brushmoveY() { if (!d3.event.sourceEvent) return; if (d3.event.sourceEvent.type === "brush") return; if (!d3.event.selection) return; var d0 = d3.event.selection.map(scaleY.invert); var d = d3.select(this).select(".selection"); d.datum().value = d0[0]; update(); } function update() { svgbrushY .call(brushY.move, function (d) { return [d.value, 0].map(scaleY); }) .selectAll("text") .attr("y", function (d) { return scaleY(d.value) + 25; }) .text(function (d) { return d3.format(".2")(d.value); }); // 同步更新每个柱体的填充色 svgbrushY.select('.selection') .attr('fill', function(d) { const delta = d.value - d.originalValue; return colorScale(delta); }) } // 初始化时设置一次初始颜色 update(); </script> </body> </html>
内容的提问来源于stack exchange,提问作者colder programmer
相关产品推荐
相关产品推荐

