Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save chris-creditdesign/7589b606444cfef15f48 to your computer and use it in GitHub Desktop.
Save chris-creditdesign/7589b606444cfef15f48 to your computer and use it in GitHub Desktop.
A Pen by chris-creditdesign.
<div class="outer-wrapper" id="graphic">
<div class="chart"></div>
<div class="key" id="key"></div>
</div>
function buildData () {
var myArray = [];
for (var i = 0; i < 500; i++) {
myObject = {};
myObject.xValue = Math.random() * 100;
myObject.yValue = Math.random() * 100;
myObject.name = [myObject.xValue, myObject.yValue].toString();
myArray.push(myObject);
}
return myArray;
}
function buildParams () {
var params = {};
/* Margin, Width and height */
params.margin = {top: 20, right: 20, mid: 30, bottom: 50, left: 50};
params.brushThickness = 60;
params.width = $('.outer-wrapper').width() - params.margin.left - params.brushThickness - params.margin.mid - params.margin.right;
params.height = $('.outer-wrapper').width() - params.margin.top - params.margin.mid - params.brushThickness - params.margin.bottom;
/* Global variable to control the length of D3 transitons */
params.duration = 450;
params.delay = 450;
return params;
}
function BuildWidget (target, params, data) {
this.target = target;
this.params = params;
this.data = data;
}
BuildWidget.prototype.buildGraphic = function () {
this.svg = d3.select(this.target + " > .chart").append("svg")
.attr("width", this.params.width + this.params.margin.left + this.params.brushThickness + this.params.margin.mid + this.params.margin.right)
.attr("height", this.params.height + this.params.margin.top + this.params.brushThickness + this.params.margin.mid + this.params.margin.bottom);
var clip = this.svg.append("defs").append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", this.params.width)
.attr("height", this.params.height);
var miniClip = this.svg.append("defs").append("svg:clipPath")
.attr("id", "mini-clip")
.append("svg:rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", this.params.brushThickness)
.attr("height", this.params.brushThickness);
this.scatterGroup = this.svg.append("g")
.attr("class","scatterGroup")
.attr("clip-path", "url(#clip)")
.attr("transform","translate(" + (this.params.margin.left + this.params.brushThickness + this.params.margin.mid) + "," + this.params.margin.top + ")");
this.xBrushGroup = this.svg.append("g")
.attr("class","xBrushGroup")
.attr("transform","translate(" + (this.params.margin.left + this.params.brushThickness + this.params.margin.mid) + "," + (this.params.margin.top + this.params.height + this.params.margin.mid)+ ")");
this.yBrushGroup = this.svg.append("g")
.attr("class","yBrushGroup")
.attr("transform","translate(" + this.params.margin.left + "," + this.params.margin.top + ")");
this.miniMapGroup = this.svg.append("g")
.attr("class","miniMapGroup")
.attr("clip-path", "url(#mini-clip)")
.attr("transform","translate(" + this.params.margin.left + "," + (this.params.margin.top + this.params.height + this.params.margin.mid) + ")");
this.miniMapGroup.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", this.params.brushThickness)
.attr("height", this.params.brushThickness)
.attr("fill","#eee")
.attr("stroke","none");
this.mapperGroup = this.svg.append("g")
.attr("class","miniMapGroup")
.attr("transform","translate(" + this.params.margin.left + "," + (this.params.margin.top + this.params.height + this.params.margin.mid) + ")");
};
BuildWidget.prototype.buildScales = function () {
this.yScale = d3.scale.linear()
.range([this.params.height, 0])
.domain([0,100]);
this.yBrushScale = d3.scale.linear()
.range([this.params.height, 0])
.domain([0,100]);
this.xScale = d3.scale.linear()
.range([0, this.params.width])
.domain([0,100]);
this.xBrushScale = d3.scale.linear()
.range([0, this.params.width])
.domain([0,100]);
this.yMiniMapScale = d3.scale.linear()
.range([this.params.brushThickness, 0])
.domain([0,100]);
this.yMiniMapInvertScale = d3.scale.linear()
.range([this.params.brushThickness, 0])
.domain([100,0]);
this.xMiniMapScale = d3.scale.linear()
.range([0, this.params.brushThickness])
.domain([0,100]);
};
BuildWidget.prototype.buildAxes = function () {
this.yAxis = d3.svg.axis()
.scale(this.yScale)
.tickSize(3,0)
.orient("left");
this.xAxis = d3.svg.axis()
.scale(this.xScale)
.tickSize(3,0)
.orient("bottom");
/* Prepare the y axis */
this.svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + (this.params.margin.left + this.params.brushThickness + this.params.margin.mid) + "," + this.params.margin.top + ")")
.call(this.yAxis);
this.svg.append("g")
.attr("class", "y-brush axis")
.attr("transform", "translate(" + this.params.margin.left + "," + this.params.margin.top + ")")
.call(this.yAxis)
.append("g")
.attr("class", "axisLabel")
.append("text")
.attr("transform", "translate(" + -(this.params.margin.left * 0.6) + "," + (this.params.height / 2) + "), rotate(-90)")
.style("text-anchor", "middle")
.text("Y Axis");
/* Prepare the x axis */
this.svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + (this.params.margin.left + this.params.brushThickness + this.params.margin.mid) + "," + (this.params.margin.top + this.params.height) + ")")
.call(this.xAxis)
this.svg.append("g")
.attr("class", "x-brush axis")
.attr("transform", "translate(" + (this.params.margin.left + this.params.brushThickness + this.params.margin.mid) + "," + (this.params.margin.top + this.params.height + this.params.margin.mid + this.params.brushThickness) + ")")
.call(this.xAxis)
.append("g")
.attr("class","axisLabel")
.append("text")
.attr("transform", "translate(" + (this.params.width / 2) + "," + (this.params.margin.bottom * 0.7) + ")")
.style("text-anchor","middle")
.text("X Axis");
};
BuildWidget.prototype.buildBrush = function () {
var self = this;
var updateView = function () {
self.updateScatterPlot();
self.svg.selectAll(".x.axis").call(self.xAxis);
self.svg.selectAll(".y.axis").call(self.yAxis);
self.updateMiniMap(xBrush.extent(), yBrush.extent());
};
var xDisplay = function () {
var extent = xBrush.extent();
self.xScale.domain(extent);
updateView();
};
var xBrushend = function () {
if (xBrush.extent()[0] === xBrush.extent()[1]) {
d3.select(this).call(xBrush.extent([0, 100]));
self.xScale.domain([0,100]);
updateView();
}
};
var yDisplay = function () {
var extent = yBrush.extent();
self.yScale.domain(extent);
updateView();
};
var yBrushend = function () {
if (yBrush.extent()[0] === yBrush.extent()[1]) {
d3.select(this).call(yBrush.extent([0, 100]));
self.yScale.domain([0,100]);
updateView();
}
};
var xBrush = d3.svg.brush()
.x(this.xBrushScale)
.extent([0, 100])
.on("brush", xDisplay)
.on("brushend", xBrushend);
var yBrush = d3.svg.brush()
.y(this.yBrushScale)
.extent([0,100])
.on("brush", yDisplay)
.on("brushend", yBrushend)
this.xBrushGroup.append("g")
.attr("class", "brush")
.call(xBrush)
.selectAll("rect")
.attr("fill","#999")
.attr("height", this.params.brushThickness );
this.yBrushGroup.append("g")
.attr("class", "brush")
.call(yBrush)
.selectAll("rect")
.attr("fill", "#999")
.attr("width", this.params.brushThickness)
};
BuildWidget.prototype.enterScatterPlot = function (target, main) {
var self = this;
target.selectAll("circle")
.data(this.data, function (d) {
return d.name;
})
.enter()
.append("circle")
.attr("cx", function (d) {
return main ? self.xScale(d.xValue) : self.xMiniMapScale(d.xValue);
})
.attr("cy", function (d) {
return main ? self.yScale(d.yValue) : self.yMiniMapScale(d.yValue);
})
.attr("r", function () {
return main ? (Math.random() * 8) : 0.5;
})
.attr("opacity", 1)
.attr("fill", function(d,i) {
var deg = i * 10;
return "hsl(" + deg + ", 50%, 50%)";
});
};
BuildWidget.prototype.updateScatterPlot = function () {
var self = this;
this.scatterGroup.selectAll("circle")
.attr("cx", function (d) {
return self.xScale(d.xValue);
})
.attr("cy", function (d) {
return self.yScale(d.yValue);
});
};
BuildWidget.prototype.buildMiniMap = function () {
this.mapper = this.mapperGroup.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", this.params.brushThickness)
.attr("height", this.params.brushThickness)
.attr("fill","none")
.attr("stroke","#333")
.attr("storke-width","1px");
};
BuildWidget.prototype.updateMiniMap = function(x, y) {
var self = this;
this.mapper
.attr("x", function() {
return self.xMiniMapScale(x[0]);
})
.attr("y", function() {
var top = 100 - y[1]
return self.yMiniMapInvertScale(top);
})
.attr("width", function () {
var width = x[1] - x[0];
return self.xMiniMapScale(width);
})
.attr("height", function () {
var height = y[1] - y[0];
return self.yMiniMapInvertScale(height);
});
};
function init () {
var params = buildParams();
var graphicData = buildData();
var graphic = new BuildWidget("#graphic", params, graphicData);
graphic.buildGraphic();
graphic.buildScales();
graphic.buildAxes();
graphic.buildBrush();
graphic.enterScatterPlot(graphic.scatterGroup, true);
graphic.enterScatterPlot(graphic.miniMapGroup, false);
graphic.buildMiniMap();
}
$( document ).ready( init );
.outer-wrapper {
width: 600px;
position: relative;
border: 1px solid #c8c7cf;
.axis{
path, line {
fill: none;
stroke: #666;
shape-rendering: crispEdges;
}
text {
font-family: sans-serif;
fill: #666;
font-size: 13px;
shape-rendering: crispEdges;
} // SVG
}
.key {
margin-left: 30px;
}
.palette {
border-left: 20px solid;
padding-left: 4px;
display: inline-block;
margin: 10px;
}
} // outerwrapper
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment