diff --git a/.gitignore b/.gitignore index e6f33ca..3c2ef74 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,10 @@ build # coverage report /coverage + +# OS related +.DS_Store + +# IDE related +.idea + diff --git a/htdocs/index.html b/htdocs/index.html index 7f2c06a..a8fe49e 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -356,6 +356,9 @@ Enable zoom + + Change Zoom Type + Zoom on category axis diff --git a/htdocs/samples/zoom_type.html b/htdocs/samples/zoom_type.html new file mode 100644 index 0000000..042cf1c --- /dev/null +++ b/htdocs/samples/zoom_type.html @@ -0,0 +1,62 @@ + +
+ + + +Zoom Type Scroll with Default Zoom Behavior
+ + +Zoom Type Drag with Default Zoom Behavior
+ + + + + + + diff --git a/src/class.js b/src/class.js index 66171a7..9202e92 100644 --- a/src/class.js +++ b/src/class.js @@ -22,6 +22,7 @@ var CLASS = c3_chart_internal_fn.CLASS = { eventRectsMultiple: 'c3-event-rects-multiple', zoomRect: 'c3-zoom-rect', brush: 'c3-brush', + dragZoom: 'c3-drag-zoom', focused: 'c3-focused', defocused: 'c3-defocused', region: 'c3-region', diff --git a/src/config.js b/src/config.js index ae9aa9f..4331c26 100644 --- a/src/config.js +++ b/src/config.js @@ -10,6 +10,8 @@ c3_chart_internal_fn.getDefaultConfig = function () { padding_bottom: undefined, resize_auto: true, zoom_enabled: false, + zoom_type: 'scroll', + zoom_disableDefaultBehavior: false, zoom_extent: undefined, zoom_privileged: false, zoom_rescale: false, diff --git a/src/core.js b/src/core.js index 4dad60d..bfb84f8 100644 --- a/src/core.js +++ b/src/core.js @@ -268,6 +268,7 @@ c3_chart_internal_fn.initWithData = function (data) { // Define regions main = $$.main = $$.svg.append("g").attr("transform", $$.getTranslate('main')); + if ($$.initDragZoom) { $$.initDragZoom(); } if ($$.initSubchart) { $$.initSubchart(); } if ($$.initTooltip) { $$.initTooltip(); } if ($$.initLegend) { $$.initLegend(); } diff --git a/src/scss/main.scss b/src/scss/main.scss index fecfcfe..0b34feb 100644 --- a/src/scss/main.scss +++ b/src/scss/main.scss @@ -61,3 +61,7 @@ /*-- Arc --*/ @import 'arc'; + +/*-- Zoom --*/ + +@import 'zoom'; diff --git a/src/scss/zoom.scss b/src/scss/zoom.scss new file mode 100644 index 0000000..1e0c6fe --- /dev/null +++ b/src/scss/zoom.scss @@ -0,0 +1,13 @@ +.c3-drag-zoom.enabled{ + pointer-events: all!important; + visibility: visible; +} + +.c3-drag-zoom.disabled{ + pointer-events: none!important; + visibility: hidden; +} + +.c3-drag-zoom .extent { + fill-opacity: .1; +} diff --git a/src/zoom.js b/src/zoom.js index c11dd8b..cef0ad8 100644 --- a/src/zoom.js +++ b/src/zoom.js @@ -3,14 +3,26 @@ c3_chart_internal_fn.initZoom = function () { $$.zoom = d3.behavior.zoom() .on("zoomstart", function () { + if (config.zoom_type !== 'scroll') { + return; + } + startEvent = d3.event.sourceEvent; $$.zoom.altDomain = d3.event.sourceEvent.altKey ? $$.x.orgDomain() : null; config.zoom_onzoomstart.call($$.api, d3.event.sourceEvent); }) .on("zoom", function () { + if (config.zoom_type !== 'scroll') { + return; + } + $$.redrawForZoom.call($$); }) .on('zoomend', function () { + if (config.zoom_type !== 'scroll') { + return; + } + var event = d3.event.sourceEvent; // if click, do nothing. otherwise, click interaction will be canceled. if (event && startEvent.clientX === event.clientX && startEvent.clientY === event.clientY) { @@ -20,6 +32,7 @@ c3_chart_internal_fn.initZoom = function () { $$.updateZoom(); config.zoom_onzoomend.call($$.api, $$.x.orgDomain()); }); + $$.zoom.scale = function (scale) { return config.axis_rotated ? this.y(scale) : this.x(scale); }; @@ -34,6 +47,75 @@ c3_chart_internal_fn.initZoom = function () { return this; }; }; + +c3_chart_internal_fn.initDragZoom = function () { + if (this.config.zoom_type === 'drag' && this.config.zoom_enabled) { + var $$ = this, d3 = $$.d3, config = $$.config, + context = $$.context = $$.svg, + brushXPos, brushYPos; + + $$.dragZoomBrush = d3.svg.brush() + .x($$.x) + .y($$.y) + .on("brushstart", function () { + config.zoom_onzoomstart.call($$.api, $$.x.orgDomain()); + }) + .on("brush", function () { + var extent = $$.dragZoomBrush.extent(), + ar1 = [extent[0][0], $$.y.domain()[0]], + ar2 = [extent[1][0], $$.y.domain()[1]]; + + $$.dragZoomBrush.extent([ar1, ar2]); + $$.svg.select("." + CLASS.dragZoom).call($$.dragZoomBrush); + + + config.zoom_onzoom.call($$.api, $$.x.orgDomain()); + }) + .on("brushend", function () { + var extent = $$.dragZoomBrush.extent(); + + if (!config.zoom_disableDefaultBehavior) { + $$.api.zoom([extent[0][0], extent[1][0]]); + } + else { + var ar1 = [$$.x.domain()[0], $$.y.domain()[0]], + ar2 = [$$.x.domain()[1], $$.y.domain()[1]]; + $$.dragZoomBrush.extent([ar1, ar2]); + $$.api.zoom([$$.x.domain()[0], $$.x.domain()[1]]); + } + + d3.selectAll("." + CLASS.dragZoom) + .attr("class", CLASS.dragZoom + " disabled"); + + $$.dragZoomBrush.clear(); + $$.svg.select("." + CLASS.dragZoom).call($$.dragZoomBrush); + + config.zoom_onzoomend.call($$.api, [extent[0][0], extent[1][0]]); + }); + + brushXPos = $$.margin.left + 20.5; + brushYPos = $$.margin.top + 0.5; + context.append("g") + .attr("clip-path", $$.clipPath) + .attr("class", CLASS.dragZoom + " disabled") + .attr("transform", "translate(" + brushXPos + "," + brushYPos + ")") + .call($$.dragZoomBrush); + + $$.svg.on("mousedown", function () { + d3.selectAll("." + CLASS.dragZoom) + .attr("class", CLASS.dragZoom + " enabled"); + + var brush_elm = $$.svg.select("." + CLASS.dragZoom).node(); + var new_click_event = new Event('mousedown'); + new_click_event.pageX = d3.event.pageX; + new_click_event.clientX = d3.event.clientX; + new_click_event.pageY = d3.event.pageY; + new_click_event.clientY = d3.event.clientY; + brush_elm.dispatchEvent(new_click_event); + }); + } +}; + c3_chart_internal_fn.getZoomDomain = function () { var $$ = this, config = $$.config, d3 = $$.d3, min = d3.min([$$.orgXDomain[0], config.zoom_x_min]), @@ -41,7 +123,7 @@ c3_chart_internal_fn.getZoomDomain = function () { return [min, max]; }; c3_chart_internal_fn.updateZoom = function () { - var $$ = this, z = $$.config.zoom_enabled ? $$.zoom : function () {}; + var $$ = this, z = $$.config.zoom_enabled && $$.config.zoom_type === 'scroll' ? $$.zoom : function () {}; $$.main.select('.' + CLASS.zoomRect).call(z).on("dblclick.zoom", null); $$.main.selectAll('.' + CLASS.eventRect).call(z).on("dblclick.zoom", null); }; @@ -53,23 +135,26 @@ c3_chart_internal_fn.redrawForZoom = function () { if ($$.filterTargetsToShow($$.data.targets).length === 0) { return; } - if (d3.event.sourceEvent.type === 'mousemove' && zoom.altDomain) { - x.domain(zoom.altDomain); - zoom.scale(x).updateScaleExtent(); - return; - } - if ($$.isCategorized() && x.orgDomain()[0] === $$.orgXDomain[0]) { - x.domain([$$.orgXDomain[0] - 1e-10, x.orgDomain()[1]]); - } - $$.redraw({ - withTransition: false, - withY: config.zoom_rescale, - withSubchart: false, - withEventRect: false, - withDimension: false - }); - if (d3.event.sourceEvent.type === 'mousemove') { - $$.cancelClick = true; + + if (config.zoom_type === 'scroll' && !config.zoom_disableDefaultBehavior) { + if (d3.event.sourceEvent.type === 'mousemove' && zoom.altDomain) { + x.domain(zoom.altDomain); + zoom.scale(x).updateScaleExtent(); + return; + } + if ($$.isCategorized() && x.orgDomain()[0] === $$.orgXDomain[0]) { + x.domain([$$.orgXDomain[0] - 1e-10, x.orgDomain()[1]]); + } + $$.redraw({ + withTransition: false, + withY: config.zoom_rescale, + withSubchart: false, + withEventRect: false, + withDimension: false + }); + if (d3.event.sourceEvent.type === 'mousemove') { + $$.cancelClick = true; + } } config.zoom_onzoom.call($$.api, x.orgDomain()); };