Reputation: 466
I'm using d3.js to create a graphical grid with 2 axis. Data is displayed using cells whose color will change based on the data value. An simple example is visible here. The chart is created as an svg image. I have a problem with the clipping of the data area : when zooming and panning, the data overlaps the axes. You can see this in the provided example, by clicking on the chart content and dragging up or left.
Does someone know how I can make the data to disappear under the axes?
Here is the typescript code I used to create the chart:
let cellSize = 49;
let borderSize = 5;
let margin = { top: 40, right: 40, bottom: 40, left: 40 };
function createChart() {
// Create test data
let count = 500;
let perLine = 50;
let markers = [];
for (let i = 0; i < count; i++) {
let id = i + 1;
let line = Math.floor(i / perLine) + 1;
let point = i % perLine + 1;
markers.push({id: id, line: line, point: point});
}
//---
let lineMin = d3.min(markers, (item) => item.line);
let lineMax = d3.max(markers, (item) => item.line);
let pointMin = d3.min(markers, (item) => item.point);
let pointMax = d3.max(markers, (item) => item.point);
let chart = document.querySelector('.chart');
let viewWidth = chart.clientWidth - 2;
let viewHeight = 400;
let chartWidth = cellSize * (pointMax - pointMin + 1);
let chartHeight = cellSize * (lineMax - lineMin + 1);
let point = d3.scale.linear()
.domain([pointMin, pointMax])
.range([cellSize / 2, chartWidth - cellSize / 2]);
let line = d3.scale.linear()
.domain([lineMin, lineMax])
.range([cellSize / 2, chartHeight - cellSize / 2]);
let pointAxis = d3.svg.axis()
.scale(point)
.tickSize(0)
.tickValues(d3.range(pointMin, pointMax + 1))
.orient('top');
let lineAxis = d3.svg.axis()
.scale(line)
.tickSize(0)
.tickValues(d3.range(lineMin, lineMax + 1))
.orient('left');
let zoom = d3.behavior.zoom()
.x(point)
.y(line)
.scaleExtent([0.1, 1])
.on('zoom', () => {
let width = Math.min(viewWidth - margin.left - margin.right, chartWidth);
let height = Math.min(viewHeight - margin.top - margin.bottom, chartHeight);
let t = zoom.translate();
let tx = Math.min(t[0], 0);
tx = Math.max(tx, width - chartWidth);
let ty = Math.min(t[1], 0);
ty = Math.max(ty, height - chartHeight);
zoom.translate([tx, ty]);
let scale = zoom.scale();
svg.select('.x.axis').call(pointAxis).selectAll('text')
.attr('x', -3)
.attr('y', 5)
.attr('transform', 'rotate(90)')
.style('text-anchor', 'end');
svg.select('.y.axis').call(lineAxis);
grid.attr('transform', `translate(${[tx, ty]})scale(${scale})`);
});
let svg = d3.select('.chart').append('svg')
.attr('id', 'svg')
.attr('width', viewWidth)
.attr('height', viewHeight)
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`)
.call(zoom);
let grid = svg.append('g')
.attr('id', 'grid');
// Add the grid background
grid.append('rect')
.attr('class', 'grid')
.attr('x', 0)
.attr('y', 0)
.attr('width', chartWidth)
.attr('height', chartHeight);
// Create the cells
let cell = grid.selectAll('.cell')
.data(markers).enter()
.append('g')
.attr('id', (marker) => `cell${marker.line}-${marker.point}`)
.attr('class', 'cell')
.attr('transform', (marker) => `translate(${point(marker.point) - cellSize / 2}, ${line(marker.line) - cellSize / 2})`);
cell.append('rect')
.attr('class', 'border')
.attr('x', 0)
.attr('y', 0)
.attr('width', cellSize)
.attr('height', cellSize);
cell.append('rect')
.attr('class', (marker) => `fill valid`)
.attr('x', borderSize)
.attr('y', borderSize)
.attr('width', cellSize - 2 * borderSize)
.attr('height', cellSize - 2 * borderSize);
cell.append('rect')
.attr('class', 'selection')
.attr('x', 0)
.attr('y', 0)
.attr('width', cellSize)
.attr('height', cellSize);
// Add the axes
svg.append('g')
.attr('class', 'x axis')
.call(pointAxis)
.selectAll('text')
.attr('x', -3)
.attr('y', 5)
.attr('transform', 'rotate(90)')
.style('text-anchor', 'end');
svg.append('g')
.attr('class', 'y axis')
.call(lineAxis);
}
Upvotes: 0
Views: 1387
Reputation: 20821
If you don't need to actually use clipPath
, you could just add a simple bg for x axis like this before you add the axes.
// Add bg for x axis
svg.append('rect')
.attr('height', 30)
.attr('y', -25)
.attr('fill', 'white')
.attr('width', chartWidth);
Plunkr: https://plnkr.co/edit/tVZb7YEaDTMZaFX4Nod3?p=preview
Upvotes: 1