Skip to content

Commit 26b91b3

Browse files
committed
annotation width/height/(v)align
1 parent b19a87f commit 26b91b3

File tree

4 files changed

+126
-31
lines changed

4 files changed

+126
-31
lines changed

src/components/annotations/annotation_defaults.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ module.exports = function handleAnnotationDefaults(annIn, annOut, fullLayout, op
3030
if(!(visible || clickToShow)) return annOut;
3131

3232
coerce('opacity');
33-
coerce('align');
3433
coerce('bgcolor');
3534

3635
var borderColor = coerce('bordercolor'),
@@ -45,6 +44,11 @@ module.exports = function handleAnnotationDefaults(annIn, annOut, fullLayout, op
4544
coerce('textangle');
4645
Lib.coerceFont(coerce, 'font', fullLayout.font);
4746

47+
coerce('width');
48+
coerce('height');
49+
coerce('align');
50+
coerce('valign');
51+
4852
// positioning
4953
var axLetters = ['x', 'y'],
5054
arrowPosDflt = [-10, -30],

src/components/annotations/attributes.js

+36-4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,27 @@ module.exports = {
4949
font: extendFlat({}, fontAttrs, {
5050
description: 'Sets the annotation text font.'
5151
}),
52+
width: {
53+
valType: 'number',
54+
min: 0,
55+
dflt: 0,
56+
role: 'style',
57+
description: [
58+
'Sets an explicit width for the text box. 0 (default) lets the',
59+
'text set the box width. Wider text will be clipped.',
60+
'There is no automatic wrapping; use <br> to start a new line.'
61+
].join(' ')
62+
},
63+
height: {
64+
valType: 'number',
65+
min: 0,
66+
dflt: 0,
67+
role: 'style',
68+
description: [
69+
'Sets an explicit height for the text box. 0 (default) lets the',
70+
'text set the box height. Taller text will be clipped.'
71+
].join(' ')
72+
},
5273
opacity: {
5374
valType: 'number',
5475
min: 0,
@@ -63,10 +84,21 @@ module.exports = {
6384
dflt: 'center',
6485
role: 'style',
6586
description: [
66-
'Sets the vertical alignment of the `text` with',
67-
'respect to the set `x` and `y` position.',
68-
'Has only an effect if `text` spans more two or more lines',
69-
'(i.e. `text` contains one or more <br> HTML tags).'
87+
'Sets the horizontal alignment of the `text` within the box.',
88+
'Has an effect only if `text` spans more two or more lines',
89+
'(i.e. `text` contains one or more <br> HTML tags) or if an',
90+
'explicit width is set to override the text width.'
91+
].join(' ')
92+
},
93+
valign: {
94+
valType: 'enumerated',
95+
values: ['top', 'middle', 'bottom'],
96+
dflt: 'middle',
97+
role: 'style',
98+
description: [
99+
'Sets the vertical alignment of the `text` within the box.',
100+
'Has an effect only if an explicit height is set to override',
101+
'the text height.'
70102
].join(' ')
71103
},
72104
bgcolor: {

src/components/annotations/draw.js

+52-19
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,16 @@ function drawOne(gd, index) {
118118
.call(Color.stroke, options.bordercolor)
119119
.call(Color.fill, options.bgcolor);
120120

121+
var annClipID = 'clip' + fullLayout._uid + '_ann' + index;
122+
var annTextClip = fullLayout._defs.select('.clips')
123+
.selectAll('#' + annClipID)
124+
.data([0]);
125+
126+
annTextClip.enter().append('clipPath')
127+
.classed('annclip', true)
128+
.attr('id', annClipID)
129+
.append('rect');
130+
121131
var font = options.font;
122132

123133
var annText = annTextGroupInner.append('text')
@@ -144,19 +154,21 @@ function drawOne(gd, index) {
144154
// at the end, even if their position changes
145155
annText.selectAll('tspan.line').attr({y: 0, x: 0});
146156

147-
var mathjaxGroup = annTextGroupInner.select('.annotation-math-group'),
148-
hasMathjax = !mathjaxGroup.empty(),
149-
anntextBB = Drawing.bBox(
150-
(hasMathjax ? mathjaxGroup : annText).node()),
151-
annwidth = anntextBB.width,
152-
annheight = anntextBB.height,
153-
outerwidth = Math.round(annwidth + 2 * borderfull),
154-
outerheight = Math.round(annheight + 2 * borderfull);
157+
var mathjaxGroup = annTextGroupInner.select('.annotation-math-group');
158+
var hasMathjax = !mathjaxGroup.empty();
159+
var anntextBB = Drawing.bBox(
160+
(hasMathjax ? mathjaxGroup : annText).node());
161+
var textWidth = anntextBB.width;
162+
var textHeight = anntextBB.height;
163+
var annWidth = options.width || textWidth;
164+
var annHeight = options.height || textHeight;
165+
var outerWidth = Math.round(annWidth + 2 * borderfull);
166+
var outerHeight = Math.round(annHeight + 2 * borderfull);
155167

156168

157169
// save size in the annotation object for use by autoscale
158-
options._w = annwidth;
159-
options._h = annheight;
170+
options._w = annWidth;
171+
options._h = annHeight;
160172

161173
function shiftFraction(v, anchor) {
162174
if(anchor === 'auto') {
@@ -181,8 +193,8 @@ function drawOne(gd, index) {
181193
ax = Axes.getFromId(gd, axRef),
182194
dimAngle = (textangle + (axLetter === 'x' ? 0 : -90)) * Math.PI / 180,
183195
// note that these two can be either positive or negative
184-
annSizeFromWidth = outerwidth * Math.cos(dimAngle),
185-
annSizeFromHeight = outerheight * Math.sin(dimAngle),
196+
annSizeFromWidth = outerWidth * Math.cos(dimAngle),
197+
annSizeFromHeight = outerHeight * Math.sin(dimAngle),
186198
// but this one is the positive total size
187199
annSize = Math.abs(annSizeFromWidth) + Math.abs(annSizeFromHeight),
188200
anchor = options[axLetter + 'anchor'],
@@ -299,22 +311,43 @@ function drawOne(gd, index) {
299311
return;
300312
}
301313

314+
var xShift = 0;
315+
var yShift = 0;
316+
317+
if(options.align !== 'left') {
318+
xShift = (annWidth - textWidth) * (options.align === 'center' ? 0.5 : 1);
319+
}
320+
if(options.valign !== 'top') {
321+
yShift = (annHeight - textHeight) * (options.valign === 'middle' ? 0.5 : 1);
322+
}
323+
302324
if(hasMathjax) {
303-
mathjaxGroup.select('svg').attr({x: borderfull - 1, y: borderfull});
325+
mathjaxGroup.select('svg').attr({
326+
x: borderfull + xShift - 1,
327+
y: borderfull + yShift
328+
})
329+
.call(Drawing.setClipUrl, annClipID);
304330
}
305331
else {
306-
var texty = borderfull - anntextBB.top,
307-
textx = borderfull - anntextBB.left;
308-
annText.attr({x: textx, y: texty});
332+
var texty = borderfull + yShift - anntextBB.top,
333+
textx = borderfull + xShift - anntextBB.left;
334+
annText.attr({
335+
x: textx,
336+
y: texty
337+
})
338+
.call(Drawing.setClipUrl, annClipID);
309339
annText.selectAll('tspan.line').attr({y: texty, x: textx});
310340
}
311341

342+
annTextClip.select('rect').call(Drawing.setRect, borderfull, borderfull,
343+
annWidth, annHeight);
344+
312345
annTextBG.call(Drawing.setRect, borderwidth / 2, borderwidth / 2,
313-
outerwidth - borderwidth, outerheight - borderwidth);
346+
outerWidth - borderwidth, outerHeight - borderwidth);
314347

315348
annTextGroupInner.call(Drawing.setTranslate,
316-
Math.round(annPosPx.x.text - outerwidth / 2),
317-
Math.round(annPosPx.y.text - outerheight / 2));
349+
Math.round(annPosPx.x.text - outerWidth / 2),
350+
Math.round(annPosPx.y.text - outerHeight / 2));
318351

319352
/*
320353
* rotate text and background

test/image/mocks/annotations-autorange.json

+33-7
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@
4848
"zeroline":false,
4949
"showline":true
5050
},
51-
"height":300,
51+
"height":360,
5252
"width":800,
53-
"margin":{"r":40,"b":40,"l":40,"t":40},
53+
"margin":{"r":40,"b":100,"l":40,"t":40},
5454
"annotations":[
5555
{"ay":0,"ax":50,"x":1,"y":1.5,"text":"Left"},
5656
{"ay":0,"ax":-50,"x":2,"y":1.5,"text":"Right"},
@@ -60,16 +60,42 @@
6060
"xref":"x2","yref":"y2","text":"From left","y":2,"ax":-17,"ay":0,"x":"2001-01-01",
6161
"xanchor": "right", "yanchor": "top", "textangle": 35, "bordercolor": "#444"
6262
},
63-
{"xref":"x2","yref":"y2","text":"From right","y":2,"x":"2001-03-01","ay":0,"ax":50},
63+
{
64+
"xref":"x2","yref":"y2","text":"From<br>right","y":2,"x":"2001-03-01","ay":0,"ax":50,
65+
"bgcolor": "#eee", "width": 50, "height": 40, "textangle": 70
66+
},
6467
{
6568
"xref":"x2","yref":"y2","text":"From top","y":3,"ax":0,"ay":-27,"x":"2001-02-01",
6669
"xanchor": "left", "yanchor": "bottom", "textangle": -15, "bordercolor": "#444"
6770
},
68-
{"xref":"x2","yref":"y2","text":"From Bottom","y":1,"ax":0,"ay":50,"x":"2001-02-01"},
69-
{"xref":"x3","yref":"y3","text":"Left<br>no<br>arrow","y":1.5,"x":1,"showarrow":false},
71+
{
72+
"xref":"x2","yref":"y2","text":"From Bottom","y":1,"ax":0,"ay":50,"x":"2001-02-01",
73+
"bordercolor": "#444", "borderwidth": 3, "height": 30
74+
},
75+
{
76+
"xref":"x3","yref":"y3","text":"Left<br>no<br>arrow","y":1.5,"x":1,"showarrow":false,
77+
"bordercolor": "#444", "bgcolor": "#eee", "width": 50, "height": 60, "textangle": 10,
78+
"align": "right", "valign": "bottom"
79+
},
7080
{"xref":"x3","yref":"y3","text":"Right<br>no<br>arrow","y":1.5,"x":2,"showarrow":false},
71-
{"xref":"x3","yref":"y3","text":"Bottom<br>no<br>arrow","y":1,"x":1.5,"showarrow":false},
72-
{"xref":"x3","yref":"y3","text":"Top<br>no<br>arrow","y":2,"x":1.5,"showarrow":false}
81+
{
82+
"xref":"x3","yref":"y3","text":"Bottom<br>no<br>arrow","y":1,"x":1.5,"showarrow":false,
83+
"bgcolor": "#eee", "width": 30, "height": 40, "textangle":-10,
84+
"align": "left", "valign": "top"
85+
},
86+
{"xref":"x3","yref":"y3","text":"Top<br>no<br>arrow","y":2,"x":1.5,"showarrow":false},
87+
{
88+
"xref": "paper", "yref": "paper", "text": "On the<br>bottom of the plot",
89+
"x": 0.3, "y": -0.1, "showarrow": false,
90+
"xanchor": "right", "yanchor": "top", "width": 200, "height": 60,
91+
"bgcolor": "#eee", "bordercolor": "#444"
92+
},
93+
{
94+
"xref": "paper", "yref": "paper", "text": "blah blah blah blah<br>blah<br>blah<br>blah<br>blah<br>blah",
95+
"x": 0.3, "y": -0.25, "ax": 100, "ay": 0, "textangle": 40, "borderpad": 4,
96+
"xanchor": "left", "yanchor": "bottom", "align": "left", "valign": "top",
97+
"width": 60, "height": 40, "bgcolor": "#eee", "bordercolor": "#444"
98+
}
7399
]
74100
}
75101
}

0 commit comments

Comments
 (0)