首页 > 解决方案 > 如果要缩放,要计算缩放和平移的转换值

问题描述

我在看这个例子,它展示了如何使用缩放功能来放大指定的域范围

https://bl.ocks.org/mbostock/431a331294d2b5ddd33f947cf4c81319

我对这部分感到困惑:

var d0 = new Date(2003, 0, 1),
    d1 = new Date(2004, 0, 1);

// Gratuitous intro zoom!
svg.call(zoom).transition()
      .duration(1500)
      .call(zoom.transform, d3.zoomIdentity
          .scale(width / (x(d1) - x(d0))) // I think this is to caulcuate k which is the zoom factor
          .translate(-x(d0), 0)); // but what is this?

我无法理解所做的计算。如果我的假设是错误的,请纠正我

d3.zoomIdentity这是一种在应用时不执行任何操作的转换。

.scale(width / (x(d1) - x(d0)))这是通过计算两个数据点之间的比率和width像素差之间的比率来计算要应用的比例d0d1

.translate(-x(d0), 0))我不明白这部分。为什么被x(d0)否定? 的x坐标d(0)与需要应用多少翻译有什么关系?

标签: d3.js

解决方案


平移值正在对齐图形,因此x(d0)是绘图区域中可见的最左边的 x 值。这确保了绘图区域的可见部分从d0穿过d1(可见子域)延伸。如果我们的 x 尺度的整个域的最小值为 0,则将x(0)向左移动(负移)x(d0)像素。

我将使用一个片段来演示:

var svg = d3.select("svg"),
    margin = {top: 10, right: 50, bottom: 70, left: 200},
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom;

// Scale for Data:
var x = d3.scaleLinear()
  .range([0, width])
  .domain([0,20]);
  
// Scale for Zoom:
var xZoom = d3.scaleLinear()
  .range([0,width])
  .domain([0,width]);
  
var xAxis = d3.axisBottom(x).ticks(5);
var xZoomAxis = d3.axisBottom(xZoom);

var zoom = d3.zoom()
    .scaleExtent([1, 32])
    .translateExtent([[0, 0], [width, height]])
    .extent([[0, 0], [width, height]])
    .on("zoom", zoomed);
    
var g = svg.append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// plot area
g.append("rect")
  .attr("width",width)
  .attr("height",height)
  .attr("fill","url(#stripes)");
  
g.append("text")
  .attr("x",width/2)
  .attr("y",height/2)
  .style("text-anchor","middle")
  .text("plot area");
  
g.append("line")
  .attr("y1",0)
  .attr("y2",height)
  .attr("stroke-width",1)
  .attr("stroke","black");

// zoomed plot area:
var rect = g.append("rect")
  .attr("width",width)
  .attr("height",height)
  .attr("fill","lightgrey")
  .attr("opacity",0.4);
  
// Axis for plot:
g.append("g")
  .attr("class", "axis axis--x")
  .attr("transform", "translate(0," + height + ")")
  .call(xAxis);
  
// Axis for zoom:
g.append("g")
  .attr("class", "axis axis-zoom-x")
  .attr("transform", "translate(0,"+(height+30)+")")
  .call(xZoomAxis);  
  
var text = g.append("text")
  .attr("y", height+60)
  .attr("text-anchor","middle")
  .text("zoom units")
  .attr("x",width/2);
  
// Gratuitous intro zoom:   
var d1 = 18;
var d0 = 8;

svg.call(zoom).transition()
 .duration(2000)
 .call(zoom.transform, d3.zoomIdentity
 .scale(width / (x(d1) - x(d0)))
 .translate(-x(d0), 0));


function zoomed() {
  var t = d3.event.transform, xt = t.rescaleX(x);
  xZoom.range([xt(0),xt(20)]);
  g.select(".axis--x").call(xAxis.scale(xt));
  g.select(".axis-zoom-x").call(xZoomAxis.scale(xZoom));
  rect.attr("x", xt(0));
  rect.attr("width", xt(20) - xt(0));
  text.attr("x", xt(10));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="400" height="180">
<defs>
  <pattern id="stripes" patternUnits="userSpaceOnUse" width="8" height="8" patternTransform="rotate(45 0 0)">
  <rect width="3" height="8" fill="orange"></rect>
</pattern>
</defs>

</svg>

片段解释:

  • 地块区域:橙色条纹
  • 数据的全尺寸范围:灰色框。
  • g对于拥有一切的父级,绘图区域的左侧是 x=0(像素) 。

当我们放大数据的范围时,超出了绘图区域。我们想显示我们数据的特定子域。我们通过比例实现了其中的一部分(正如您正确推断的那样),但另一部分是通过平移:我们将小于 x 子域的最小值的值推到左侧。通过将整个图形向左推等于x(d0)x(d0)显示为绘图区域的最左侧坐标。


推荐阅读