diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js index af780f8dd50..2e281c145f2 100644 --- a/src/components/drawing/index.js +++ b/src/components/drawing/index.js @@ -692,6 +692,36 @@ drawing.setPointGroupScale = function(selection, x, y) { return scale; }; +var TEXT_POINT_LAST_TRANSLATION_RE = /translate\([^)]*\)\s*$/; + +drawing.setTextPointsScale = function(selection, xScale, yScale) { + selection.each(function() { + var transforms; + var el = d3.select(this); + var text = el.select('text'); + var x = parseFloat(text.attr('x') || 0); + var y = parseFloat(text.attr('y') || 0); + + var existingTransform = (el.attr('transform') || '').match(TEXT_POINT_LAST_TRANSLATION_RE); + + if(xScale === 1 && yScale === 1) { + transforms = []; + } else { + transforms = [ + 'translate(' + x + ',' + y + ')', + 'scale(' + xScale + ',' + yScale + ')', + 'translate(' + (-x) + ',' + (-y) + ')', + ]; + } + + if(existingTransform) { + transforms.push(existingTransform); + } + + el.attr('transform', transforms.join(' ')); + }); +}; + drawing.measureText = function(tester, text, font) { var dummyText = tester.append('text') .text(text) diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index 3c174224e60..df0d64511c9 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -30,7 +30,6 @@ var constants = require('./constants'); var MINDRAG = constants.MINDRAG; var MINZOOM = constants.MINZOOM; - // flag for showing "doubleclick to zoom out" only at the beginning var SHOWZOOMOUTTIP = true; @@ -747,6 +746,10 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { // as a whole: .select('.scatterlayer').selectAll('.points').selectAll('.point') .call(Drawing.setPointGroupScale, xScaleFactor2, yScaleFactor2); + + subplot.plot.select('.scatterlayer') + .selectAll('.points').selectAll('.textpoint') + .call(Drawing.setTextPointsScale, xScaleFactor2, yScaleFactor2); } } diff --git a/src/plots/cartesian/transition_axes.js b/src/plots/cartesian/transition_axes.js index b41c50b8cc3..44344a35132 100644 --- a/src/plots/cartesian/transition_axes.js +++ b/src/plots/cartesian/transition_axes.js @@ -148,9 +148,11 @@ module.exports = function transitionAxes(gd, newLayout, transitionOpts, makeOnCo // This is specifically directed at scatter traces, applying an inverse // scale to individual points to counteract the scale of the trace // as a whole: - .selectAll('.points').selectAll('.point') + .select('.scatterlayer').selectAll('.points').selectAll('.point') .call(Drawing.setPointGroupScale, 1, 1); + subplot.plot.select('.scatterlayer').selectAll('.points').selectAll('.textpoint') + .call(Drawing.setTextPointsScale, 1, 1); } function updateSubplot(subplot, progress) { @@ -229,6 +231,8 @@ module.exports = function transitionAxes(gd, newLayout, transitionOpts, makeOnCo .selectAll('.points').selectAll('.point') .call(Drawing.setPointGroupScale, 1 / xScaleFactor, 1 / yScaleFactor); + subplot.plot.selectAll('.points').selectAll('.textpoint') + .call(Drawing.setTextPointsScale, 1 / xScaleFactor, 1 / yScaleFactor); } var onComplete; diff --git a/src/traces/scatter/plot.js b/src/traces/scatter/plot.js index 57930c6e613..d01b2ddaf16 100644 --- a/src/traces/scatter/plot.js +++ b/src/traces/scatter/plot.js @@ -452,7 +452,7 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition // each text needs to go in its own 'g' in case // it gets converted to mathjax - join.enter().append('g').append('text'); + join.enter().append('g').classed('textpoint', true).append('text'); join.each(function(d) { var sel = transition(d3.select(this).select('text')); @@ -460,7 +460,6 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition }); join.selectAll('text') - .classed('textpoint', true) .call(Drawing.textPointStyle, trace) .each(function(d) { diff --git a/test/jasmine/tests/drawing_test.js b/test/jasmine/tests/drawing_test.js index 3c20e719a1a..9b6f81b4a07 100644 --- a/test/jasmine/tests/drawing_test.js +++ b/test/jasmine/tests/drawing_test.js @@ -308,4 +308,38 @@ describe('Drawing', function() { expect(el.getAttribute('transform')).toBe('translate(1,2)'); }); }); + + describe('setTextPointsScale', function() { + var svg, g, text; + + beforeEach(function() { + svg = d3.select(document.createElement('svg')); + g = svg.append('g'); + text = g.append('text'); + }); + + it('sets the transform on an empty element', function() { + Drawing.setTextPointsScale(g, 2, 3); + expect(g.attr('transform')).toEqual('translate(0,0) scale(2,3) translate(0,0)'); + }); + + it('unsets the transform', function() { + Drawing.setTextPointsScale(g, 1, 1); + expect(g.attr('transform')).toEqual(''); + }); + + it('preserves a leading translate', function() { + Drawing.setTextPointsScale(g, 1, 1); + g.attr('transform', 'translate(1, 2)'); + expect(g.attr('transform')).toEqual('translate(1, 2)'); + }); + + it('preserves transforms', function() { + text.attr('x', 8); + text.attr('y', 9); + g.attr('transform', 'translate(1, 2)'); + Drawing.setTextPointsScale(g, 4, 5); + expect(g.attr('transform')).toEqual('translate(8,9) scale(4,5) translate(-8,-9) translate(1, 2)'); + }); + }); });