d3.js 画笔填充颜色直方图

Posted

技术标签:

【中文标题】d3.js 画笔填充颜色直方图【英文标题】:d3.js brush fill color histogram 【发布时间】:2016-12-11 15:16:47 【问题描述】:

我用d3.js 创建了一些直方图。 我设法根据brush 的位置更改rect 的填充颜色。 但我想更改rect 内的颜色。例如,如果brush startrect 的中间,我想让我的rect 有两种颜色。

目前这就是我所拥有的:

这就是我想要的:

我见过一些例子,比如Here。我是 d3 的新手,我尝试理解代码。 我看到他们使用clip-path,当他们没有画笔时肯定会隐藏背景栏,并根据画笔的范围显示它们。

这是JS Bin

更新

我已详细阅读了link 中提供的代码。我发现他们没有创建<rect> 元素来制作图表,但barPath 喜欢关注:

function barPath(groups) 
        var path = [],
            i = -1,
            n = groups.length,
            d;
        while (++i < n) 
          d = groups[i];
          path.push("M", x(d.key), ",", height, "V", y(d.value), "h9V", height);
        
        return path.join("");
      

但我不明白这个函数中发生了什么,以及如果他们没有其他方法可以做到这一点,如何以这种方式添加它。

【问题讨论】:

【参考方案1】:

我不会尝试绘制部分条形(正如您的编辑似乎暗示的那样),而是将条形附加两次,一次是底部的灰色,然后是顶部的钢蓝。然后,您可以将剪辑路径应用于蓝色条,当它们被剪辑时,您会看到下面的灰色。

完整代码:

<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
  <style>
    .charts 
      padding: 10px 0;
    
    
    .chart 
      padding-left: 20px;
      padding-top: 10px;
    
    
    .axis text 
      font: 10px sans-serif;
      fill: black;
    
    
    .chart text 
      font: 10px sans-serif;
      fill: black;
    
    
    .axis path,
    .axis line 
      fill: none;
      stroke: #000;
      shape-rendering: crispEdges;
    
    /*dont display yAxis for categorical variable*/
    
    #chart .y.axis g 
      display: none;
    
    /*Labels in categorical chart */
    
    text#catTitle.catTitle 
      font: 10px sans-serif;
      fill: white;
    
    /*Color for the brush */
    
    .brush rect.extent 
      fill: steelblue;
      fill-opacity: .125;
    
    /*Color for the brush resize path*/
    
    .brush .resize path 
      fill: #eee;
      stroke: #666;
    
    /*Color for the hidden object*/
    
    .hidden 
      fill: grey;
    
    
    .bar 
      fill: steelblue;
    
  </style>
</head>

<body>

  <svg class="chart" id="chart"></svg>

  <script>
    var data = [
      key: 1,
      value: 37
    , 
      key: 1.5,
      value: 13
    , 
      key: 2.5,
      value: 1
    , 
      key: 3,
      value: 4
    , 
      key: 3.5,
      value: 14
    , 
      key: 4,
      value: 18
    , 
      key: 4.5,
      value: 21
    , 
      key: 5,
      value: 17
    , 
      key: 5.5,
      value: 16
    , 
      key: 6,
      value: 5
    , 
      key: 6.5,
      value: 4
    ];

    var margin = 
      top: 10,
      right: 41,
      bottom: 42,
      left: 10
    ;

    var width = 400 - margin.left - margin.right,
      height = 250 - margin.top - margin.bottom;

    var y = d3.scale.linear()
      .domain([0, d3.max(data, function(d) 
        return d.value
      )])
      .range([height, 0]);

    var x = d3.scale.linear()
      .domain([0, d3.max(data, function(d) 
        return d.key;
      ) + 1])
      .rangeRound([0, width]);

    var xAxis = d3.svg.axis()
      .scale(x)
      .orient("bottom");

    var yAxis = d3.svg.axis()
      .scale(y)
      .orient("left");

    var chart = d3.select(".chart#chart")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .style("margin-left", 15 + "px");

    chart.append("defs")
      .append("clipPath")
      .attr("id", "clip")
      .append("rect")
      .attr("x", 0)
      .attr("y", 0)
      .attr("width", width)
      .attr("height", height);

    var brush = d3.svg.brush()
      .x(x)
      .on("brush", brushed)
      .on("brushend", brushend);

    function brushend() 
      if (brush.empty())
        chart.select("#clip>rect")
          .attr("x", 0)
          .attr("width", width);
      
    

    function brushed() 
      var e = brush.extent();
      chart.select("#clip>rect")
        .attr("x", x(e[0]))
        .attr("width", x(e[1]) - x(e[0]));
    

    chart.selectAll(".hidden")
      .data(data)
      .enter().append("rect")
      .attr("class", "hidden")
      .attr("x", function(d) 
        return x(d.key);
      )
      .attr("y", function(d) 
        return y(d.value);
      )
      .attr("height", function(d) 
        return height - y(d.value);
      )
      .attr("width", x(0.5))
      .style("stroke", "white")
      .append("title")
      .text(function(d) 
        return d.key;
      );

    chart.selectAll(".bar")
      .data(data)
      .enter().append("rect")
      .attr("clip-path", "url(#clip)")
      .attr("class", "bar")
      .attr("x", function(d) 
        return x(d.key);
      )
      .attr("y", function(d) 
        return y(d.value);
      )
      .attr("height", function(d) 
        return height - y(d.value);
      )
      .attr("width", x(0.5))
      .style("stroke", "white")
      .append("title")
      .text(function(d) 
        return d.key;
      );

    chart.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

    chart.append("text") //Add chart title
      .attr("transform", "translate(" + (width / 2) + " ," + (height + margin.bottom) + ")")
      .style("text-anchor", "middle")
      .text("Petal Length");

    chart.append("g")
      .attr("class", "y axis")
      .call(yAxis);

    chart.append("g")
      .attr("class", "x brush")
      .call(brush) //call the brush function, causing it to create the rectangles
      .selectAll("rect") //select all the just-created rectangles
      .attr("y", -6)
      .attr("height", (height + margin.top)) //set their height

    function resizePath(d) 
      var e = +(d == "e"),
        x = e ? 1 : -1,
        y = height / 3;
      return "M" + (.5 * x) + "," + y + "A6,6 0 0 " + e + " " + (6.5 * x) + "," + (y + 6) + "V" + (2 * y - 6) + "A6,6 0 0 " + e + " " + (.5 * x) + "," + (2 * y) + "Z" + "M" + (2.5 * x) + "," + (y + 8) + "V" + (2 * y - 8) + "M" + (4.5 * x) + "," + (y + 8) + "V" + (2 * y - 8);
    

    chart.selectAll(".resize").append("path").attr("d", resizePath);
  </script>
</body>

</html>

【讨论】:

【参考方案2】:

对于希望将@Mark 的答案带到 v6 的任何人:

const data = [
  key: 1,
  value: 37
, 
  key: 1.5,
  value: 13
, 
  key: 2.5,
  value: 1
, 
  key: 3,
  value: 4
, 
  key: 3.5,
  value: 14
, 
  key: 4,
  value: 18
, 
  key: 4.5,
  value: 21
, 
  key: 5,
  value: 17
, 
  key: 5.5,
  value: 16
, 
  key: 6,
  value: 5
, 
  key: 6.5,
  value: 4
];

// svg sizes
const width = 400,
      height = 200;

const m = 50;
const margin = 
  top: m,
  right: m,
  bottom: m,
  left: m,
;

const y = d3.scaleLinear()
  .domain(d3.extent(data, d => d.value))
  .range([height - margin.bottom, margin.top]);

const x = d3.scaleLinear()
  .domain(d3.extent(data, d => d.key).map((v, i) => i==0 ? v - 1 : v + 1))
  .rangeRound([margin.left, width - margin.right]);

const svg = d3.select('svg')
  .attr('width', width)
  .attr('height', height)
  .attr('viewBox', `0 0 $width $height`)

const rects = svg.append('g').attr('class', 'rects');
const clips = svg.append('g').attr('class', 'clips');

svg.append('g')
  .attr('class', 'x-axis')
  .attr('transform', `translate(0,$height - margin.bottom)`)
  .call(d3.axisBottom(x));

svg.append('g')
  .attr('class', 'y-axis')
  .style('display', 'none')
  .attr('transform', `translate($margin.left,0)`)
  .call(d3.axisLeft(y));

svg.append('defs')
  .append('clipPath')
  .attr('id', 'clip')
    .append('rect')
    .attr('x', margin.left)
    .attr('y', margin.top)
    .attr('width', width - margin.right)
    .attr('height', height - margin.bottom);

const brush = d3.brushX()
  .extent([
    [x.range()[0], margin.top],
    [x.range()[1], height - margin.bottom]
  ])
  .on('brush', brushed)
  .on('start', brushed)
  .on('end', brushend);

function brushend(e) 
  if (!e.selection || !e.selection.length) 
    svg.select('#clip>rect')
      .attr('x', margin.left)
      .attr('width', width - margin.right);
  


function brushed(e) 
  svg.select('#clip>rect')
    .attr('x', e.selection[0])
    .attr('width', e.selection[1] - e.selection[0]);

  const selected = 
    x0: x.invert(e.selection[0]),
    x1: x.invert(e.selection[1]),
  


rects.selectAll('rect')
  .data(data)
  .enter().append('rect')
  .attr('x', d => x(d.key))
  .attr('y', d => y(d.value))
  .attr('height', d => height - y(d.value) - margin.bottom)
  .attr('width', 20)
  .style('stroke', 'white')
  .style('fill', 'gray')
  .append('title')
  .text(d => d.key);

clips.selectAll('rect')
  .data(data)
  .enter().append('rect')
  .attr('clip-path', 'url(#clip)')
  .attr('x', d => x(d.key))
  .attr('y', d => y(d.value))
  .attr('height', d => height - y(d.value) - margin.bottom)
  .attr('width', 20)
  .style('stroke', 'white')
  .append('title')
  .text(d => d.key);

svg.append('g')
  .attr('class', 'x brush')
  .call(brush) // initialize the brush
  .selectAll('rect')
  .attr('y', 0)
  .attr('height', height)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
<svg/>

【讨论】:

以上是关于d3.js 画笔填充颜色直方图的主要内容,如果未能解决你的问题,请参考以下文章

用ggplot2直方图中另一个连续变量的平均值填充条形颜色

直方图 颜色映射

使用 stat_bin ggplot 的颜色直方图条

使用自定义渐变填充直方图箱

直方图绘制

使用从颜色图中获取的颜色绘制直方图