【问题标题】:d3.js change zoom behavior to semantic zoomd3.js 将缩放行为更改为语义缩放
【发布时间】:2016-03-17 15:23:53
【问题描述】:

我正在使用 d3.js 进行一些关于缩放的测试。目前,我已经在测试中成功实现了几何缩放,但它有一个缺点:缩放后的​​g 下的元素正在被缩放。据我了解,这可以通过使用semantic zooming 来解决。

问题是我在测试中需要scale,因为我正在将它与jQuery.UI 滑块value 同步。

另一方面,我希望在缩放操作后调整 text 元素的大小以保持其大小。

我有一个我当前尝试的示例here

我无法更改我的代码以适应此目的。任何人都可以分享一些见解/想法吗?

【问题讨论】:

    标签: javascript d3.js zooming


    【解决方案1】:

    对于您的解决方案,我合并了 2 个示例:

    代码sn-ps:

    function zoom() {
      text.attr("transform", transform);
      var scale = zoombehavior.scale();
      //to make the scale rounded to 2 decimal digits
      scale = Math.round(scale * 100) / 100;
      //setting the slider to the new value
      $("#slider").slider( "option", "value", scale );
      //setting the slider text to the new value
      $("#scale").val(scale);
    }
    //note here we are not handling the scale as its Semantic Zoom
    function transform(d) {
      //translate string
      return "translate(" + x(d[0]) + "," + y(d[1]) + ")";
    }
    
    function interpolateZoom(translate, scale) {
      zoombehavior
            .scale(scale)//we are setting this zoom only for detecting the scale for slider..we are not zoooming using scale.
            .translate(translate);
          zoom();
    }
    var slider = $(function() {
      $("#slider").slider({
        value: zoombehavior.scaleExtent()[0],//setting the value
        min: zoombehavior.scaleExtent()[0],//setting the min value
        max: zoombehavior.scaleExtent()[1],//settinng the ax value
        step: 0.01,
        slide: function(event, ui) {
          var newValue = ui.value;
          var  center = [centerX, centerY],
            extent = zoombehavior.scaleExtent(),
            translate = zoombehavior.translate(),
            l = [],
            view = {
              x: translate[0],
              y: translate[1],
              k: zoombehavior.scale()
            };
          //translate w.r.t the center
          translate0 = [(center[0] - view.x) / view.k, (center[1] - view.y) / view.k];
          view.k = newValue;//the scale as per the slider
          //the translate after the scale(so we are multiplying the translate)
          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);
    
        }
      });
    });
    

    我正在缩放 w.r.t。 250,250 是剪辑圆的中心。

    工作代码here(已添加必要的cmets)

    希望这会有所帮助!

    【讨论】:

    • 您制作的东西符合我的预期,但不是基于我的示例。它有很多有效的观点,我谢谢你,但我正在寻找关于如何修改我的示例的更详细的解释,而不是完全改变它。抱歉,我在问题/赏金中不太清楚。
    • @Joum 你的例子是非常几何缩放中心 60% 的代码被删除了 b'coz 其余的我刚刚优化......我认为它工作顺利,我真的没有找到任何问题。在网上没有任何帮助(因为没有人尝试过这个)它是重写一个工作示例的任何一种方式..:)
    【解决方案2】:

    要做你想做的事,你需要先稍微重构一下代码。对于 d3,最好使用 data() 将项目附加到选择中,而不是使用 for 循环。

    所以这个:

    for(i=0; i<7; i++){
    	pointsGroup.append("text")
      			  .attr("x", function(){
                  	var plusOrMinus = Math.random() < 0.5 ? -1 : 1;
                    var randx = Math.random();
                    return Math.floor(plusOrMinus*randx*75)+centerx;
                  })
                  .attr("y", function(){
                  	var plusOrMinus = Math.random() < 0.5 ? -1 : 1;
                    var randy = Math.random();
                    return Math.floor(plusOrMinus*randy*75)+centery;
                  })
                  .html("star")
                  .attr("class", "point material-icons")
                  .on("click", function(){console.log("click!");});
    }

    变成这个

    var arr = [];
    
    for(i=0; i<7; i++){
      var plusOrMinus = Math.random() < 0.5 ? -1 : 1;
      var randx = Math.random();
      var x = Math.floor(plusOrMinus*randx*75)+centerx; 
      
      var plusOrMinus = Math.random() < 0.5 ? -1 : 1;
      var randy = Math.random();
      var y = Math.floor(plusOrMinus*randy*75)+centery;
      
      arr.push({"x":x,"y":y});
    }
    	
    pointsGroup.selectAll("text")
                .data(arr)
                .enter()
                .append("text")
                .attr("x", function(d,i){
                  	return d.x;// This corresponds to arr[i].x
                  })
                .attr("y", function(d,i){
                 	return d.y;// This corresponds to arr[i].y
                  })
                .html("star")
                .attr("class", "point material-icons")
                .on("click", function(){console.log("click!");});

    这样,您可以使用例如.attr("x",function(d,i){ //d.x is equal to arr.x, i is item index in the selection}); 访问各个坐标

    那么,为了实现你的目标,你应该使用线性比例来改变每个星星的位置,而不是改变你的物品的比例。

    首先,为您的小提琴添加线性比例并将它们应用于缩放:

    var scalex = d3.scale.linear();
    var scaley = d3.scale.linear();
    
    var zoom = d3.behavior.zoom().x(scalex).y(scaley).scaleExtent([1, 5]).on('zoom', onZoom);

    最后在缩放事件中将比例应用于每个星星的xy

    function onZoom(){
      d3.selectAll("text")
        .attr("x",function(d,i){
          
          return scalex(d.x);
        })
        .attr("y",function(d,i){
         
          return scaley(d.y);
        });
    }

    此时,缩放将在没有滑块的情况下工作。要添加滑块,只需在 onSlide 事件期间手动更改缩放行为比例值,然后调用 onZoom。

    function onSlide(scale){
      var sc = $("#slider").slider("value");
      zoom.scale(sc);
      onZoom();
    }

    注意:我将这个配置用于滑块:

    var slider = $(function() {
    	    $( "#slider" ).slider({
    			value: 1,
    			min: 1,
    			max: 5,
    			step: 0.1,
    			slide: function(event, ui){
                  onSlide(5/ui.value);
    			}
    	    });
    	});

    请注意,此时 ui 的缩放是相对于 (0,0) 执行的,而不是您的“圆形”窗口中心。为了解决这个问题,我从编程示例中简化了以下函数,该函数计算有效的平移和缩放以提供给缩放行为。

    // To handle center zooming
    var width = 500;
    var height = 600;
    
    function zoomClick(sliderValue) {
      var center = [width / 2, height / 2],
        extent = zoom.scaleExtent(),
        translate = zoom.translate();
    
    
      var view = {
        x: zoom.translate()[0],
        y: zoom.translate()[1],
        k: zoom.scale()
      };
    
      var target_zoom = sliderValue;
    
      if (target_zoom < extent[0] || target_zoom > extent[1]) {
        return false;
      }
    
      var translate0 = [(center[0] - view.x) / view.k, (center[1] - view.y) / view.k];
    
      view.k = target_zoom;
      var l = [];
      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];
    
      // [view.x view.y] is valid translate
      // view.k is valid scale
      // Then, simply feed them to the zoom behavior
      zoom
        .scale(view.k)
        .translate([view.x, view.y]);
      // and call onZoom to update points position
      onZoom();
    
    }

    然后只需更改 onSlide 以在每次滑块移动时使用此新功能

    function onSlide(scale){
      var sc = $("#slider").slider("value");
      zoomClick(sc);
    }

    完整的sn-p

    function onZoom(){
      d3.selectAll("text")
        .attr("x",function(d,i){
          
          return scalex(d.x);
        })
        .attr("y",function(d,i){
         
          return scaley(d.y);
        });
    }
    
    
    function onSlide(scale){
      var sc = $("#slider").slider("value");
      zoomClick(sc);
    }
    
    var scalex = d3.scale.linear();
    var scaley = d3.scale.linear();
    
    var zoom = d3.behavior.zoom().x(scalex).y(scaley).scaleExtent([1, 5]).on('zoom', onZoom);
    
    var svg = d3.select("body").append("svg")
                                .attr("height", "500px")
                                .attr("width", "500px")
    					        .call(zoom)
    					        .on("mousedown.zoom", null)
    					        .on("touchstart.zoom", null)
        				        .on("touchmove.zoom", null)
        				        .on("touchend.zoom", null);
    
    var centerx = 250,
        centery = 250;
    
    var circleGroup = svg.append("g")
                          .attr("id", "circleGroup");
    
    var circle = circleGroup.append("circle")
                      .attr("cx", "50%")
                      .attr("cy", "50%")
                      .attr("r", 150)
                      .attr("class", "circle");
    
    
    
    var pointsParent = svg.append("g").attr("clip-path", "url(#clip)").attr("id", "pointsParent");
    
    var pointsGroup = pointsParent.append("g")
    					  .attr("id", "pointsGroup");
    
    var arr = [];
    
    for(i=0; i<7; i++){
      var plusOrMinus = Math.random() < 0.5 ? -1 : 1;
      var randx = Math.random();
      var x = Math.floor(plusOrMinus*randx*75)+centerx; 
      
      var plusOrMinus = Math.random() < 0.5 ? -1 : 1;
      var randy = Math.random();
      var y = Math.floor(plusOrMinus*randy*75)+centery;
      
      arr.push({"x":x,"y":y});
    }
    	
    pointsGroup.selectAll("text")
                .data(arr)
                .enter()
                .append("text")
                .attr("x", function(d,i){
                  	return d.x;// This corresponds to arr[i].x
                  })
                .attr("y", function(d,i){
                 	return d.y;// This corresponds to arr[i].y
                  })
                .html("star")
                .attr("class", "point material-icons")
                .on("click", function(){console.log("click!");});
    
    zoom(pointsGroup);
    
    var clip = svg.append("defs").append("svg:clipPath")
            .attr("id", "clip")
            .append("svg:circle")
            .attr("id", "clip-circ")
            .attr("cx", centerx)
            .attr("cy", centery)
            .attr("r", 149);
    
    var slider = $(function() {
    	    $( "#slider" ).slider({
    			value: 1,
    			min: 1,
    			max: 5,
    			step: 0.1,
    			slide: function(event, ui){
                  onSlide(5/ui.value);
    			}
    	    });
    	});
    
    
    
    // To handle center zooming
    var width = 500;
    var height = 600;
    
    function zoomClick(sliderValue) {
        var target_zoom = 1,
            center = [width / 2, height / 2],
            extent = zoom.scaleExtent(),
            translate = zoom.translate();
            
        
        var view = {x: zoom.translate()[0], y: zoom.translate()[1], k: zoom.scale()};
      
        target_zoom = sliderValue;
    
        if (target_zoom < extent[0] || target_zoom > extent[1]) { return false; }
    
        var translate0 = [];
        translate0 = [(center[0] - view.x) / view.k, (center[1] - view.y) / view.k];
      
        view.k = target_zoom;
        var l = [];
        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];
      
        zoom
           .scale(view.k)
           .translate([view.x, view.y]);
        onZoom();
      
    }
    body {
      font: 10px sans-serif;
    }
    
    text {
      font: 10px sans-serif;
    }
    
    .circle{
      stroke: black;
      stroke-width: 2px;
      fill: white;
    }
    
    .point{
      fill: goldenrod;
      cursor: pointer;
    }
    
    .blip{
      fill: black;
    }
    
    #slider{
      width: 200px;
      margin: auto;
    }
    <!DOCTYPE html>
    <html>
    <head>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons"
          rel="stylesheet">
    <link href="https://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css" rel="stylesheet" type="text/css" />
    <script src="https://code.jquery.com/jquery-1.11.3.js"></script>
    <script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
      
    
    <meta charset=utf-8 />
    <title>d3.JS slider zoom</title>
    </head>
      
    <body>
      <div id="slider"></div>
    </body>
    </html>

    【讨论】:

    • 你能分享一下你目前的尝试吗?
    • 我已经用它编辑了我的(很长,抱歉)帖子。 Cyril 的程序示例做了您正在寻找的滑块,我将尝试添加缺少的代码
    • 问题解决了,代码需要大量的清理和重构,但它应该可以帮助你理解我做了什么。如果您有任何问题,请告诉我
    • :) 非常接近,但有一个小问题:鼠标滚轮缩放以鼠标光标而不是图形中心为中心...但我想我可以自己解决它。
    • 不知道你想要这种行为,但这应该通过在程序开头添加zoom.center([width/2 height/2]) 来解决,也可能在 onClick 的最后添加。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-14
    • 2015-06-21
    • 2015-01-18
    • 2021-08-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多