Skip to content

Commit 293f201

Browse files
authored
Merge pull request #994 from monfera/FI-71
Hover / unhover events on the 2d WebGL plots
2 parents fce36fa + c1abd3e commit 293f201

File tree

8 files changed

+571
-13
lines changed

8 files changed

+571
-13
lines changed

src/plots/gl2d/scene2d.js

+42-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ function Scene2D(options, fullLayout) {
3333
this.staticPlot = !!options.staticPlot;
3434

3535
this.fullLayout = fullLayout;
36+
this.fullData = null;
3637
this.updateAxes(fullLayout);
3738

3839
this.makeFramework();
@@ -49,6 +50,7 @@ function Scene2D(options, fullLayout) {
4950

5051
// trace set
5152
this.traces = {};
53+
this._inputs = {};
5254

5355
// create axes spikes
5456
this.spikes = createSpikes(this.glplot);
@@ -58,6 +60,9 @@ function Scene2D(options, fullLayout) {
5860
outerFill: true
5961
});
6062

63+
// last button state
64+
this.lastButtonState = 0;
65+
6166
// last pick result
6267
this.pickResult = null;
6368

@@ -332,6 +337,8 @@ proto.destroy = function() {
332337
this.container.removeChild(this.svgContainer);
333338
this.container.removeChild(this.mouseContainer);
334339

340+
this.fullData = null;
341+
this._inputs = null;
335342
this.glplot = null;
336343
this.stopped = true;
337344
};
@@ -422,6 +429,8 @@ proto.updateTraces = function(fullData, calcData) {
422429
var traceIds = Object.keys(this.traces);
423430
var i, j, fullTrace;
424431

432+
this.fullData = fullData;
433+
425434
// remove empty traces
426435
trace_id_loop:
427436
for(i = 0; i < traceIds.length; i++) {
@@ -443,7 +452,7 @@ proto.updateTraces = function(fullData, calcData) {
443452
// update / create trace objects
444453
for(i = 0; i < fullData.length; i++) {
445454
fullTrace = fullData[i];
446-
455+
this._inputs[fullTrace.uid] = i;
447456
var calcTrace = calcData[i],
448457
traceObj = this.traces[fullTrace.uid];
449458

@@ -455,6 +464,24 @@ proto.updateTraces = function(fullData, calcData) {
455464
}
456465
};
457466

467+
proto.emitPointAction = function(nextSelection, eventType) {
468+
469+
var curveIndex = this._inputs[nextSelection.trace.uid];
470+
471+
this.graphDiv.emit(eventType, {
472+
points: [{
473+
x: nextSelection.traceCoord[0],
474+
y: nextSelection.traceCoord[1],
475+
curveNumber: curveIndex,
476+
pointNumber: nextSelection.pointIndex,
477+
data: this.fullData[curveIndex]._input,
478+
fullData: this.fullData,
479+
xaxis: this.xaxis,
480+
yaxis: this.yaxis
481+
}]
482+
});
483+
};
484+
458485
proto.draw = function() {
459486
if(this.stopped) return;
460487

@@ -463,8 +490,11 @@ proto.draw = function() {
463490
var glplot = this.glplot,
464491
camera = this.camera,
465492
mouseListener = camera.mouseListener,
493+
mouseUp = this.lastButtonState === 1 && mouseListener.buttons === 0,
466494
fullLayout = this.fullLayout;
467495

496+
this.lastButtonState = mouseListener.buttons;
497+
468498
this.cameraChanged();
469499

470500
var x = mouseListener.x * glplot.pixelRatio;
@@ -494,8 +524,13 @@ proto.draw = function() {
494524
(y / glplot.pixelRatio) - (size.t + (1 - domainY[1]) * size.h)
495525
);
496526

527+
var nextSelection = result && result.object._trace.handlePick(result);
528+
529+
if(nextSelection && mouseUp) {
530+
this.emitPointAction(nextSelection, 'plotly_click');
531+
}
532+
497533
if(result && result.object._trace.hoverinfo !== 'skip' && fullLayout.hovermode) {
498-
var nextSelection = result.object._trace.handlePick(result);
499534

500535
if(nextSelection && (
501536
!this.lastPickResult ||
@@ -522,6 +557,10 @@ proto.draw = function() {
522557
glplot.pixelRatio
523558
];
524559

560+
// this needs to happen before the next block that deletes traceCoord data
561+
// also it's important to copy, otherwise data is lost by the time event data is read
562+
this.emitPointAction(nextSelection, 'plotly_hover');
563+
525564
var hoverinfo = selection.hoverinfo;
526565
if(hoverinfo !== 'all') {
527566
var parts = hoverinfo.split('+');
@@ -549,6 +588,7 @@ proto.draw = function() {
549588
else if(!result && this.lastPickResult) {
550589
this.spikes.update({});
551590
this.lastPickResult = null;
591+
this.graphDiv.emit('plotly_unhover');
552592
Fx.loneUnhover(this.svgContainer);
553593
}
554594
}

src/traces/contourgl/convert.js

+10-6
Original file line numberDiff line numberDiff line change
@@ -60,20 +60,24 @@ function Contour(scene, uid) {
6060
var proto = Contour.prototype;
6161

6262
proto.handlePick = function(pickResult) {
63-
var index = pickResult.pointId,
64-
options = this.heatmapOptions,
65-
shape = options.shape;
63+
var options = this.heatmapOptions,
64+
shape = options.shape,
65+
index = pickResult.pointId,
66+
xIndex = index % shape[0],
67+
yIndex = Math.floor(index / shape[0]),
68+
zIndex = index;
6669

6770
return {
6871
trace: this,
6972
dataCoord: pickResult.dataCoord,
7073
traceCoord: [
71-
options.x[index % shape[0]],
72-
options.y[Math.floor(index / shape[0])],
73-
options.z[index]
74+
options.x[xIndex],
75+
options.y[yIndex],
76+
options.z[zIndex]
7477
],
7578
textLabel: this.textLabels[index],
7679
name: this.name,
80+
pointIndex: [xIndex, yIndex],
7781
hoverinfo: this.hoverinfo
7882
};
7983
};

src/traces/heatmapgl/convert.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,24 @@ function Heatmap(scene, uid) {
4646
var proto = Heatmap.prototype;
4747

4848
proto.handlePick = function(pickResult) {
49-
var index = pickResult.pointId,
50-
shape = this.options.shape;
49+
var options = this.options,
50+
shape = options.shape,
51+
index = pickResult.pointId,
52+
xIndex = index % shape[0],
53+
yIndex = Math.floor(index / shape[0]),
54+
zIndex = index;
5155

5256
return {
5357
trace: this,
5458
dataCoord: pickResult.dataCoord,
5559
traceCoord: [
56-
this.options.x[index % shape[0]],
57-
this.options.y[Math.floor(index / shape[0])],
58-
this.options.z[index]
60+
options.x[xIndex],
61+
options.y[yIndex],
62+
options.z[zIndex]
5963
],
6064
textLabel: this.textLabels[index],
6165
name: this.name,
66+
pointIndex: [xIndex, yIndex],
6267
hoverinfo: this.hoverinfo
6368
};
6469
};

src/traces/pointcloud/convert.js

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ proto.handlePick = function(pickResult) {
6363
this.textLabels,
6464
color: this.color,
6565
name: this.name,
66+
pointIndex: index,
6667
hoverinfo: this.hoverinfo
6768
};
6869
};

src/traces/scattergl/convert.js

+1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ proto.handlePick = function(pickResult) {
128128
this.color[index] :
129129
this.color,
130130
name: this.name,
131+
pointIndex: index,
131132
hoverinfo: this.hoverinfo
132133
};
133134
};

test/jasmine/assets/hover.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
var mouseEvent = require('./mouse_event');
2+
3+
module.exports = function hover(x, y) {
4+
mouseEvent('mousemove', x, y);
5+
};

test/jasmine/assets/timed_click.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
var mouseEvent = require('./mouse_event');
2+
3+
module.exports = function click(x, y) {
4+
mouseEvent('mousemove', x, y, {buttons: 0});
5+
6+
window.setTimeout(function() {
7+
8+
mouseEvent('mousedown', x, y, {buttons: 1});
9+
10+
window.setTimeout(function() {
11+
12+
mouseEvent('mouseup', x, y, {buttons: 0});
13+
14+
}, 50);
15+
16+
}, 150);
17+
};

0 commit comments

Comments
 (0)