Limiting domain when zooming or panning in D3.js

こ雲淡風輕ζ 提交于 2019-11-29 14:36:29

问题


I have implemented a simple D3.js line chart that can be zoomed and panned. It is based on Stephen Bannasch's excellent example here.

The domain of my data is [0, n] in the x dimension.

How can I limit zooming and panning to this domain using the built-in zoom behavior (i.e. using mousewheel events)?

I want to prevent users from panning past 0 on the lower end or n on the upper end, for example they should never be able to see negative values on the x-axis, and want to limit zooming to the same window.

The examples that I found based on Jason Davies work using extent( [...],[...],[...] ) seem to no longer work in version 2.9.1. Unfortunately, the zoom behavior is currently one of the few features not documented in the otherwise outstanding API documentation.

Any pointers are welcome.

PS. I have posted the same question on the D3.js mailing list but did not get a response: https://groups.google.com/d/topic/d3-js/w6LrHLF2CYc/discussion. Apologies for the cross-posting.


回答1:


You just need to limit the domain on redraw. The following code will prevent the graph from being zoomed out past it's initial domains (as used in http://bl.ocks.org/1182434).

SimpleGraph.prototype.redraw = function() {
  var self = this;
  return function() {

    self.x.domain([Math.max(self.x.domain()[0], self.options.xmin), Math.min(self.x.domain()[1], self.options.xmax)]);

    self.y.domain([Math.max(self.y.domain()[0], self.options.ymin), Math.min(self.y.domain()[1], self.options.ymax)]);

    ....



回答2:


Sadly, the solution posted by Bill did only half the trick: While it does indeed inhibit the panning, it causes the graph to distort if zoom is applied. It is then usually impossible to return to a properly proportioned and positioned graph.

In the following version the proportions of the axes are maintained, even if scrolling to the borders.

As soon as the scaling hits 100%, the scales' domains are reset to their original position. This guarantees a correct positioning, even if the intermediate steps return illegal parameters for the axes.

While not perfect, I hope this script can help somebody until d3 (re)implements this feature.

# x and y are the scales
# xAxis and yAxis are the axes
# graph is the graph you want attach the zoom to

x0 = x.copy()
y0 = y.copy()

successfulTranslate = [0, 0]

zoomer = d3.behavior.zoom()
  .scaleExtent([1,2])

onZoom = ->
  ev = d3.event # contains: .translate[x,y], .scale
  if ev.scale == 1.0
    x.domain x0.domain()
    y.domain y0.domain()
    successfulTranslate = [0, 0]
  else
    xTrans = x0.range().map( (xVal) -> (xVal-ev.translate[0]) / ev.scale ).map(x0.invert)
    yTrans = y0.range().map( (yVal) -> (yVal-ev.translate[1]) / ev.scale ).map(y0.invert)
    xTransOk = xTrans[0] >= x0.domain()[0] and xTrans[1] <= x0.domain()[1]
    yTransOk = yTrans[0] >= y0.domain()[0] and yTrans[1] <= y0.domain()[1]
    if xTransOk
      x.domain xTrans
      successfulTranslate[0] = ev.translate[0]
    if yTransOk
      y.domain yTrans
      successfulTranslate[1] = ev.translate[1]
  zoomer.translate successfulTranslate

graph.select('g.x.axis').call(xAxis)
graph.select('g.y.axis').call(yAxis)
drawBars()

zoomer.on('zoom', onZoom)

# ...
graph.call(zoomer)


来源:https://stackoverflow.com/questions/10422738/limiting-domain-when-zooming-or-panning-in-d3-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!