@@ -33,6 +33,7 @@ var MINUS_SIGN = constants.MINUS_SIGN;
3333var BADNUM = constants . BADNUM ;
3434
3535var MID_SHIFT = require ( '../../constants/alignment' ) . MID_SHIFT ;
36+ var CAP_SHIFT = require ( '../../constants/alignment' ) . CAP_SHIFT ;
3637var LINE_SPACING = require ( '../../constants/alignment' ) . LINE_SPACING ;
3738var OPPOSITE_SIDE = require ( '../../constants/alignment' ) . OPPOSITE_SIDE ;
3839
@@ -1859,6 +1860,11 @@ axes.drawOne = function(gd, ax, opts) {
18591860 transFn : transFn
18601861 } ) ;
18611862 } ) ;
1863+ } else if ( ax . title . hasOwnProperty ( 'standoff' ) ) {
1864+ seq . push ( function ( ) {
1865+ var sgn = { l : - 1 , t : - 1 , r : 1 , b : 1 } [ ax . side . charAt ( 0 ) ] ;
1866+ ax . _depth = sgn * ( getLabelLevelBbox ( ) [ ax . side ] - mainLinePosition ) ;
1867+ } ) ;
18621868 }
18631869
18641870 var hasRangeSlider = Registry . getComponentMethod ( 'rangeslider' , 'isVisible' ) ( ax ) ;
@@ -1936,10 +1942,7 @@ axes.drawOne = function(gd, ax, opts) {
19361942 ax . _anchorAxis . domain [ domainIndices [ 0 ] ] ;
19371943
19381944 if ( ax . title . text !== fullLayout . _dfltTitle [ axLetter ] ) {
1939- var extraLines = ( ax . title . text . match ( svgTextUtils . BR_TAG_ALL ) || [ ] ) . length ;
1940- push [ s ] += extraLines ?
1941- ax . title . font . size * ( extraLines + 1 ) * LINE_SPACING :
1942- ax . title . font . size ;
1945+ push [ s ] += approxTitleDepth ( ax ) + ( ax . title . standoff || 0 ) ;
19431946 }
19441947
19451948 if ( ax . mirror && ax . anchor !== 'free' ) {
@@ -2699,42 +2702,84 @@ axes.getPxPosition = function(gd, ax) {
26992702 }
27002703} ;
27012704
2705+ /**
2706+ * Approximate axis title depth (w/o computing its bounding box)
2707+ *
2708+ * @param {object } ax (full) axis object
2709+ * - {string} title.text
2710+ * - {number} title.font.size
2711+ * - {number} title.standoff
2712+ * @return {number } (in px)
2713+ */
2714+ function approxTitleDepth ( ax ) {
2715+ var fontSize = ax . title . font . size ;
2716+ var extraLines = ( ax . title . text . match ( svgTextUtils . BR_TAG_ALL ) || [ ] ) . length ;
2717+ if ( ax . title . hasOwnProperty ( 'standoff' ) ) {
2718+ return extraLines ?
2719+ fontSize * ( CAP_SHIFT + ( extraLines * LINE_SPACING ) ) :
2720+ fontSize * CAP_SHIFT ;
2721+ } else {
2722+ return extraLines ?
2723+ fontSize * ( extraLines + 1 ) * LINE_SPACING :
2724+ fontSize ;
2725+ }
2726+ }
2727+
2728+ /**
2729+ * Draw axis title, compute default standoff if necessary
2730+ *
2731+ * @param {DOM element } gd
2732+ * @param {object } ax (full) axis object
2733+ * - {string} _id
2734+ * - {string} _name
2735+ * - {string} side
2736+ * - {number} title.font.size
2737+ * - {object} _selections
2738+ *
2739+ * - {number} _depth
2740+ * - {number} title.standoff
2741+ * OR
2742+ * - {number} linewidth
2743+ * - {boolean} showticklabels
2744+ */
27022745function drawTitle ( gd , ax ) {
27032746 var fullLayout = gd . _fullLayout ;
27042747 var axId = ax . _id ;
27052748 var axLetter = axId . charAt ( 0 ) ;
27062749 var fontSize = ax . title . font . size ;
27072750
27082751 var titleStandoff ;
2709- if ( ax . type === 'multicategory' ) {
2710- titleStandoff = ax . _depth ;
2752+
2753+ if ( ax . title . hasOwnProperty ( 'standoff' ) ) {
2754+ titleStandoff = ax . _depth + ax . title . standoff + approxTitleDepth ( ax ) ;
27112755 } else {
2712- var offsetBase = 1.5 ;
2713- titleStandoff = 10 + fontSize * offsetBase + ( ax . linewidth ? ax . linewidth - 1 : 0 ) ;
2756+ if ( ax . type === 'multicategory' ) {
2757+ titleStandoff = ax . _depth ;
2758+ } else {
2759+ var offsetBase = 1.5 ;
2760+ titleStandoff = 10 + fontSize * offsetBase + ( ax . linewidth ? ax . linewidth - 1 : 0 ) ;
2761+ }
2762+
2763+ if ( axLetter === 'x' ) {
2764+ titleStandoff += ax . side === 'top' ?
2765+ fontSize * ( ax . showticklabels ? 1 : 0 ) :
2766+ fontSize * ( ax . showticklabels ? 1.5 : 0.5 ) ;
2767+ } else {
2768+ titleStandoff += ax . side === 'right' ?
2769+ fontSize * ( ax . showticklabels ? 1 : 0.5 ) :
2770+ fontSize * ( ax . showticklabels ? 0.5 : 0 ) ;
2771+ }
27142772 }
27152773
27162774 var pos = axes . getPxPosition ( gd , ax ) ;
27172775 var transform , x , y ;
27182776
27192777 if ( axLetter === 'x' ) {
27202778 x = ax . _offset + ax . _length / 2 ;
2721-
2722- if ( ax . side === 'top' ) {
2723- y = - titleStandoff - fontSize * ( ax . showticklabels ? 1 : 0 ) ;
2724- } else {
2725- y = titleStandoff + fontSize * ( ax . showticklabels ? 1.5 : 0.5 ) ;
2726- }
2727- y += pos ;
2779+ y = ( ax . side === 'top' ) ? pos - titleStandoff : pos + titleStandoff ;
27282780 } else {
27292781 y = ax . _offset + ax . _length / 2 ;
2730-
2731- if ( ax . side === 'right' ) {
2732- x = titleStandoff + fontSize * ( ax . showticklabels ? 1 : 0.5 ) ;
2733- } else {
2734- x = - titleStandoff - fontSize * ( ax . showticklabels ? 0.5 : 0 ) ;
2735- }
2736- x += pos ;
2737-
2782+ x = ( ax . side === 'right' ) ? pos + titleStandoff : pos - titleStandoff ;
27382783 transform = { rotate : '-90' , offset : 0 } ;
27392784 }
27402785
@@ -2753,6 +2798,10 @@ function drawTitle(gd, ax) {
27532798 avoid . offsetLeft = translation . x ;
27542799 avoid . offsetTop = translation . y ;
27552800 }
2801+
2802+ if ( ax . title . hasOwnProperty ( 'standoff' ) ) {
2803+ avoid . pad = 0 ;
2804+ }
27562805 }
27572806
27582807 return Titles . draw ( gd , axId + 'title' , {
0 commit comments