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
xxxxxxxxxx
<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>
https://d3js.org/d3.v4.0.0-alpha.49.min.js
https://datavis-tech.github.io/reactive-model/reactive-model-v0.11.0.min.js