Skip to content

Instantly share code, notes, and snippets.

@pbogden
Last active December 19, 2015 20:58
Show Gist options
  • Save pbogden/6016415 to your computer and use it in GitHub Desktop.
Save pbogden/6016415 to your computer and use it in GitHub Desktop.
fit-to-height x-y plot

Simple x-y plot with axes, labels & margins (and a transition). Plot region resizes to fit window.innerHeight.

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<link rel="stylesheet" href="styles.css" type="text/css">
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<script>
var defaultWidth = 960, // Default width & height of plot region
defaultHeight = 500,
defaultMinHeight = 150,
padding = 0.1, // fraction to pad maximum of range for y-axis
paddedExtent; // padded extent that results from using padding
var outerWidth, // dimensions of plot area
outerHeight,
marginWidth=50, // margins -- the same on all sides
strokeWidth=1; // this should be consistent with <style>
// viewport <meta> info -- diagnostic only
var viewportMeta = document.getElementsByName("viewport").content, // <meta> "content" string
viewportWidth = document.documentElement.clientWidth, // *This* gets viewport width
viewportHeight = document.documentElement.clientHeight; // *This* gets viewport height
var width = (window.innerWidth > 0) ? window.innerWidth : defaultWidth;
var height = (window.innerHeight > 0) ? window.innerHeight : defaultHeight;
outerWidth = Math.min(defaultWidth,width);
outerHeight = Math.min(defaultHeight,height);
outerHeight = Math.max(defaultMinHeight,height);
// Note: on iPhone in portrait: viewportWidth = window.innerWidth = screen.width = 320
// '' '' landscape: viewportWidth = window.innerWidth = 568 BUT screen.width = 320
// on desktop: screen.width = 1680 > viewportWidth = window.innerWidth (= variable)
alert("viewport Width & Height:\n" + viewportWidth + ", " + viewportHeight + "\n" +
"window.innerWidth & innerHeight:\n" + window.innerWidth + ", " + window.innerHeight + "\n" +
"screen.width & height:\n" + screen.width + ", " + screen.height + "\n" +
"width & height for D3:\n" + width + ", " + height);
// More plot dimensions (computed from previous)
var marginLeft = marginWidth+strokeWidth/2, // leftmost point inside the margins
marginTop = marginWidth+strokeWidth/2, // topmost point inside the margins
width = outerWidth -2*marginLeft, // width of inner plot area *and* margins
height = outerHeight-2*marginTop; // height of inner plot area *and* margins
// Eliminate default margin from <body> element
document.body.style.margin="0px";
// Check aspect ratio and resize accordingly
// TBD
var data = [
{x: 0, y: 00},
{x: 1, y: 30},
{x: 2, y: 40},
{x: 3, y: 20},
{x: 4, y: 90},
{x: 5, y: 70}
];
function xValue(d) { return d.x; } // accessors for data
function yValue(d) { return d.y; }
var x = d3.scale.linear() // interpolator for X axis -- inner plot region
.domain(d3.extent(data,xValue)) // equivalent to .domain([0,1.1*5])
.range([0,width]); // range is inner plot area (i.e., inside margins)
// pads extent of graph above the max y value; makes it easier to select max value w/brush
paddedExtent = d3.extent(data,yValue);
paddedExtent[1] += padding*(paddedExtent[1]-paddedExtent[0]);
var y = d3.scale.linear() // interpolator for Y axis -- inner plot region
.domain(paddedExtent) // use the padded yxtent for plotting "y"
.range([height,0]); // remember, (0,0) is upper left -- this reverses "y"
var svg = d3.svg;
var line = svg.line() // SVG line generator
.x(function(d) { return x(d.x); } )
.y(function(d) { return y(d.y); } );
var xAxis = svg.axis() // x Axis
.scale(x)
.ticks(5) // request 5 ticks on the x axis
.orient("bottom");
var yAxis = svg.axis() // y Axis
.scale(y)
.ticks(4)
.orient("left");
var svg = d3.select("body").append("svg"); // "svg" points to the SVG node in the DOM
svg.attr("width", outerWidth) // SVG domain is the outer plot area (i.e., inner area plus margins)
.attr("height", outerHeight); // Note: ok to leave this without units, implied "px"
var g = svg.append("g") // <g> element is the inner plot area (i.e., inside the margins)
.attr("transform", "translate(" + marginLeft + "," + marginTop + ")");
g.append("g") // render the Y axis in the inner plot area
.attr("class", "y axis")
.call(yAxis);
g.append("g") // render the X axis in the inner plot area
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")") // axis runs along lower part of graph
.call(xAxis);
g.append("text") // inner x-axis label
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("x", width - 6)
.attr("y", height - 6)
.text("inner x-axis label");
g.append("text") // outer x-axis label
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("x", width/2)
.attr("y", height + 2*marginWidth/3 + 6)
.text("outer x-axis label");
g.append("text") // inner y-axis label
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("x", -6)
.attr("y", 6)
.attr("dy", ".75em")
.attr("transform", "rotate(-90)")
.text("inner y-axis label");
g.append("text") // outer y-axis label
.attr("class", "x label")
.attr("text-anchor", "middle")
.attr("x", -height/2)
.attr("y", -6 - marginWidth/3)
.attr("dy", "-.75em")
.attr("transform", "rotate(-90)")
.text("outer y-axis label");
g.append("path") // plot the data as a line
.datum(data)
.attr("class", "line")
.attr("d", line);
g.append("rect") // plot a rectangle that encloses the inner plot area
.attr("width", width)
.attr("width", width)
.attr("height", height);
svg.append("circle") // plot a circle in the upper left of the SVG element
.attr("cx", 0)
.attr("cy", 0)
.attr("r", 10);
svg.append("circle") // plot a circle in the lower right of the SVG element
.attr("cx", outerWidth)
.attr("cy", outerHeight)
.attr("r", 10);
g.selectAll(".dot") // plot a circle at each data location
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", function(d) { return x(d.x); } )
.attr("cy", function(d) { return y(d.y); } )
.attr("r", 5);
svg.append("rect") // plot a rectangle that encloses the entire SVG element
.attr("x", 0)
.attr("y", 0)
.attr("width", outerWidth)
.attr("height", outerHeight);
d3.selectAll("path").transition() // data transition
.style("stroke", "steelblue")
.delay(1000)
.duration(2000);
</script>
</body>
</html>
.line {
fill: none;
stroke: white; // starts invisible, made visible with transition
stroke-width: 1px; // half is inside the margin, half is outside
}
rect {
fill: none;
stroke: black;
stroke-width: 1px; // half is inside the margin, half is outside
}
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
<svg width="400px" height="300px" viewBox="0 0 400 300"
xmlns="http://www.w3.org/2000/svg">
<desc>This example uses the 'switch' element to provide a
fallback graphical representation of a paragraph, if
XHTML is not supported.</desc>
<!-- The 'switch' element will process the first child element
whose testing attributes evaluate to true.-->
<switch>
<!-- Process the embedded XHTML if the requiredExtensions attribute
evaluates to true (i.e., the user agent supports XHTML
embedded within SVG). -->
<foreignObject width="100" height="50"
requiredExtensions="http://www.w3.org/1999/xhtml">
<!-- XHTML content goes here -->
<body xmlns="http://www.w3.org/1999/xhtml">
<p>Here is a paragraph that requires word wrap</p>
</body>
</foreignObject>
<!-- Else, process the following alternate SVG.
Note that there are no testing attributes on the 'text' element.
If no testing attributes are provided, it is as if there
were testing attributes and they evaluated to true.-->
<text font-size="10" font-family="Verdana">
<tspan x="10" y="10">Here is a paragraph that</tspan>
<tspan x="10" y="20">requires word wrap.</tspan>
</text>
</switch>
</svg>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment