Skip to content

Commit 3653b4e

Browse files
committed
Initial version of zoom with _zoomOutRestrict using D3 zoom features
1 parent 728de18 commit 3653b4e

File tree

2 files changed

+58
-43
lines changed

2 files changed

+58
-43
lines changed

src/coordinate-grid-mixin.js

+57-42
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ dc.coordinateGridMixin = function (_chart) {
5454
var _renderHorizontalGridLine = false;
5555
var _renderVerticalGridLine = false;
5656

57-
var _refocused = false, _resizing = false;
57+
var _resizing = false;
5858
var _unitCount;
5959

6060
var _zoomScale = [1, Infinity];
@@ -1243,36 +1243,76 @@ dc.coordinateGridMixin = function (_chart) {
12431243
.extent([[0, 0], [_chart.width(), _chart.height()]])
12441244
.duration(_chart.transitionDuration());
12451245

1246+
if (_zoomOutRestrict) {
1247+
_zoom.constrain(function (transform, extent, translateExtent) {
1248+
var newDomain = _zoomTransformToDomain(transform, _origX);
1249+
// If it is outside the original domain, cancel the current op by returning the current transform
1250+
if (newDomain[0] < _xOriginalDomain[0] || newDomain[1] > _xOriginalDomain[1]) {
1251+
transform = _domainToZoomTransform(_x.domain(), _xOriginalDomain, _origX);
1252+
}
1253+
1254+
return transform;
1255+
});
1256+
}
1257+
12461258
_chart.root().call(_zoom);
1259+
1260+
// Tell D3 zoom our current zoom/pan status
1261+
_updateD3zoomTransform();
12471262
};
12481263

12491264
_chart._disableMouseZoom = function () {
12501265
_chart.root().call(_nullZoom);
12511266
};
12521267

12531268
function zoomHandler () {
1254-
_refocused = true;
1255-
if (_zoomOutRestrict) {
1256-
var constraint = _xOriginalDomain;
1257-
if (_rangeChart) {
1258-
constraint = intersectExtents(constraint, _rangeChart.x().domain());
1259-
}
1260-
var constrained = constrainExtent(_chart.x().domain(), constraint);
1261-
if (constrained) {
1262-
_chart.x().domain(constrained);
1263-
}
1264-
}
1265-
12661269
var domain = _chart.x().domain();
12671270
var domFilter = dc.filters.RangedFilter(domain[0], domain[1]);
12681271

12691272
_chart.replaceFilter(domFilter);
12701273
_chart.rescale();
12711274
_chart.redraw();
1275+
}
1276+
1277+
// Our zooming is not standard d3 zoom as defined in their examples.
1278+
// Instead we want to focus the chart based on current zoom transform.
1279+
// The following code computes values of new domain the same way as transform.rescaleX.
1280+
// The difference is what we do after we get the newDomain
1281+
var _zoomTransformToDomain = function (transform, xScale) {
1282+
return xScale.range().map(function (xCoord) {
1283+
return _origX.invert(transform.invertX(xCoord));
1284+
});
1285+
};
1286+
1287+
// _zoomTransformToDomain(transform, xScale) should give back newDomain
1288+
var _domainToZoomTransform = function (newDomain, origDomain, xScale) {
1289+
var k = (origDomain[1] - origDomain[0]) / (newDomain[1] - newDomain[0]);
1290+
var xt = -1 * xScale(newDomain[0]);
1291+
1292+
return d3.zoomIdentity.scale(k).translate(xt, 0);
1293+
};
1294+
1295+
// If we changing zoom status (for example by calling focus), tell D3 zoom about it
1296+
var _updateD3zoomTransform = function () {
1297+
if (_zoom) {
1298+
_zoom.transform(_chart.root(), _domainToZoomTransform(_chart.x().domain(), _xOriginalDomain, _origX));
1299+
}
1300+
};
1301+
1302+
function onZoom () {
1303+
var event = d3.event;
1304+
// Avoids infinite recursion
1305+
// To ensure that when it is called because of programatic zoom there is no d3.event.sourceEvent
1306+
d3.event = null;
1307+
if (!event.sourceEvent) { return; }
1308+
1309+
var newDomain = _zoomTransformToDomain(event.transform, _origX);
1310+
1311+
_chart.focus(newDomain);
12721312

12731313
if (_rangeChart && !rangesEqual(_chart.filter(), _rangeChart.filter())) {
12741314
dc.events.trigger(function () {
1275-
_rangeChart.replaceFilter(domFilter);
1315+
_rangeChart.replaceFilter(newDomain);
12761316
_rangeChart.redraw();
12771317
});
12781318
}
@@ -1283,33 +1323,6 @@ dc.coordinateGridMixin = function (_chart) {
12831323
_chart.redrawGroup();
12841324
}, dc.constants.EVENT_DELAY);
12851325

1286-
_refocused = !rangesEqual(domain, _xOriginalDomain);
1287-
}
1288-
1289-
function onZoom () {
1290-
if (!d3.event.sourceEvent && d3.event.sourceEvent.type !== 'zoom') { return; }
1291-
1292-
_chart.x(d3.event.transform.rescaleX(_origX));
1293-
1294-
zoomHandler();
1295-
}
1296-
1297-
function intersectExtents (ext1, ext2) {
1298-
if (ext1[0] > ext2[1] || ext1[1] < ext2[0]) {
1299-
console.warn('could not intersect extents');
1300-
}
1301-
return [Math.max(ext1[0], ext2[0]), Math.min(ext1[1], ext2[1])];
1302-
}
1303-
1304-
function constrainExtent (extent, constraint) {
1305-
var size = extent[1] - extent[0];
1306-
if (extent[0] < constraint[0]) {
1307-
return [constraint[0], Math.min(constraint[1], dc.utils.add(constraint[0], size, 'millis'))];
1308-
} else if (extent[1] > constraint[1]) {
1309-
return [Math.max(constraint[0], dc.utils.subtract(constraint[1], size, 'millis')), constraint[1]];
1310-
} else {
1311-
return null;
1312-
}
13131326
}
13141327

13151328
/**
@@ -1337,11 +1350,13 @@ dc.coordinateGridMixin = function (_chart) {
13371350
_chart.x().domain(_xOriginalDomain);
13381351
}
13391352

1353+
_updateD3zoomTransform();
1354+
13401355
zoomHandler();
13411356
};
13421357

13431358
_chart.refocused = function () {
1344-
return _refocused;
1359+
return !rangesEqual(_chart.x().domain(), _xOriginalDomain);
13451360
};
13461361

13471362
_chart.focusChart = function (c) {

web/zoom/restrict-panning.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@
132132
group = dimension.group().reduceSum(function(d) { return d.value; });
133133

134134
rangeChart
135-
.height(40)
135+
.height(60)
136136
.dimension(dimension)
137137
.group(group)
138138
.x(d3.scaleTime().domain(domain))

0 commit comments

Comments
 (0)