Skip to content

Commit be47533

Browse files
committed
Add spikecolor, spikethickness, spikedash and spikemode
Move 'dash' function to lib for re-use Use dynamic contrast color based on same logic as hovertext contrast text
1 parent 5d39b53 commit be47533

File tree

5 files changed

+154
-67
lines changed

5 files changed

+154
-67
lines changed

src/components/drawing/index.js

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -113,19 +113,8 @@ drawing.lineGroupStyle = function(s, lw, lc, ld) {
113113

114114
drawing.dashLine = function(s, dash, lineWidth) {
115115
lineWidth = +lineWidth || 0;
116-
var dlw = Math.max(lineWidth, 3);
117-
118-
if(dash === 'solid') dash = '';
119-
else if(dash === 'dot') dash = dlw + 'px,' + dlw + 'px';
120-
else if(dash === 'dash') dash = (3 * dlw) + 'px,' + (3 * dlw) + 'px';
121-
else if(dash === 'longdash') dash = (5 * dlw) + 'px,' + (5 * dlw) + 'px';
122-
else if(dash === 'dashdot') {
123-
dash = (3 * dlw) + 'px,' + dlw + 'px,' + dlw + 'px,' + dlw + 'px';
124-
}
125-
else if(dash === 'longdashdot') {
126-
dash = (5 * dlw) + 'px,' + (2 * dlw) + 'px,' + dlw + 'px,' + (2 * dlw) + 'px';
127-
}
128-
// otherwise user wrote the dasharray themselves - leave it be
116+
117+
dash = Lib.dash(dash, lineWidth);
129118

130119
s.style({
131120
'stroke-dasharray': dash,

src/lib/index.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,3 +636,31 @@ lib.numSeparate = function(value, separators, separatethousands) {
636636

637637
return x1 + x2;
638638
};
639+
640+
/**
641+
* Accepts a named dash style or stroke-dasharray string
642+
* and returns a dasharray representing the named type.
643+
*
644+
* @param {string} dash named dash format, or dasharray
645+
* @param {number} lineWidth width of the line, used to scale the dashes
646+
*
647+
* @returns {string} SVG stroke-dasharray formatted string
648+
*/
649+
lib.dash = function(dash, lineWidth) {
650+
lineWidth = +lineWidth || 1;
651+
var dlw = Math.max(lineWidth, 3);
652+
653+
if(dash === 'solid') dash = '';
654+
else if(dash === 'dot') dash = dlw + 'px,' + dlw + 'px';
655+
else if(dash === 'dash') dash = (3 * dlw) + 'px,' + (3 * dlw) + 'px';
656+
else if(dash === 'longdash') dash = (5 * dlw) + 'px,' + (5 * dlw) + 'px';
657+
else if(dash === 'dashdot') {
658+
dash = (3 * dlw) + 'px,' + dlw + 'px,' + dlw + 'px,' + dlw + 'px';
659+
}
660+
else if(dash === 'longdashdot') {
661+
dash = (5 * dlw) + 'px,' + (2 * dlw) + 'px,' + dlw + 'px,' + (2 * dlw) + 'px';
662+
}
663+
// otherwise user wrote the dasharray themselves - leave it be
664+
665+
return dash;
666+
};

src/plots/cartesian/axis_defaults.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce,
100100
containerOut.cleanRange();
101101

102102
coerce('showspikes');
103+
coerce('spikecolor');
104+
coerce('spikethickness');
105+
coerce('spikedash');
106+
coerce('spikemode');
103107

104108
handleTickValueDefaults(containerIn, containerOut, coerce, axType);
105109
handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options);

src/plots/cartesian/graph_interact.js

Lines changed: 75 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -857,73 +857,95 @@ function createDroplines(hoverData, opts) {
857857
yAnchoredBase = yEdge - outerBBox.top,
858858
xBase = c0.ya.anchor === 'free' ? xFreeBase : xAnchoredBase,
859859
yBase = c0.xa.anchor === 'free' ? yFreeBase : yAnchoredBase,
860-
color = c0.color;
860+
xColor = c0.xa.spikecolor ? c0.xa.spikecolor : c0.color,
861+
yColor = c0.ya.spikecolor ? c0.ya.spikecolor : c0.color,
862+
xContrastColor = tinycolor(xColor).getBrightness() > 128 ?
863+
'#000' : Color.background,
864+
yContrastColor = tinycolor(yColor).getBrightness() > 128 ?
865+
'#000' : Color.background,
866+
xThickness = c0.xa.spikethickness,
867+
yThickness = c0.ya.spikethickness,
868+
xDash = Lib.dash(c0.xa.spikedash, xThickness),
869+
yDash = Lib.dash(c0.xa.spikedash, yThickness),
870+
xMarker = c0.xa.spikemode.indexOf('marker') !== -1,
871+
yMarker = c0.ya.spikemode.indexOf('marker') !== -1,
872+
xSpikeLine = c0.xa.spikemode.indexOf('toaxis') !== -1 || c0.xa.spikemode.indexOf('across') !== -1,
873+
ySpikeLine = c0.ya.spikemode.indexOf('toaxis') !== -1 || c0.ya.spikemode.indexOf('across') !== -1,
874+
xEndSpike = c0.xa.spikemode.indexOf('across') !== -1 ? xBase + xLength : xPoint,
875+
yEndSpike = c0.ya.spikemode.indexOf('across') !== -1 ? yBase - yLength : yPoint;
861876

862877
// Remove old dropline items
863878
container.selectAll('line.dropline').remove();
864879
container.selectAll('circle.dropline').remove();
865880

866881

867882
if(c0.ya.showspikes) {
868-
// Background horizontal Line (to y-axis)
869-
container.append('line')
870-
.attr('x1', xBase)
871-
.attr('x2', xPoint)
872-
.attr('y1', yPoint)
873-
.attr('y2', yPoint)
874-
.attr('stroke-width', 5)
875-
.attr('stroke', '#fff')
876-
.attr('class', 'dropline');
877-
878-
// Foreground horizontal line (to y-axis)
879-
container.append('line')
880-
.attr('x1', xBase)
881-
.attr('x2', xPoint)
882-
.attr('y1', yPoint)
883-
.attr('y2', yPoint)
884-
.attr('stroke-width', 3)
885-
.attr('stroke', color)
886-
.attr('stroke-dasharray', '5,5')
887-
.attr('class', 'dropline');
888-
883+
if(ySpikeLine) {
884+
// Background horizontal Line (to y-axis)
885+
container.append('line')
886+
.attr('x1', xBase)
887+
.attr('x2', xEndSpike)
888+
.attr('y1', yPoint)
889+
.attr('y2', yPoint)
890+
.attr('stroke-width', yThickness + 2)
891+
.attr('stroke', yContrastColor)
892+
.attr('class', 'dropline');
893+
894+
// Foreground horizontal line (to y-axis)
895+
container.append('line')
896+
.attr('x1', xBase)
897+
.attr('x2', xEndSpike)
898+
.attr('y1', yPoint)
899+
.attr('y2', yPoint)
900+
.attr('stroke-width', yThickness)
901+
.attr('stroke', yColor)
902+
.attr('stroke-dasharray', yDash)
903+
.attr('class', 'dropline');
904+
}
889905
// Y axis marker
890-
container.append('circle')
891-
.attr('cx', xBase)
892-
.attr('cy', yPoint)
893-
.attr('r', 3)
894-
.attr('fill', color)
895-
.attr('class', 'dropline');
906+
if(yMarker) {
907+
container.append('circle')
908+
.attr('cx', xAnchoredBase)
909+
.attr('cy', yPoint)
910+
.attr('r', yThickness)
911+
.attr('fill', yColor)
912+
.attr('class', 'dropline');
913+
}
896914
}
897915

898916
if(c0.xa.showspikes) {
917+
if(xSpikeLine) {
899918
// Background vertical line (to x-axis)
900-
container.append('line')
901-
.attr('x1', xPoint)
902-
.attr('x2', xPoint)
903-
.attr('y1', yPoint)
904-
.attr('y2', yBase)
905-
.attr('stroke-width', 5)
906-
.attr('stroke', '#fff')
907-
.attr('class', 'dropline');
908-
909-
// Foreground vertical line (to x-axis)
910-
container.append('line')
911-
.attr('x1', xPoint)
912-
.attr('x2', xPoint)
913-
.attr('y1', yPoint)
914-
.attr('y2', yBase)
915-
.attr('stroke-width', 3)
916-
.attr('stroke', color)
917-
.attr('stroke-dasharray', '5,5')
918-
.attr('class', 'dropline');
919+
container.append('line')
920+
.attr('x1', xPoint)
921+
.attr('x2', xPoint)
922+
.attr('y1', yEndSpike)
923+
.attr('y2', yBase)
924+
.attr('stroke-width', xThickness + 2)
925+
.attr('stroke', xContrastColor)
926+
.attr('class', 'dropline');
927+
928+
// Foreground vertical line (to x-axis)
929+
container.append('line')
930+
.attr('x1', xPoint)
931+
.attr('x2', xPoint)
932+
.attr('y1', yEndSpike)
933+
.attr('y2', yBase)
934+
.attr('stroke-width', xThickness)
935+
.attr('stroke', xColor)
936+
.attr('stroke-dasharray', xDash)
937+
.attr('class', 'dropline');
938+
}
919939

920940
// X axis marker
921-
container.append('circle')
922-
.attr('cx', xPoint)
923-
.attr('cy', yBase)
924-
.attr('r', 3)
925-
.attr('fill', color)
926-
.attr('class', 'dropline');
941+
if(xMarker) {
942+
container.append('circle')
943+
.attr('cx', xPoint)
944+
.attr('cy', yAnchoredBase)
945+
.attr('r', xThickness)
946+
.attr('fill', xColor)
947+
.attr('class', 'dropline');
948+
}
927949
}
928950
}
929951

src/plots/cartesian/layout_attributes.js

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,13 +246,57 @@ module.exports = {
246246
},
247247
showspikes: {
248248
valType: 'boolean',
249-
dflt: false,
249+
dflt: true,
250250
role: 'style',
251251
description: [
252252
'Determines whether or not spikes (aka droplines) are drawn for this axis.',
253253
'Note: This only takes affect when hovermode = closest'
254254
].join(' ')
255255
},
256+
spikecolor: {
257+
valType: 'color',
258+
dflt: null,
259+
role: 'style',
260+
description: 'Sets the spike color. If undefined, will use the series color'
261+
},
262+
spikethickness: {
263+
valType: 'number',
264+
dflt: 3,
265+
role: 'style',
266+
description: 'Sets the width (in px) of the zero line.'
267+
},
268+
spikedash: {
269+
valType: 'string',
270+
// string type usually doesn't take values... this one should really be
271+
// a special type or at least a special coercion function, from the GUI
272+
// you only get these values but elsewhere the user can supply a list of
273+
// dash lengths in px, and it will be honored
274+
values: ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'],
275+
dflt: 'dash',
276+
role: 'style',
277+
description: [
278+
'Sets the style of the lines. Set to a dash string type',
279+
'or a dash length in px.'
280+
].join(' ')
281+
},
282+
spikemode: {
283+
valType: 'flaglist',
284+
flags: ['toaxis', 'across', 'marker'],
285+
extras: ['none'],
286+
role: 'style',
287+
dflt: 'toaxis',
288+
description: [
289+
'Determines the drawing mode for the spike line',
290+
'If *toaxis*, the line is drawn from the data point to the axis the ',
291+
'series is plotted on.',
292+
293+
'If *across*, the line is drawn across the entire plot area, and',
294+
'supercedes *toaxis*.',
295+
296+
'If *marker*, then a marker dot is drawn on the axis the series is',
297+
'plotted on'
298+
].join(' ')
299+
},
256300
tickfont: extendFlat({}, fontAttrs, {
257301
description: 'Sets the tick font.'
258302
}),

0 commit comments

Comments
 (0)