I am using dagre-d3 which is based upon d3.js. After my graph is rendered I want it to zoom to fit all contents and it must be centered.
As you can see I can center it alright but I cant seem to fit all contents as my height is 400.
Please note that I want a solution which should be usable to other dagre situation , for example just scaling to 0.5 will fit in this case may be but this is not what i want.
JSFIDDLE: /
CODE:
'use strict';
//
// setup Zoom from example
//
var width = 960,
height = 400,
center = [width / 2, height / 2];
//
var svg = d3.select('svg'),
inner = svg.select('g');
//
var zoom = d3.behavior.zoom()
.translate([0, 0])
.scale(1)
.size([900, 400])
.scaleExtent([1, 8])
.on('zoom', zoomed);
//
svg
.call(zoom) // delete this line to disable free zooming
.call(zoom.event);
function zoomed() {
inner.attr('transform', 'translate(' + zoom.translate() + ')scale(' + zoom.scale() + ')');
}
function interpolateZoom(translate, scale) {
var self = this;
return d3.transition().duration(350).tween('zoom', function () {
var iTranslate = d3.interpolate(zoom.translate(), translate),
iScale = d3.interpolate(zoom.scale(), scale);
return function (t) {
zoom
.scale(iScale(t))
.translate(iTranslate(t));
zoomed();
};
});
}
function zoomClick() {
var clicked = d3.event.target,
direction = 1,
factor = 0.2,
target_zoom = 1,
center = [width / 2, height / 2],
extent = zoom.scaleExtent(),
translate = zoom.translate(),
translate0 = [],
l = [],
view = {
x: translate[0],
y: translate[1],
k: zoom.scale()
};
d3.event.preventDefault();
direction = (this.id === 'zoom_in') ? 1 : -1;
target_zoom = zoom.scale() * (1 + factor * direction);
if (target_zoom < extent[0] || target_zoom > extent[1]) {
return false;
}
translate0 = [(center[0] - view.x) / view.k, (center[1] - view.y) / view.k];
view.k = target_zoom;
l = [translate0[0] * view.k + view.x, translate0[1] * view.k + view.y];
view.x += center[0] - l[0];
view.y += center[1] - l[1];
interpolateZoom([view.x, view.y], view.k);
}
d3.selectAll('button').on('click', zoomClick);
//
//
//
// tcp-state-diagram EXAMPLE
//
// Create a new directed graph
var g = new dagreD3.graphlib.Graph().setGraph({});
// States and transitions from RFC 793
var states = ['CLOSED', 'LISTEN', 'SYN RCVD', 'SYN SENT',
'ESTAB', 'FINWAIT-1', 'CLOSE WAIT', 'FINWAIT-2',
'CLOSING', 'LAST-ACK', 'TIME WAIT'
];
// Automatically label each of the nodes
states.forEach(function (state) {
g.setNode(state, {
label: state
});
});
// Set up the edges
g.setEdge('CLOSED', 'LISTEN', {
label: 'open'
});
g.setEdge('LISTEN', 'SYN RCVD', {
label: 'rcv SYN'
});
g.setEdge('LISTEN', 'SYN SENT', {
label: 'send'
});
g.setEdge('LISTEN', 'CLOSED', {
label: 'close'
});
g.setEdge('SYN RCVD', 'FINWAIT-1', {
label: 'close'
});
g.setEdge('SYN RCVD', 'ESTAB', {
label: 'rcv ACK of SYN'
});
g.setEdge('SYN SENT', 'SYN RCVD', {
label: 'rcv SYN'
});
g.setEdge('SYN SENT', 'ESTAB', {
label: 'rcv SYN, ACK'
});
g.setEdge('SYN SENT', 'CLOSED', {
label: 'close'
});
g.setEdge('ESTAB', 'FINWAIT-1', {
label: 'close'
});
g.setEdge('ESTAB', 'CLOSE WAIT', {
label: 'rcv FIN'
});
g.setEdge('FINWAIT-1', 'FINWAIT-2', {
label: 'rcv ACK of FIN'
});
g.setEdge('FINWAIT-1', 'CLOSING', {
label: 'rcv FIN'
});
g.setEdge('CLOSE WAIT', 'LAST-ACK', {
label: 'close'
});
g.setEdge('FINWAIT-2', 'TIME WAIT', {
label: 'rcv FIN'
});
g.setEdge('CLOSING', 'TIME WAIT', {
label: 'rcv ACK of FIN'
});
g.setEdge('LAST-ACK', 'CLOSED', {
label: 'rcv ACK of FIN'
});
g.setEdge('TIME WAIT', 'CLOSED', {
label: 'timeout=2MSL'
});
// Set some general styles
g.nodes().forEach(function (v) {
var node = g.node(v);
node.rx = node.ry = 5;
});
// Add some custom colors based on state
g.node('CLOSED').style = 'fill: #f77';
g.node('ESTAB').style = 'fill: #7f7';
// Create the renderer
var render = new dagreD3.render();
// Run the renderer. This is what draws the final graph.
render(inner, g);
// Center the graph
var initialScale = 0.75;
var _height = svg.attr('height') - g.graph().height;
var _width = svg.attr('width') - g.graph().width;
console.log(height / _height);
zoom.translate([(svg.attr('width') - g.graph().width * initialScale) / 2, 10]).scale(1).event(svg);
//svg.transition().duration(750).call(zoom.translate([0, 0]).scale(1).event);
//svg.transition().duration(500).attr('transform', 'scale(0.75) translate(0,0)');
I am using dagre-d3 which is based upon d3.js. After my graph is rendered I want it to zoom to fit all contents and it must be centered.
As you can see I can center it alright but I cant seem to fit all contents as my height is 400.
Please note that I want a solution which should be usable to other dagre situation , for example just scaling to 0.5 will fit in this case may be but this is not what i want.
JSFIDDLE: https://jsfiddle/bababalcksheep/xa9rofm5/8/
CODE:
'use strict';
//
// setup Zoom from example http://bl.ocks/mgold/f61420a6f02adb618a70
//
var width = 960,
height = 400,
center = [width / 2, height / 2];
//
var svg = d3.select('svg'),
inner = svg.select('g');
//
var zoom = d3.behavior.zoom()
.translate([0, 0])
.scale(1)
.size([900, 400])
.scaleExtent([1, 8])
.on('zoom', zoomed);
//
svg
.call(zoom) // delete this line to disable free zooming
.call(zoom.event);
function zoomed() {
inner.attr('transform', 'translate(' + zoom.translate() + ')scale(' + zoom.scale() + ')');
}
function interpolateZoom(translate, scale) {
var self = this;
return d3.transition().duration(350).tween('zoom', function () {
var iTranslate = d3.interpolate(zoom.translate(), translate),
iScale = d3.interpolate(zoom.scale(), scale);
return function (t) {
zoom
.scale(iScale(t))
.translate(iTranslate(t));
zoomed();
};
});
}
function zoomClick() {
var clicked = d3.event.target,
direction = 1,
factor = 0.2,
target_zoom = 1,
center = [width / 2, height / 2],
extent = zoom.scaleExtent(),
translate = zoom.translate(),
translate0 = [],
l = [],
view = {
x: translate[0],
y: translate[1],
k: zoom.scale()
};
d3.event.preventDefault();
direction = (this.id === 'zoom_in') ? 1 : -1;
target_zoom = zoom.scale() * (1 + factor * direction);
if (target_zoom < extent[0] || target_zoom > extent[1]) {
return false;
}
translate0 = [(center[0] - view.x) / view.k, (center[1] - view.y) / view.k];
view.k = target_zoom;
l = [translate0[0] * view.k + view.x, translate0[1] * view.k + view.y];
view.x += center[0] - l[0];
view.y += center[1] - l[1];
interpolateZoom([view.x, view.y], view.k);
}
d3.selectAll('button').on('click', zoomClick);
//
//
//
// tcp-state-diagram EXAMPLE
//
// Create a new directed graph
var g = new dagreD3.graphlib.Graph().setGraph({});
// States and transitions from RFC 793
var states = ['CLOSED', 'LISTEN', 'SYN RCVD', 'SYN SENT',
'ESTAB', 'FINWAIT-1', 'CLOSE WAIT', 'FINWAIT-2',
'CLOSING', 'LAST-ACK', 'TIME WAIT'
];
// Automatically label each of the nodes
states.forEach(function (state) {
g.setNode(state, {
label: state
});
});
// Set up the edges
g.setEdge('CLOSED', 'LISTEN', {
label: 'open'
});
g.setEdge('LISTEN', 'SYN RCVD', {
label: 'rcv SYN'
});
g.setEdge('LISTEN', 'SYN SENT', {
label: 'send'
});
g.setEdge('LISTEN', 'CLOSED', {
label: 'close'
});
g.setEdge('SYN RCVD', 'FINWAIT-1', {
label: 'close'
});
g.setEdge('SYN RCVD', 'ESTAB', {
label: 'rcv ACK of SYN'
});
g.setEdge('SYN SENT', 'SYN RCVD', {
label: 'rcv SYN'
});
g.setEdge('SYN SENT', 'ESTAB', {
label: 'rcv SYN, ACK'
});
g.setEdge('SYN SENT', 'CLOSED', {
label: 'close'
});
g.setEdge('ESTAB', 'FINWAIT-1', {
label: 'close'
});
g.setEdge('ESTAB', 'CLOSE WAIT', {
label: 'rcv FIN'
});
g.setEdge('FINWAIT-1', 'FINWAIT-2', {
label: 'rcv ACK of FIN'
});
g.setEdge('FINWAIT-1', 'CLOSING', {
label: 'rcv FIN'
});
g.setEdge('CLOSE WAIT', 'LAST-ACK', {
label: 'close'
});
g.setEdge('FINWAIT-2', 'TIME WAIT', {
label: 'rcv FIN'
});
g.setEdge('CLOSING', 'TIME WAIT', {
label: 'rcv ACK of FIN'
});
g.setEdge('LAST-ACK', 'CLOSED', {
label: 'rcv ACK of FIN'
});
g.setEdge('TIME WAIT', 'CLOSED', {
label: 'timeout=2MSL'
});
// Set some general styles
g.nodes().forEach(function (v) {
var node = g.node(v);
node.rx = node.ry = 5;
});
// Add some custom colors based on state
g.node('CLOSED').style = 'fill: #f77';
g.node('ESTAB').style = 'fill: #7f7';
// Create the renderer
var render = new dagreD3.render();
// Run the renderer. This is what draws the final graph.
render(inner, g);
// Center the graph
var initialScale = 0.75;
var _height = svg.attr('height') - g.graph().height;
var _width = svg.attr('width') - g.graph().width;
console.log(height / _height);
zoom.translate([(svg.attr('width') - g.graph().width * initialScale) / 2, 10]).scale(1).event(svg);
//svg.transition().duration(750).call(zoom.translate([0, 0]).scale(1).event);
//svg.transition().duration(500).attr('transform', 'scale(0.75) translate(0,0)');
Share
Improve this question
asked Apr 16, 2016 at 17:13
djangodjango
2,9096 gold badges51 silver badges83 bronze badges
3 Answers
Reset to default 2I had some code based on the d3.v3.js which alot of people were using, then converted to d3.v5.js (to see if any bug fixes in latest d3 would help my code). The zoom was the hardest part to figure out how to center.
Here is an example of the latter d3.v5.js: https://jsfiddle/armyofda12mnkeys/1burht5j/21/
and code below for parison between the different versions:
d3.v3
var g = new dagreD3.graphlib.Graph({ pound: false, multigraph: true }).setGraph({});
//I have various code here to draw the graph dynamically with setNode() setEdge()
var svg = d3.select("svg"),
inner = svg.select("g");
// Set the rankdir
g.graph().rankdir = 'TB';//'TB' (aka vertical) or 'LR' (aka horizontal)
g.graph().nodesep = 50;
// Set up zoom support
var zoom = d3.zoom().on("zoom", function() {
inner.attr("transform", "translate(" + d3.event.translate + ")" +
"scale(" + d3.event.scale + ")");
console.log('zoom!');
});
svg.call(zoom); //not sure what purpose of this initial zoom call is, still need the 2nd piece of code to fit upon landing on the page
// Create the renderer
var render = new dagreD3.render();
// Run the renderer. This is what draws the final graph.
render(inner, g);
//set the 'fit to content graph' upon landing on the page
var initialScale = 0.75;
zoom
.translate([(svg.attr("width") - g.graph().width * initialScale) / 2, 20])
.scale(initialScale)
.event(svg);
svg.attr('height', g.graph().height * initialScale + 40);
d3.v5
var g = new dagreD3.graphlib.Graph({ pound: false, multigraph: true }).setGraph({});
//I have various code here to draw the graph dynamically with setNode() setEdge()
var svg = d3.select("svg"),
inner = svg.select("g");
// Set the rankdir
g.graph().rankdir = 'TB';//'TB' (aka vertical) or 'LR' (aka horizontal)
g.graph().nodesep = 50;
var zoom = d3.zoom()
//.scaleExtent([0, 20]) //removed for now, acts funky
.on("zoom", function() {
inner.attr("transform", d3.event.transform);
//dynamically resets height (for dynamic 'scrollbar sizing') if thats wanted:
//svg.attr('height', g.graph().height * d3.event.transform.k * 1.25 ); //make a tiny bit bigger so you can see the whole thing with a little space
console.log('zoom!'+ d3.event.transform.x +','+ d3.event.transform.y +':'+ d3.event.transform.k + ', width: '+ g.graph().width + ', height: '+ g.graph().height);
});
svg.call(zoom); //not sure what purpose of this initial zoom call is, still need the 2nd piece of code to fit upon landing on the page
//... render the graph
// Create the renderer
var render = new dagreD3.render();
render(inner, g);
//...
//set the 'fit to content graph' upon landing on the page
var initialScale = 0.75;
let transform = d3.zoomIdentity
.translate( (svg.attr("width") - g.graph().width * initialScale) / 2, 20)
.scale(initialScale);
let transitionDuration = 5000;
inner //or svg, doesn't seem to matter
.transition()
.duration(transitionDuration || 0) // milliseconds
.call(zoom.transform, transform);
svg.attr('height', g.graph().height * initialScale + 40);
d3 v6
const $svg = d3.select(svg)
const $container = $svg.append("g")
// Setup d3-zoom
const zoom = d3.zoom().on("zoom", (event) => {
$container.attr("transform", event.transform)
})
$svg.call(zoom)
// Render graph
const dagre_render = new dagre.render({})
const graph = new dagre.graphlib.Graph(...)
dagre_render($container, graph)
// Zoom to fit
const { width, height } = $container.node().getBBox()
if (width && height) {
const scale = Math.min(svg.clientWidth / width, svg.clientHeight / height) * 0.95
zoom.scaleTo($svg, scale)
zoom.translateTo($svg, width / 2, height / 2)
}
The appropriate default scale would be the lesser ratio of the svg height (or width) to the zoom containers height (or width). Simplifying a bit:
var padding = 20,
bBox = inner.node().getBBox(),
hRatio = height / (bBox.height + padding),
wRatio = width / (bBox.width + padding);
zoom.translate([(width - bBox.width * initialScale) / 2, padding / 2])
.scale(hRatio < wRatio ? hRatio : wRatio)
.event(svg);
Updated fiddle.
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744791237a4593927.html
评论列表(0条)