@@ -54,7 +54,7 @@ dc.coordinateGridMixin = function (_chart) {
54
54
var _renderHorizontalGridLine = false ;
55
55
var _renderVerticalGridLine = false ;
56
56
57
- var _refocused = false , _resizing = false ;
57
+ var _resizing = false ;
58
58
var _unitCount ;
59
59
60
60
var _zoomScale = [ 1 , Infinity ] ;
@@ -1243,36 +1243,76 @@ dc.coordinateGridMixin = function (_chart) {
1243
1243
. extent ( [ [ 0 , 0 ] , [ _chart . width ( ) , _chart . height ( ) ] ] )
1244
1244
. duration ( _chart . transitionDuration ( ) ) ;
1245
1245
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
+
1246
1258
_chart . root ( ) . call ( _zoom ) ;
1259
+
1260
+ // Tell D3 zoom our current zoom/pan status
1261
+ _updateD3zoomTransform ( ) ;
1247
1262
} ;
1248
1263
1249
1264
_chart . _disableMouseZoom = function ( ) {
1250
1265
_chart . root ( ) . call ( _nullZoom ) ;
1251
1266
} ;
1252
1267
1253
1268
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
-
1266
1269
var domain = _chart . x ( ) . domain ( ) ;
1267
1270
var domFilter = dc . filters . RangedFilter ( domain [ 0 ] , domain [ 1 ] ) ;
1268
1271
1269
1272
_chart . replaceFilter ( domFilter ) ;
1270
1273
_chart . rescale ( ) ;
1271
1274
_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 ) ;
1272
1312
1273
1313
if ( _rangeChart && ! rangesEqual ( _chart . filter ( ) , _rangeChart . filter ( ) ) ) {
1274
1314
dc . events . trigger ( function ( ) {
1275
- _rangeChart . replaceFilter ( domFilter ) ;
1315
+ _rangeChart . replaceFilter ( newDomain ) ;
1276
1316
_rangeChart . redraw ( ) ;
1277
1317
} ) ;
1278
1318
}
@@ -1283,33 +1323,6 @@ dc.coordinateGridMixin = function (_chart) {
1283
1323
_chart . redrawGroup ( ) ;
1284
1324
} , dc . constants . EVENT_DELAY ) ;
1285
1325
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
- }
1313
1326
}
1314
1327
1315
1328
/**
@@ -1337,11 +1350,13 @@ dc.coordinateGridMixin = function (_chart) {
1337
1350
_chart . x ( ) . domain ( _xOriginalDomain ) ;
1338
1351
}
1339
1352
1353
+ _updateD3zoomTransform ( ) ;
1354
+
1340
1355
zoomHandler ( ) ;
1341
1356
} ;
1342
1357
1343
1358
_chart . refocused = function ( ) {
1344
- return _refocused ;
1359
+ return ! rangesEqual ( _chart . x ( ) . domain ( ) , _xOriginalDomain ) ;
1345
1360
} ;
1346
1361
1347
1362
_chart . focusChart = function ( c ) {
0 commit comments