@@ -232,7 +232,13 @@ function _hover(gd, evt, subplot, noHoverEvent) {
232232 xval ,
233233 yval ,
234234 pointData ,
235- closedataPreviousLength ;
235+ closedataPreviousLength ,
236+
237+ // crosslinePoints: the set of candidate points we've found to draw crosslines to
238+ crosslinePoints = {
239+ hLinePoint : null ,
240+ vLinePoint : null
241+ } ;
236242
237243 // Figure out what we're hovering on:
238244 // mouse location or user-supplied data
@@ -402,10 +408,60 @@ function _hover(gd, evt, subplot, noHoverEvent) {
402408 hoverData . splice ( 0 , closedataPreviousLength ) ;
403409 distance = hoverData [ 0 ] . distance ;
404410 }
411+
412+ var showSpikes = fullLayout . xaxis && fullLayout . xaxis . showspikes && fullLayout . yaxis && fullLayout . yaxis . showspikes ;
413+ var showCrosslines = fullLayout . xaxis && fullLayout . xaxis . showcrossline || fullLayout . yaxis && fullLayout . yaxis . showcrossline ;
414+
415+ if ( fullLayout . _has ( 'cartesian' ) && showCrosslines && ! ( showSpikes && hovermode === 'closest' ) ) {
416+ // Now find the points for the crosslines.
417+ if ( fullLayout . yaxis . showcrossline ) {
418+ crosslinePoints . hLinePoint = findCrosslinePoint ( pointData , xval , yval , 'y' , crosslinePoints . hLinePoint ) ;
419+ }
420+ if ( fullLayout . xaxis . showcrossline ) {
421+ crosslinePoints . vLinePoint = findCrosslinePoint ( pointData , xval , yval , 'x' , crosslinePoints . vLinePoint ) ;
422+ }
423+ }
405424 }
406425
407- // nothing left: remove all labels and quit
408- if ( hoverData . length === 0 ) return dragElement . unhoverRaw ( gd , evt ) ;
426+ function findCrosslinePoint ( pointData , xval , yval , mode , endPoint ) {
427+ var resultPoint = endPoint ;
428+ pointData . distance = Infinity ;
429+ pointData . index = false ;
430+ var closestPoints = trace . _module . hoverPoints ( pointData , xval , yval , mode ) ;
431+ if ( closestPoints ) {
432+ var closestPt = closestPoints [ 0 ] ;
433+ if ( isNumeric ( closestPt . x0 ) && isNumeric ( closestPt . y0 ) ) {
434+ var tmpPoint = {
435+ xa : closestPt . xa ,
436+ ya : closestPt . ya ,
437+ x0 : closestPt . x0 ,
438+ x1 : closestPt . x1 ,
439+ y0 : closestPt . y0 ,
440+ y1 : closestPt . y1 ,
441+ distance : closestPt . distance ,
442+ curveNumber : closestPt . trace . index ,
443+ pointNumber : closestPt . index
444+ } ;
445+ if ( ! resultPoint || ( resultPoint . distance > tmpPoint . distance ) ) {
446+ resultPoint = tmpPoint ;
447+ }
448+ }
449+ }
450+ return resultPoint ;
451+ }
452+
453+ // if hoverData is empty check for the crosslines to draw and quit if there are none
454+ if ( hoverData . length === 0 ) {
455+ var result = dragElement . unhoverRaw ( gd , evt ) ;
456+ if ( fullLayout . _has ( 'cartesian' ) && ( ( crosslinePoints . hLinePoint !== null ) || ( crosslinePoints . vLinePoint !== null ) ) ) {
457+ createCrosslines ( crosslinePoints , fullLayout ) ;
458+ }
459+ return result ;
460+ }
461+
462+ if ( fullLayout . _has ( 'cartesian' ) ) {
463+ createCrosslines ( crosslinePoints , fullLayout ) ;
464+ }
409465
410466 hoverData . sort ( function ( d1 , d2 ) { return d1 . distance - d2 . distance ; } ) ;
411467
@@ -1089,6 +1145,77 @@ function cleanPoint(d, hovermode) {
10891145 return d ;
10901146}
10911147
1148+ function createCrosslines ( hoverData , fullLayout ) {
1149+ var showXSpikeline = fullLayout . xaxis && fullLayout . xaxis . showspikes ;
1150+ var showYSpikeline = fullLayout . yaxis && fullLayout . yaxis . showspikes ;
1151+ var showH = fullLayout . yaxis && fullLayout . yaxis . showcrossline ;
1152+ var showV = fullLayout . xaxis && fullLayout . xaxis . showcrossline ;
1153+ var container = fullLayout . _hoverlayer ;
1154+ var hovermode = fullLayout . hovermode ;
1155+ if ( ! ( showV || showH ) || ( showXSpikeline && showYSpikeline && hovermode === 'closest' ) ) return ;
1156+ var hLinePoint ,
1157+ vLinePoint ,
1158+ xa ,
1159+ ya ,
1160+ hLinePointY ,
1161+ vLinePointX ;
1162+
1163+ // Remove old crossline items
1164+ container . selectAll ( '.crossline' ) . remove ( ) ;
1165+
1166+ var contrastColor = Color . combine ( fullLayout . plot_bgcolor , fullLayout . paper_bgcolor ) ;
1167+ var dfltCrosslineColor = Color . contrast ( contrastColor ) ;
1168+
1169+ // do not draw a crossline if there is a spikeline
1170+ if ( showV && ! ( showXSpikeline && hovermode === 'closest' ) ) {
1171+ vLinePoint = hoverData . vLinePoint ;
1172+ xa = vLinePoint . xa ;
1173+ vLinePointX = xa . _offset + ( vLinePoint . x0 + vLinePoint . x1 ) / 2 ;
1174+
1175+ var xThickness = xa . crosslinethickness ;
1176+ var xDash = xa . crosslinedash ;
1177+ var xColor = xa . crosslinecolor || dfltCrosslineColor ;
1178+
1179+ // Foreground vertical line (to x-axis)
1180+ container . insert ( 'line' , ':first-child' )
1181+ . attr ( {
1182+ 'x1' : vLinePointX ,
1183+ 'x2' : vLinePointX ,
1184+ 'y1' : xa . _counterSpan [ 0 ] ,
1185+ 'y2' : xa . _counterSpan [ 1 ] ,
1186+ 'stroke-width' : xThickness ,
1187+ 'stroke' : xColor ,
1188+ 'stroke-dasharray' : Drawing . dashStyle ( xDash , xThickness )
1189+ } )
1190+ . classed ( 'crossline' , true )
1191+ . classed ( 'crisp' , true ) ;
1192+ }
1193+
1194+ if ( showH && ! ( showYSpikeline && hovermode === 'closest' ) ) {
1195+ hLinePoint = hoverData . hLinePoint ;
1196+ ya = hLinePoint . ya ;
1197+ hLinePointY = ya . _offset + ( hLinePoint . y0 + hLinePoint . y1 ) / 2 ;
1198+
1199+ var yThickness = ya . crosslinethickness ;
1200+ var yDash = ya . crosslinedash ;
1201+ var yColor = ya . crosslinecolor || dfltCrosslineColor ;
1202+
1203+ // Foreground horizontal line (to y-axis)
1204+ container . insert ( 'line' , ':first-child' )
1205+ . attr ( {
1206+ 'x1' : ya . _counterSpan [ 0 ] ,
1207+ 'x2' : ya . _counterSpan [ 1 ] ,
1208+ 'y1' : hLinePointY ,
1209+ 'y2' : hLinePointY ,
1210+ 'stroke-width' : yThickness ,
1211+ 'stroke' : yColor ,
1212+ 'stroke-dasharray' : Drawing . dashStyle ( yDash , yThickness )
1213+ } )
1214+ . classed ( 'crossline' , true )
1215+ . classed ( 'crisp' , true ) ;
1216+ }
1217+ }
1218+
10921219function createSpikelines ( hoverData , opts ) {
10931220 var hovermode = opts . hovermode ;
10941221 var container = opts . container ;
0 commit comments