Skip to content

Instantly share code, notes, and snippets.

@curran
Last active June 13, 2016 15:45
Show Gist options
  • Save curran/735c8063f00c773ef1dea78f62a321fa to your computer and use it in GitHub Desktop.
Save curran/735c8063f00c773ef1dea78f62a321fa to your computer and use it in GitHub Desktop.
Margin Convention II with ReactiveModel
license: mit


The data flow graph for this example (made with graph-diagrams).

This example shows a variation of Margin Convention with ReactiveModel that encapsulates functionality using "reactive mixins" and reactiveModel.call. This demonstrates how property definitions and pieces of reactive behavior can be organized and potentially split off into separate modules. The purpose of this is to allow common patterns used by many data visualizations to be encapsulated as reusable modules, allowing the complexity of visualizations to grow while still being manageable and well organized. The model.call(fn) method was added in release v0.7.0, inspired by call() in d3-selection.

See also

web counter
<!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;
}
</style>
</head>
<body>
<!-- The SVG graphics will be injected into this div. -->
<div id="chart-container"></div>
<script>
// Resizes the SVG container.
function ReactiveSVG(my){
my("svg")
("width", 100)
("height", 100)
("svg-width", function (svg, width){
svg.attr("width", width);
}, "svg, width")
("svg-height", function (svg, height){
svg.attr("height", height);
}, "svg, height");
}
// Encapsulates the margin convention.
function ReactiveMargin(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")
("g-transform", 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")
("rect-width", function (rect, innerWidth){
rect.attr("width", innerWidth);
}, "rect, innerWidth")
("rect-height", function (rect, innerHeight){
rect.attr("height", innerHeight);
}, "rect, innerHeight");
}
// The constructor for a "margin visualization" component.
// Renders a gray rectangle to show the configured margin.
function MarginVis(){
return ReactiveModel()
.call(ReactiveSVG)
.call(ReactiveMargin)
.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"),
marginVis = MarginVis()
.svg(container.append("svg"))
.call(Resize, container.node());
// Change the margins around in a funky way.
d3.timer(function updateMargin(time){
time /= 2000;
marginVis
.marginLeft( (Math.sin(time ) + 1.2) * 100)
.marginRight( (Math.sin(time * 2 ) + 1.2) * 100)
.marginTop( (Math.sin(time * 3 ) + 1.5) * 50)
.marginBottom((Math.sin(time * 4 ) + 1.5) * 50);
});
}
main();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment