This example shows one way to make responsive density-based linear axes using D3 4.0 and ReactiveModel. Combines Margin Convention II with ReactiveModel with Density-based Ticks. To experience the resize behavior, run this example full-screen and resize the browser.
Last active
June 13, 2016 15:45
Responsive Axes with ReactiveModel
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
license: mit |
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> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Margin Convention II with ReactiveModel</title> | |
<script src="//d3js.org/d3.v4.0.0-alpha.49.min.js"></script> | |
<script src="//datavis-tech.github.io/reactive-model/reactive-model-v0.11.0.min.js"></script> | |
<style> | |
/* Make the chart container fill the page using CSS. */ | |
#chart-container { | |
position: fixed; | |
left: 0px; | |
right: 0px; | |
top: 0px; | |
bottom: 0px; | |
} | |
.axis text { | |
font: 16pt sans-serif; | |
} | |
.axis .label { | |
font: 24pt sans-serif; | |
} | |
.axis path { | |
display: none; | |
} | |
.tick line { | |
fill: none; | |
stroke: #000; | |
stroke-width: 1px; | |
shape-rendering: crispEdges; | |
} | |
</style> | |
</head> | |
<body> | |
<!-- The SVG graphics will be injected into this div. --> | |
<div id="chart-container"></div> | |
<script> | |
// Resizes the SVG container. | |
function SVG(my){ | |
my("svg") | |
("width", 100) | |
("height", 100) | |
(function (svg, width, height){ | |
svg | |
.attr("width", width) | |
.attr("height", height); | |
}, "svg, width, height"); | |
} | |
// Encapsulates the margin convention. | |
function Margin(my){ | |
my("marginTop", 50) | |
("marginBottom", 50) | |
("marginLeft", 50) | |
("marginRight", 50) | |
("innerWidth", function (width, marginLeft, marginRight){ | |
return width - marginLeft - marginRight; | |
}, "width, marginLeft, marginRight") | |
("innerHeight", function (height, marginTop, marginBottom){ | |
return height - marginTop - marginBottom; | |
}, "height, marginTop, marginBottom") | |
("g", function (svg){ | |
return svg.append("g"); | |
}, "svg") | |
(function (g, marginLeft, marginTop){ | |
g.attr("transform", "translate(" + marginLeft + "," + marginTop + ")"); | |
}, "g, marginLeft, marginTop"); | |
} | |
// Adds a gray rectangle inside the margin. | |
function GrayRectangle(my){ | |
my("rect", function (g){ | |
return g.append("rect") | |
.attr("fill", "lightgray") | |
.attr("stroke", "gray"); | |
}, "g") | |
(function (rect, innerWidth, innerHeight){ | |
rect | |
.attr("width", innerWidth) | |
.attr("height", innerHeight); | |
}, "rect, innerWidth, innerHeight"); | |
} | |
// Tracks the X scale on the model. | |
function XScale(my){ | |
var scale = d3.scaleLinear(); | |
my("xScaleDomain") | |
("xScaleRange", function (innerWidth){ | |
return [0, innerWidth]; | |
}, "innerWidth") | |
("xScale", function(xScaleDomain, xScaleRange){ | |
return scale | |
.domain(xScaleDomain) | |
.range(xScaleRange); | |
}, "xScaleDomain, xScaleRange"); | |
} | |
// Tracks the Y scale on the model. | |
function YScale(my){ | |
var scale = d3.scaleLinear(); | |
my("yScaleDomain") | |
("yScaleRange", function (innerHeight){ | |
return [innerHeight, 0]; | |
}, "innerHeight") | |
("yScale", function(yScaleDomain, yScaleRange){ | |
return scale | |
.domain(yScaleDomain) | |
.range(yScaleRange); | |
}, "yScaleDomain, yScaleRange"); | |
} | |
// Creates the X axis. | |
function XAxis(my){ | |
var axis = d3.axisBottom(); | |
// Approximate number of pixels between ticks. | |
my("xAxisTickSpacing", 70) | |
("xAxisG", function (g){ | |
return g.append("g") | |
.attr("class", "x axis"); | |
}, "g") | |
(function(xAxisG, innerHeight){ | |
xAxisG.attr("transform", "translate(0," + innerHeight + ")"); | |
}, "xAxisG, innerHeight") | |
("xAxisTicks", function (xAxisTickSpacing, innerWidth){ | |
return innerWidth / xAxisTickSpacing; | |
}, "xAxisTickSpacing, innerWidth") | |
("xAxis", function(xAxisTicks, xScale){ | |
return axis | |
.scale(xScale) | |
.ticks(xAxisTicks); | |
}, "xAxisTicks, xScale") | |
(function(xAxisG, xAxis){ | |
xAxis(xAxisG); | |
}, "xAxisG, xAxis"); | |
} | |
// Creates the X axis. | |
function YAxis(my){ | |
var axis = d3.axisLeft(); | |
// Approximate number of pixels between ticks. | |
my("yAxisTickSpacing", 40) | |
("yAxisG", function (g){ | |
return g.append("g") | |
.attr("class", "y axis"); | |
}, "g") | |
("yAxisTicks", function (yAxisTickSpacing, innerHeight){ | |
return innerHeight / yAxisTickSpacing; | |
}, "yAxisTickSpacing, innerHeight") | |
("yAxis", function(yAxisTicks, yScale){ | |
return axis | |
.scale(yScale) | |
.ticks(yAxisTicks); | |
}, "yAxisTicks, yScale") | |
(function(yAxisG, yAxis){ | |
yAxis(yAxisG); | |
}, "yAxisG, yAxis") | |
} | |
// The constructor for an "axis visualization" component. | |
// Renders a gray rectangle with responsive axes. | |
function AxesVis(){ | |
return ReactiveModel() | |
.call(SVG) | |
.call(Margin) | |
.call(XScale) | |
.call(XAxis) | |
.call(YScale) | |
.call(YAxis) | |
.call(GrayRectangle) | |
; | |
} | |
// Respond to resize by setting width and height from DOM element. | |
function Resize(my, el){ | |
function resize(){ | |
my.width(el.clientWidth) | |
.height(el.clientHeight); | |
} | |
resize(); | |
window.addEventListener("resize", resize); | |
} | |
// The main program that uses the MarginVis component. | |
function main(){ | |
// Set up the MarginVis instance. | |
var container = d3.select("#chart-container"), | |
axesVis = AxesVis() | |
.svg(container.append("svg")) | |
.call(Resize, container.node()) | |
.xScaleDomain([0, 1000]) | |
.yScaleDomain([0, 1000]); | |
// Change the margins around in a funky way. | |
var time = 0; | |
requestAnimationFrame(function updateMargin(){ | |
time += 0.01; | |
axesVis | |
.marginLeft( (Math.sin(time ) + 1.4) * 200) | |
.marginRight( (Math.sin(time * 2 ) + 1.2) * 200) | |
.marginTop( (Math.sin(time * 3 ) + 1.5) * 100) | |
.marginBottom((Math.sin(time * 4 ) + 1.5) * 100); | |
requestAnimationFrame(updateMargin); | |
}); | |
} | |
main(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment