Skip to content

Instantly share code, notes, and snippets.

@TommyCoin80
Last active November 6, 2017 21:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TommyCoin80/458b73a4b15a7d5ee29ac2bf8b5acf90 to your computer and use it in GitHub Desktop.
Save TommyCoin80/458b73a4b15a7d5ee29ac2bf8b5acf90 to your computer and use it in GitHub Desktop.
MatrixGrid Function for D3 Dashboards
license: mit

I don't like constantly doing math to figure out dimensions when I'm building a dashboard of charts, so I made this helper function. It takes a matrix (array of arrays) as an argument, and divides the svg in the same manner as the matrix is divided by unique cell values.

Built with blockbuilder.org

<!DOCTYPE html>
<meta charset="utf-8">
<head>
<link href='https://fonts.googleapis.com/css?family=Play' rel='stylesheet' type='text/css'>
<style>
body {
margin:auto;
font-family: 'Play', sans-serif;
font-size:100%;
}
text {
font-family: 'Play', sans-serif;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="matrixGrid.js"></script>
<script src="lineChart.js"></script>
<body>
<div id="dashboard"></div>
</body>
<script>
var matrix = [
[1,1,2,2,3,3,3,3],
[1,1,4,4,3,3,3,3],
[5,5,4,4,3,3,3,3]
];
var mg = matrixGrid(matrix,960,500,{top:0,left:0,bottom:0,right:0});
d3.range(1,6,1).forEach(function(d) {
mg.setCellMargin(d,20,40,40,20)
});
mg.draw("dashboard",true,false,true);
d3.range(1,6,1).forEach(function(d) {
lineChart(d)
});
</script>
function lineChart(mgCell) {
width = mg.info(mgCell).width;
height = mg.info(mgCell).height;
margin = mg.info(mgCell).margin;
var g = mg.getCell(mgCell);
var data = d3.range(0,100,1).map(function(d) {
return {x:d,y:Math.pow(d,mgCell)}
})
var x = d3.scaleLinear()
.domain(d3.extent(data, function(d) { return d.x;}))
.range([0,width])
.nice();
var y = d3.scaleLinear()
.domain(d3.extent(data, function(d) { return d.y;}))
.range([height,0])
.nice();
var color = d3.scaleOrdinal(d3.schemeCategory10)
.domain(d3.range(1,6,1))
var line = d3.line()
.x(function(d) { return x(d.x)})
.y(function(d) { return y(d.y)});
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
g.append("g")
.call(d3.axisLeft(y).tickFormat(d3.format('.2m')))
g.selectAll(null)
.data([data])
.enter()
.append("path")
.attr("d", line)
.style("fill","none")
.style("stroke", color(mgCell));
}
function matrixGrid(matrix,width,height,margin) {
var rows = matrix.length,
cols = matrix[0].length,
margin = margin || {top:10,left:10,bottom:10,right:10},
width = width || 960 - margin.left - margin.right,
height = height || 500 - margin.top - margin.bottom,
data = [],
cells = {};
var output = {
setCellMargin:setCellMargin,
info:info,
draw:draw,
getCell:getCell
}
matrix.forEach(function(row,i){
row.forEach(function(cell,j) {
var check = data.filter(function(d) { return d.id == cell});
if(check.length == 0) {
data.push({id:cell, row:i+1, col:j+1, height:1, width:1});
} else {
data.forEach(function(d){
if(d.id == cell) {
if(d.width + d.col - 1 < j+1) {
d.width++;
}
if(d.height + d.row - 1 < i+1) {
d.height++;
}
}
})
}
})
});
data.forEach(function(d) {
d.margin = {top:10,left:10,bottom:10,right:10};
d.width = width/cols*d.width - d.margin.left - d.margin.right;
d.height = height/rows*d.height - d.margin.top - d.margin.bottom;
d.translate = [((d.col - 1)*width/cols + d.margin.left),((d.row-1)*height/rows + d.margin.top)];
});
function setCellMargin(cell,top,left,bottom,right) {
data.filter(function(d) { return d.id == cell}).forEach(function(d) {
d.width = d.width + d.margin.left + d.margin.right - left - right;
d.height = d.height + d.margin.top + d.margin.bottom - top - bottom;
d.translate[0] = d.translate[0] - d.margin.left + left;
d.translate[1] = d.translate[1] - d.margin.top + top;
d.margin = {top:top,bottom:bottom,left:left,right:right};
});
return output;
}
function info(cell) {
return data.filter(function(d) { return d.id == cell;})[0];
}
function draw(elementId, resize, inCanvas, zoomable) {
var svg;
d3.select("#" + elementId).selectAll("*").remove();
if(inCanvas) {
var canvas = d3.select("#" + elementId)
.append("canvas")
.attr("width", width/height*100)
.attr("height", 100)
.style("visibility","hidden")
.style("display","block")
.style("height","100%")
.style("width", "100%")
}
if(resize) {
svg = d3.select("#" + elementId)
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("viewBox","0 0 " + (width + margin.left + margin.right) + " " + (height + margin.top + margin.bottom))
.attr("preserveAspectRatio","xMinYMin meet")
.style("-webkit-user-select","none")
.style("cursor","default")
.append("g")
.attr("transform","translate(" + margin.left + "," + margin.top +")");
} else {
svg = d3.select("#" + elementId)
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("-webkit-user-select","none")
.style("cursor","default")
.append("g")
.attr("transform","translate(" + margin.left + "," + margin.top +")");
}
if(inCanvas) {
d3.select(svg.node().parentNode)
.style("position","absolute")
.style("top",0)
.style("left",0)
}
var allCells = svg.selectAll(".cell")
.data(data)
.enter()
.append("g")
.attr("transform", function(d) {return 'translate('+ d.translate[0] + "," + d.translate[1] + ")"})
.each(function(d) { cells["cell" + d.id] = d3.select(this)});
if(zoomable) {
allCells.on("dblclick", zoom);
}
return output;
var zoomed = false;
function zoom(d) {
if(zoomed) {
if(inCanvas) {
canvas.transition()
.duration(1000)
.attr("width", width/height*100)
.attr("height", 100)
}
d3.select(this.parentNode.parentNode)
.transition()
.duration(1000)
.attr("viewBox","0 0 " + (width + margin.left + margin.right) + " " + (height + margin.top + margin.bottom))
allCells.transition()
.duration(1000)
.style("opacity",1)
} else {
var bb = this.getBBox()
var vb = [0,0,0,0];
vb[0] = d.translate[0] - margin.left - d.margin.left - 10;
vb[1] = d.translate[1] - margin.top - d.margin.top - 10;
vb[2] = d.width + d.margin.left + d.margin.right + 30;
vb[3] = d.height + d.margin.top + d.margin.bottom + 30;
d3.select(this.parentNode.parentNode)
.transition()
.duration(1000)
.attr("viewBox", vb[0] + " " + vb[1] + " " + vb[2] + " " + vb[3])
allCells.transition()
.duration(1000)
.style("opacity",function(e) { return e.id == d.id ? 1 : .1;})
if(inCanvas) {
canvas.transition()
.duration(1000)
.attr("width", (vb[2])/(vb[3])*100)
.attr("height", 100)
}
}
zoomed = !zoomed;
}
}
function getCell(id) {
return cells["cell" + id];
}
return output;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment