【问题标题】:dagre-d3 js Zoom Fit to all contentsdagre-d3 js 缩放适合所有内容
【发布时间】:2016-04-16 17:13:56
【问题描述】:

我正在使用基于 d3.js 的 dagre-d3。 渲染图表后,我希望它缩放以适应所有内容,并且它必须居中。

如您所见,我可以将其居中,但由于我的身高是 400,我似乎无法适应所有内容。

请注意,我想要一个适用于其他 dagre 情况的解决方案,例如仅缩放到 0.5 就适合这种情况,但这不是我想要的。

JSFIDDLE: https://jsfiddle.net/bababalcksheep/xa9rofm5/8/

代码:

'use strict';
//
// setup Zoom from example http://bl.ocks.org/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)');

【问题讨论】:

    标签: javascript d3.js dagre-d3 dagre


    【解决方案1】:

    适当的默认比例是 svg 高度(或宽度)与缩放容器高度(或宽度)的较小比例。简化一点:

    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);
    

    更新fiddle

    【讨论】:

    • 它适用于给定的示例。但是看到这里jsfiddle.net/bababalcksheep/xa9rofm5/11我似乎无法让它工作。有什么想法吗?我的 svg 可能有太长或太宽的节点。
    • 这似乎是如何工作的,但我无法理解将 wRatio initialScale jsfiddle.net/bababalcksheep/xa9rofm5/12 now svg 与宽度、高度 300 相乘以进行测试 javascript zoom.translate([(width - bBox.width*wRatio* initialScale) / 2, padding / 2]) .scale(hRatio &lt; wRatio ? hRatio : wRatio) .event(svg); 的逻辑
    • 在将节点动态添加到现有图形后,有什么方法可以自动缩放?
    【解决方案2】:

    我有一些基于 d3.v3.js 的代码,很多人都在使用,然后转换为 d3.v5.js(看看最新 d3 中的任何错误修复是否对我的代码有帮助)。 缩放是最难确定如何居中的部分。

    这里是后者d3.v5.js的一个例子:https://jsfiddle.net/armyofda12mnkeys/1burht5j/21/

    和下面的代码用于不同版本之间的比较:

    d3.v3

    var g = new dagreD3.graphlib.Graph({ compound: 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({ compound: 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);
    

    【讨论】:

      【解决方案3】:

      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)
      }
      

      【讨论】:

        猜你喜欢
        • 2018-12-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-04
        • 1970-01-01
        • 2017-05-24
        • 2013-06-16
        相关资源
        最近更新 更多