Last active
May 8, 2024 19:26
-
-
Save AlessandraSozzi/9aff786dd04515d6b028 to your computer and use it in GitHub Desktop.
D3 drag and drop: manually reorder rows and columns of a matrix
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<head> | |
<style> | |
.background { | |
fill: #fff; | |
} | |
rect { | |
stroke: #fff; | |
stroke-width: 1; | |
} | |
text { | |
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff; | |
cursor: move; | |
} | |
</style> | |
<title> MATRIX DRAG AND DROP </title> | |
</head> | |
<body> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script> | |
var n = 6; | |
var margin = {top: 50, right: 30, bottom: 30, left: 50}, | |
width = 800 - margin.left - margin.right, | |
height = 800 - margin.top - margin.bottom; | |
var y = d3.scale.ordinal().rangeBands([0, height],0,1), | |
x = d3.scale.ordinal().rangeBands([0, width],0,1), | |
c = d3.scale.category20().domain(d3.range(36)); | |
var dragging = {}; | |
var svg = d3.select("body") | |
.append("svg") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.style("margin-left", margin.left + "px") | |
.append("g") | |
.attr("id", "matrix") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
var matrixA = [], | |
matrixB = [], | |
nodes = [{"name": "A"}, | |
{"name": "B"}, | |
{"name": "C"}, | |
{"name": "D"}, | |
{"name": "E"}, | |
{"name":"F"} ], | |
n = nodes.length; | |
nodes.forEach(function(node, i) { | |
node.index = i; | |
node.count = 0; | |
matrixA[i] = d3.range(n).map(function(j) { | |
return {x: j, y: i, z: 1 + (n * i) + j}; }); | |
matrixB[i] = d3.range(n).map(function(j) { | |
return {x: i, y: j, z: (n * j) + 1 + i}; }); | |
}); | |
// Default order | |
var orders = { | |
name: d3.range(n).sort(function(a, b) { return d3.ascending(nodes[a].name, nodes[b].name); }) | |
}; | |
// The default sort order. | |
y.domain(orders.name); | |
x.domain(orders.name); | |
svg.append("rect") | |
.attr("class", "background") | |
.attr("width", width) | |
.attr("height", height); | |
var row = svg.selectAll(".row") | |
.data(matrixA) | |
.enter().append("g") | |
.attr("class", "row") | |
.attr("transform", function(d, i) { | |
return "translate(0," + y(i) + ")"; }) | |
.each(row); | |
var column = svg.selectAll(".column") | |
.data(matrixB) | |
.enter().append("g") | |
.attr("class", "column") | |
.attr("transform", function(d, i) { | |
return "translate(" + x(i) + ")rotate(-90)"; }) | |
.each(column); | |
var drag_behavior = d3.behavior.drag(); | |
var trigger; | |
d3.selectAll(".row").call(d3.behavior.drag() | |
.origin(function(d) { | |
return {y: y(d[0].y)}; | |
}) | |
.on("dragstart", function(d) { | |
trigger = d3.event.sourceEvent.target.className.baseVal; | |
if (trigger == "label") { | |
d3.selectAll(".cellrow").attr("opacity", 1); | |
dragging[d[0].y] = y(d[0].y); | |
// Move the row that is moving on the front | |
sel = d3.select(this); | |
sel.moveToFront(); | |
} | |
}) | |
.on("drag", function(d) { | |
// Hide what is in the back | |
if (trigger == "label") { | |
d3.selectAll(".cellcolumn").attr("opacity", 0); | |
dragging[d[0].y] = Math.min(height, Math.max(0, d3.event.y)); | |
orders.name.sort(function(a, b) { return position(a) - position(b); }); | |
y.domain(orders.name); | |
d3.selectAll(".row").attr("transform", function(d, i) { | |
return "translate(0," + position(d[0].y) + ")"; | |
}); | |
} | |
}) | |
.on("dragend", function(d) { | |
if (trigger == "label") { | |
delete dragging[d[0].y]; | |
transition(d3.select(this)).attr("transform", "translate(0," + y(d[0].y) + ")"); | |
d3.selectAll(".column").each(function(d) { | |
d3.select(this).selectAll(".cellcolumn").attr("x", function(d) { | |
return -y(d.y)-90; }); | |
}); | |
} | |
}) | |
); | |
row.append("text") | |
.attr("class", "label") | |
.attr("x", 60) | |
.attr("y", y.rangeBand() / 2) | |
.attr("dy", ".32em") | |
.attr("text-anchor", "end") | |
.text(function(d, i) { return nodes[i].name; }); | |
d3.selectAll(".column").call(drag_behavior | |
.origin(function(d) { | |
return {x: x(d[0].x)}; | |
}) | |
.on("dragstart", function(d) { | |
trigger = d3.event.sourceEvent.target.className.baseVal; | |
if (trigger == "label") { | |
d3.selectAll(".cellcolumn").attr("opacity", 1); | |
dragging[d[0].x] = x(d[0].x); | |
// Move the column that is moving on the front | |
sel = d3.select(this); | |
sel.moveToFront(); | |
} | |
}) | |
.on("drag", function(d) { | |
// Hide what is in the back | |
// console.log(d3.event.x); | |
// console.log(d3.event.y); | |
if (trigger == "label") { | |
d3.selectAll(".cellrow").attr("opacity", 0); | |
dragging[d[0].x] = Math.min(width, Math.max(0, d3.event.x)); | |
orders.name.sort(function(a, b) { return cPosition(a) - cPosition(b); }); | |
x.domain(orders.name); | |
d3.selectAll(".column").attr("transform", function(d, i) { | |
return "translate(" + cPosition(d[0].x) + ")rotate(-90)"; | |
}); | |
} | |
}) | |
.on("dragend", function(d) { | |
delete dragging[d[0].x]; | |
transition(d3.select(this)).attr("transform", "translate(" + x(d[0].x) + ")rotate(-90)"); | |
d3.selectAll(".row").each(function(d, i) { | |
d3.select(this).selectAll(".cellrow").attr("x", function(d) { | |
return x(d.x); }); | |
}); | |
}) | |
); | |
column.append("text") | |
.attr("class", "label") | |
.attr("x", -60) | |
.attr("y", x.rangeBand() / 2) | |
.attr("dy", ".32em") | |
.attr("text-anchor", "start") | |
.text(function(d, i) { return nodes[i].name; }); | |
function row(row) { | |
var cell = d3.select(this).selectAll(".cellrow") | |
.data(row.filter(function(d) { | |
return d.z; | |
})) | |
.enter().append("rect") | |
.attr("class", "cellrow") | |
.attr("x", function(d) { | |
return y(d.x); | |
}) | |
.attr("width", y.rangeBand()) | |
.attr("height", y.rangeBand()) | |
.style("fill", function(d) { | |
return c(d.z); | |
}); | |
} | |
function column(column) { | |
var cell = d3.select(this).selectAll(".cellcolumn") | |
.data(column.filter(function(d) { | |
return d.z; | |
})) | |
.enter().append("rect") | |
.attr("class", "cellcolumn") | |
.attr("x", function(d) { | |
return -x(d.y)-90; | |
}) | |
.attr("width", x.rangeBand()) | |
.attr("height", x.rangeBand()) | |
.style("fill", function(d) { | |
return c(d.z); | |
}); | |
} | |
d3.selection.prototype.moveToFront = function() { | |
return this.each(function(){ | |
this.parentNode.appendChild(this); | |
}); | |
}; | |
function position(d) { | |
var v = dragging[d]; | |
return v == null ? y(d) : v; | |
} | |
function cPosition(d) { | |
var v = dragging[d]; | |
return v == null ? x(d) : v; | |
} | |
function transition(g) { | |
return g.transition().duration(500); | |
} | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment