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.
| <!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