Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3.js brush fill color histogram

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 :
enter image description here

And this is what i would like to have :
enter image description here

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.

like image 404
Christophe D. Avatar asked Aug 05 '16 09:08

Christophe D.


1 Answers

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.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
<svg/>
like image 115
duhaime Avatar answered Sep 30 '22 16:09

duhaime