You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 22:06:13