最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - d3.js brush fill color histogram - Stack Overflow

programmeradmin2浏览0评论

i have created some histogram with d3.js. I managed to change fill color of rect depending on the position of the brush. But i would like to change the color inside a rect. For example if the brush start is in the middle of the rect i would like to have my rect with two color.

At the moment this is what i have :

And this is what i would like to have :

I have seen some examples like Here. I'm new to d3 and i try to understand the code.
I see that they use clip-path that certainly hide the background bar when their is no brush and show them when their is one depending on the range of the brush.

Here is a JS Bin

Update

I have read in details the code provided in the link. And i found that they dont create <rect> element to make chart but barPath like follow :

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("");
      }

But i didn't event understand what happend in this function and how to dot it in this way if their is no other way to do this.

i have created some histogram with d3.js. I managed to change fill color of rect depending on the position of the brush. But i would like to change the color inside a rect. For example if the brush start is in the middle of the rect i would like to have my rect with two color.

At the moment this is what i have :

And this is what i would like to have :

I have seen some examples like Here. I'm new to d3 and i try to understand the code.
I see that they use clip-path that certainly hide the background bar when their is no brush and show them when their is one depending on the range of the brush.

Here is a JS Bin

Update

I have read in details the code provided in the link. And i found that they dont create <rect> element to make chart but barPath like follow :

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("");
      }

But i didn't event understand what happend in this function and how to dot it in this way if their is no other way to do this.

Share Improve this question edited Aug 5, 2016 at 12:36 Christophe D. asked Aug 5, 2016 at 9:00 Christophe D.Christophe D. 1,08911 silver badges21 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 6

Instead of trying to draw partial bars (as your edit seems to suggest), I would instead append the bars twice, once gray on bottom and then steelblue on top. You can then just apply a clip-path to the blue bars and when they are clipped you'll see the gray below.

Full code:

<!DOCTYPE html>
<html>

<head>
  <script data-require="[email protected]" data-semver="3.5.3" src="//cdnjs.cloudflare./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>

For anyone looking to bring @Mark's answer to 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./ajax/libs/d3/6.7.0/d3.min.js"></script>
<svg/>

发布评论

评论列表(0)

  1. 暂无评论