Skip to content

Commit eb35573

Browse files
authored
Merge branch 'main' into feature-hoverlabel-custom-styles
2 parents db89bb8 + c31fee7 commit eb35573

21 files changed

Lines changed: 533 additions & 439 deletions

‎draftlogs/8052_fix.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Fix issue causing `scattergl` plot to freeze up when hovering over points [[#8052](https://github.com/plotly/plotly.js/pull/8052)], with thanks to @emilykl for the contribution!

‎src/components/fx/hover.js‎

Lines changed: 69 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,16 @@ exports.loneHover = function loneHover(hoverItems, opts) {
299299
return multiHover ? hoverLabel : hoverLabel.node();
300300
};
301301

302+
// Helper function for use with spikeline logic
303+
// Returns true if either axis has spikeline settings which allow a spikeline to be drawn
304+
// to a point that is not currently hovered over; returns false otherwise.
305+
function canSpikeToNonHoveredPoint(xa, ya) {
306+
return Boolean(
307+
(xa?.showspikes && xa.spikesnap !== 'hovered data') ||
308+
(ya?.showspikes && ya.spikesnap !== 'hovered data')
309+
);
310+
}
311+
302312
// The actual implementation is here:
303313
function _hover(gd, evt, subplot, noHoverEvent, eventTarget) {
304314
if (!subplot) subplot = 'xy';
@@ -729,53 +739,58 @@ function _hover(gd, evt, subplot, noHoverEvent, eventTarget) {
729739
}
730740
}
731741

732-
// Now if there is range to look in, find the points to draw the spikelines
733-
// Do it only if there is no hoverData
734-
if (hasCartesian && spikedistance !== 0) {
735-
if (hoverData.length === 0) {
736-
pointData.distance = spikedistance;
737-
pointData.index = false;
738-
var closestPoints = trace._module.hoverPoints(pointData, xval, yval, 'closest', {
739-
hoverLayer: fullLayout._hoverlayer
742+
// If there is no hoverData, and if the axis spikeline settings permit us to draw
743+
// a spikeline to the nearest non-hovered point, search for points to spike to.
744+
// This search is _expensive_ (especially when spikedistance is -1 which requires
745+
// a search over all points), so we should only do it when absolutely required
746+
if (hasCartesian
747+
&& hoverData.length === 0
748+
&& spikedistance !== 0
749+
&& canSpikeToNonHoveredPoint(pointData.xa, pointData.ya)
750+
) {
751+
// Find the points to draw the spikelines
752+
pointData.distance = spikedistance;
753+
pointData.index = false;
754+
var closestPoints = trace._module.hoverPoints(pointData, xval, yval, 'closest', {
755+
hoverLayer: fullLayout._hoverlayer
756+
});
757+
if (closestPoints) {
758+
closestPoints = closestPoints.filter(function (point) {
759+
// some hover points, like scatter fills, do not allow spikes,
760+
// so will generate a hover point but without a valid spikeDistance
761+
return point.spikeDistance <= spikedistance;
740762
});
741-
if (closestPoints) {
742-
closestPoints = closestPoints.filter(function (point) {
743-
// some hover points, like scatter fills, do not allow spikes,
744-
// so will generate a hover point but without a valid spikeDistance
745-
return point.spikeDistance <= spikedistance;
746-
});
747-
}
748-
if (closestPoints && closestPoints.length) {
749-
var tmpPoint;
750-
var closestVPoints = closestPoints.filter(function (point) {
751-
return point.xa.showspikes && point.xa.spikesnap !== 'hovered data';
752-
});
753-
if (closestVPoints.length) {
754-
var closestVPt = closestVPoints[0];
755-
if (isNumeric(closestVPt.x0) && isNumeric(closestVPt.y0)) {
756-
tmpPoint = fillSpikePoint(closestVPt);
757-
if (
758-
!spikePoints.vLinePoint ||
759-
spikePoints.vLinePoint.spikeDistance > tmpPoint.spikeDistance
760-
) {
761-
spikePoints.vLinePoint = tmpPoint;
762-
}
763+
}
764+
if (closestPoints?.length) {
765+
var tmpPoint;
766+
var closestVPoints = closestPoints.filter(function (point) {
767+
return point.xa.showspikes && point.xa.spikesnap !== 'hovered data';
768+
});
769+
if (closestVPoints.length) {
770+
var closestVPt = closestVPoints[0];
771+
if (isNumeric(closestVPt.x0) && isNumeric(closestVPt.y0)) {
772+
tmpPoint = fillSpikePoint(closestVPt);
773+
if (
774+
!spikePoints.vLinePoint ||
775+
spikePoints.vLinePoint.spikeDistance > tmpPoint.spikeDistance
776+
) {
777+
spikePoints.vLinePoint = tmpPoint;
763778
}
764779
}
780+
}
765781

766-
var closestHPoints = closestPoints.filter(function (point) {
767-
return point.ya.showspikes && point.ya.spikesnap !== 'hovered data';
768-
});
769-
if (closestHPoints.length) {
770-
var closestHPt = closestHPoints[0];
771-
if (isNumeric(closestHPt.x0) && isNumeric(closestHPt.y0)) {
772-
tmpPoint = fillSpikePoint(closestHPt);
773-
if (
774-
!spikePoints.hLinePoint ||
775-
spikePoints.hLinePoint.spikeDistance > tmpPoint.spikeDistance
776-
) {
777-
spikePoints.hLinePoint = tmpPoint;
778-
}
782+
var closestHPoints = closestPoints.filter(function (point) {
783+
return point.ya.showspikes && point.ya.spikesnap !== 'hovered data';
784+
});
785+
if (closestHPoints.length) {
786+
var closestHPt = closestHPoints[0];
787+
if (isNumeric(closestHPt.x0) && isNumeric(closestHPt.y0)) {
788+
tmpPoint = fillSpikePoint(closestHPt);
789+
if (
790+
!spikePoints.hLinePoint ||
791+
spikePoints.hLinePoint.spikeDistance > tmpPoint.spikeDistance
792+
) {
793+
spikePoints.hLinePoint = tmpPoint;
779794
}
780795
}
781796
}
@@ -1679,12 +1694,12 @@ function createHoverText(hoverData, opts) {
16791694
var mainText = !unifiedhovertitleText
16801695
? t0
16811696
: Lib.hovertemplateString({
1682-
data:
1683-
hovermode === 'x unified' ? [{ xa: item0.xa, x: item0.xVal }] : [{ ya: item0.ya, y: item0.yVal }],
1684-
fallback: item0.trace.hovertemplatefallback,
1685-
locale: fullLayout._d3locale,
1686-
template: unifiedhovertitleText
1687-
});
1697+
data:
1698+
hovermode === 'x unified' ? [{ xa: item0.xa, x: item0.xVal }] : [{ ya: item0.ya, y: item0.yVal }],
1699+
fallback: item0.trace.hovertemplatefallback,
1700+
locale: fullLayout._d3locale,
1701+
template: unifiedhovertitleText
1702+
});
16881703

16891704
var mockLayoutIn = {
16901705
showlegend: true,
@@ -2694,12 +2709,12 @@ function cleanPoint(d, hovermode) {
26942709

26952710
var getVal = Array.isArray(index)
26962711
? function (calcKey, traceKey) {
2697-
var v = Lib.castOption(cd0, index, calcKey);
2698-
return pass(v) ? v : Lib.extractOption({}, trace, '', traceKey);
2699-
}
2712+
var v = Lib.castOption(cd0, index, calcKey);
2713+
return pass(v) ? v : Lib.extractOption({}, trace, '', traceKey);
2714+
}
27002715
: function (calcKey, traceKey) {
2701-
return Lib.extractOption(cd, trace, calcKey, traceKey);
2702-
};
2716+
return Lib.extractOption(cd, trace, calcKey, traceKey);
2717+
};
27032718

27042719
function fill(key, calcKey, traceKey) {
27052720
var val = getVal(calcKey, traceKey);

‎test/image/baselines/map_0.png‎

-399 Bytes
Loading
-107 Bytes
Loading
-1.47 KB
Loading
-840 Bytes
Loading
-134 Bytes
Loading
79 Bytes
Loading
-766 Bytes
Loading
-528 Bytes
Loading

0 commit comments

Comments
 (0)