javascript d3.js将缩放行为更改为语义缩放

tnkciper  于 2023-03-28  发布在  Java
关注(0)|答案(2)|浏览(104)

我正在用d3.js做一些关于缩放的测试。
目前,我已经在测试中成功地实现了几何缩放,但它有一个缺点,比如缩放后的g下的元素正在缩放。
我明白了,这可以通过使用semantic zooming来解决。
问题是我在测试中需要scale,因为我正在将它与jQuery.UI滑块value同步。
另一方面,我想调整text元素的大小,以便在缩放操作后保持其大小。
我有一个我目前尝试here的例子。
我在修改代码以适应这个目的时遇到了麻烦。有人能分享一些见解/想法吗?

jckbn6z7

jckbn6z71#

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

代码片段:

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(已添加必要的注解)
希望这有帮助!

8cdiaqws

8cdiaqws2#

要做到这一点,首先需要对代码进行一点重构,使用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);
}

完整片段

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>

相关问题