Skip to content

Commit

Permalink
tap threshold for pan; cleanup; ref cytoscape#636
Browse files Browse the repository at this point in the history
  • Loading branch information
maxkfranz committed Dec 19, 2014
1 parent 50c98d8 commit ed9772e
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 46 deletions.
6 changes: 5 additions & 1 deletion documentation/md/core/init.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ var cy = cytoscape({
userPanningEnabled: true,
boxSelectionEnabled: true,
selectionType: (isTouchDevice ? 'additive' : 'single'),
touchTapThreshold: 8,
desktopTapThreshold: 4,
autolock: false,
autoungrabify: false,
autounselectify: false,
Expand Down Expand Up @@ -176,9 +178,11 @@ var cy = cytoscape({

**`selectionType`** : A string indicating the selection behaviour from user input. By default, this is set automatically for you based on the type of input device detected. On touch devices, `'additive'` is default — a new selection made by the user adds to the set of currenly selected elements. On mouse-input devices, `'single'` is default — a new selection made by the user becomes the entire set of currently selected elements (i.e. the previous elements are unselected).

**`touchTapThreshold`** & **`desktopTapThreshold`** : A nonnegative integer that indicates the maximum allowable distance that a user may move during a tap gesture, on touch devices and desktop devices respectively. This makes tapping easier for users. These values have sane defaults, so it is not advised to change these options unless you have very good reason for doing so. Larger values will almost certainly have undesirable consequences.

**`autoungrabify`** : Whether nodes should be ungrabified (not grabbable by user) by default (if `true`, overrides individual node state).

**`autolock`** : Whether nodes should be locked (not movable at all) by default (if `true`, overrides individual node state).
**`autolock`** : Whether nodes should be locked (not draggable at all) by default (if `true`, overrides individual node state).

**`autounselectify`** : Whether nodes should be unselectified (immutible selection state) by default (if `true`, overrides individual element state).

Expand Down
2 changes: 1 addition & 1 deletion src/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@
wheelSensitivity: $$.is.number(options.wheelSensitivity) && options.wheelSensitivity > 0 ? options.wheelSensitivity : 1,
motionBlur: options.motionBlur,
pixelRatio: $$.is.number(options.pixelRatio) && options.pixelRatio > 0 ? options.pixelRatio : (options.pixelRatio === 'auto' ? undefined : 1),
tapThreshold: $$.is.touch() ? 8 : 4
tapThreshold: defVal( $$.is.touch() ? 8 : 4, $$.is.touch() ? options.touchTapThreshold : options.desktopTapThreshold )
}, options.renderer) );

// trigger the passed function for the `initrender` event
Expand Down
1 change: 1 addition & 0 deletions src/extensions/renderer.canvas.define-and-init-etc.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
this.motionBlur = true; // for initial kick off
this.tapThreshold = options.tapThreshold;
this.tapThreshold2 = options.tapThreshold * options.tapThreshold;
this.tapholdDuration = 500;

this.load();
}
Expand Down
180 changes: 136 additions & 44 deletions src/extensions/renderer.canvas.load-and-listeners.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,41 @@
r.hoverData.capture = true;
r.hoverData.which = e.which;

var cy = r.data.cy; var pos = r.projectIntoViewport(e.clientX, e.clientY);
var cy = r.data.cy;
var pos = r.projectIntoViewport(e.clientX, e.clientY);
var select = r.data.select;
var near = r.findNearestElement(pos[0], pos[1], true);
var draggedElements = r.dragData.possibleDragElements;
var grabEvent = new $$.Event('grab');

r.hoverData.mdownPos = pos;

var checkForTaphold = function(){
r.hoverData.tapholdCancelled = false;

clearTimeout( r.hoverData.tapholdTimeout );

r.hoverData.tapholdTimeout = setTimeout(function(){

if( r.hoverData.tapholdCancelled ){
return;
} else {
var ele = r.hoverData.down;

if( ele ){
ele.trigger( new $$.Event(e, {
type: 'taphold',
cyPosition: { x: pos[0], y: pos[1] }
}) );
} else {
cy.trigger( new $$.Event(e, {
type: 'taphold',
cyPosition: { x: pos[0], y: pos[1] }
}) );
}
}

}, r.tapholdDuration);
};

// Right click button
if( e.which == 3 ){
Expand Down Expand Up @@ -273,6 +303,11 @@

if( r.nodeIsDraggable(near) ){

var grabEvent = new $$.Event(e, {
type: 'grab',
cyPosition: { x: pos[0], y: pos[1] }
});

if ( near.isNode() && !near.selected() ){

draggedElements = r.dragData.possibleDragElements = [];
Expand Down Expand Up @@ -356,6 +391,8 @@
y: pos[1]
};

checkForTaphold();

r.data.canvasNeedsRedraw[CanvasRenderer.SELECT_BOX] = true;

r.redraw();
Expand All @@ -366,6 +403,8 @@
y: pos[1]
};

checkForTaphold();

r.data.canvasNeedsRedraw[CanvasRenderer.SELECT_BOX] = true;

r.redraw();
Expand Down Expand Up @@ -430,6 +469,27 @@
var disp = [pos[0] - select[2], pos[1] - select[3]];

var draggedElements = r.dragData.possibleDragElements;

var dx = select[2] - select[0];
var dx2 = dx * dx;
var dy = select[3] - select[1];
var dy2 = dy * dy;
var dist2 = dx2 + dy2;
var rdist2 = dist2 * zoom * zoom;

r.hoverData.tapholdCancelled = true;

var updateDragDelta = function(){
var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];

if( dragDelta.length === 0 ){
dragDelta.push(0);
dragDelta.push(0);
} else {
dragDelta[0] += disp[0];
dragDelta[1] += disp[1];
}
};


preventDefault = true;
Expand Down Expand Up @@ -516,21 +576,44 @@
preventDefault = true;

if( cy.panningEnabled() && cy.userPanningEnabled() ){
var deltaP = {x: disp[0] * cy.zoom(), y: disp[1] * cy.zoom()};
var deltaP;

if( r.hoverData.justStartedPan ){
var mdPos = r.hoverData.mdownPos;

deltaP = {
x: ( pos[0] - mdPos[0] ) * zoom,
y: ( pos[1] - mdPos[1] ) * zoom
};

r.hoverData.justStartedPan = false;

} else {
deltaP = {
x: disp[0] * zoom,
y: disp[1] * zoom
};

}

cy.panBy( deltaP );

}

// Needs reproject due to pan changing viewport
pos = r.projectIntoViewport(e.clientX, e.clientY);

// Checks primary button down & out of time & mouse not moved much
} else if (select[4] == 1 && (down == null || down.isEdge())
} else if(
select[4] == 1 && (down == null || down.isEdge())
&& ( !cy.boxSelectionEnabled() || +new Date() - r.hoverData.downTime >= CanvasRenderer.panOrBoxSelectDelay )
&& (Math.abs(select[3] - select[1]) + Math.abs(select[2] - select[0]) < 4)
&& cy.panningEnabled() && cy.userPanningEnabled() ) {
//&& (Math.abs(select[3] - select[1]) + Math.abs(select[2] - select[0]) < 4)
&& rdist2 >= r.tapThreshold2
&& cy.panningEnabled() && cy.userPanningEnabled()
){

r.hoverData.dragging = true;
r.hoverData.justStartedPan = true;
select[4] = 0;

} else {
Expand Down Expand Up @@ -573,13 +656,6 @@

r.hoverData.last = near;
}

var dx = select[2] - select[0];
var dx2 = dx * dx;
var dy = select[3] - select[1];
var dy2 = dy * dy;
var dist2 = dx2 + dy2;
var rdist2 = dist2 * zoom * zoom;

if( down && down.isNode() && r.nodeIsDraggable(down) ){

Expand Down Expand Up @@ -637,15 +713,7 @@
r.redraw();

} else { // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];

if( dragDelta.length === 0 ){
dragDelta.push(0);
dragDelta.push(0);
} else {
dragDelta[0] += disp[0];
dragDelta[1] += disp[1];
}
updateDragDelta();
}
}

Expand Down Expand Up @@ -1033,13 +1101,16 @@
r.data.bgActivePosistion = undefined;

var cy = r.data.cy;
var nodes = r.getCachedNodes(); var edges = r.getCachedEdges();
var now = r.touchData.now; var earlier = r.touchData.earlier;
var nodes = r.getCachedNodes();
var edges = r.getCachedEdges();
var now = r.touchData.now;
var earlier = r.touchData.earlier;

if (e.touches[0]) { var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY); now[0] = pos[0]; now[1] = pos[1]; }
if (e.touches[1]) { var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY); now[2] = pos[0]; now[3] = pos[1]; }
if (e.touches[2]) { var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY); now[4] = pos[0]; now[5] = pos[1]; }



// record starting points for pinch-to-zoom
if( e.touches[1] ){

Expand Down Expand Up @@ -1177,7 +1248,10 @@
addNodeToDrag( near, { addToList: draggedEles } );
}

near.trigger('grab');
near.trigger( new $$.Event(e, {
type: 'grab',
cyPosition: { x: now[0], y: now[1] }
}) );
}

near
Expand Down Expand Up @@ -1231,13 +1305,16 @@
r.touchData.singleTouchMoved = false;
r.touchData.singleTouchStartTime = +new Date();

setTimeout(function() {
if (r.touchData.singleTouchMoved === false
clearTimeout( r.touchData.tapholdTimeout );
r.touchData.tapholdTimeout = setTimeout(function() {
if(
r.touchData.singleTouchMoved === false
&& !r.pinching // if pinching, then taphold unselect shouldn't take effect

// This time double constraint prevents multiple quick taps
// followed by a taphold triggering multiple taphold events
&& (+new Date()) - r.touchData.singleTouchStartTime > 250) {
//&& Date.now() - r.touchData.singleTouchStartTime > 250
){
if (r.touchData.start) {
r.touchData.start.trigger( new $$.Event(e, {
type: 'taphold',
Expand All @@ -1254,7 +1331,7 @@

// console.log('taphold');
}
}, 1000);
}, r.tapholdDuration);
}

//r.redraw();
Expand All @@ -1278,6 +1355,14 @@
if (e.touches[2]) { var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY); now[4] = pos[0]; now[5] = pos[1]; }
var disp = []; for (var j=0;j<now.length;j++) { disp[j] = now[j] - earlier[j]; }

var startPos = r.touchData.startPosition;

var dx = now[0] - startPos[0];
var dx2 = dx * dx;
var dy = now[1] - startPos[1];
var dy2 = dy * dy;
var dist2 = dx2 + dy2;
var rdist2 = dist2 * zoom * zoom;

if( capture && r.touchData.cxt ){
var f1x2 = e.touches[0].clientX - offsetLeft, f1y2 = e.touches[0].clientY - offsetTop;
Expand Down Expand Up @@ -1502,16 +1587,6 @@

if( start != null && start._private.group == 'nodes' && r.nodeIsDraggable(start) ){

var startPos = r.touchData.startPosition;

var dx = now[0] - startPos[0];
var dx2 = dx * dx;
var dy = now[1] - startPos[1];
var dy2 = dy * dy;
var dist2 = dx2 + dy2;
var rdist2 = dist2 * zoom * zoom;

// TODO thresh
if( rdist2 >= r.tapThreshold2 ){ // then dragging can happen
var draggedEles = r.dragData.touchDragEles;

Expand Down Expand Up @@ -1648,7 +1723,27 @@
}
}

if ( capture && (start == null || start.isEdge()) && cy.panningEnabled() && cy.userPanningEnabled() ) {
if(
capture
&& ( start == null || start.isEdge() )
&& cy.panningEnabled() && cy.userPanningEnabled()
){

if( r.swipePanning ){
cy.panBy({
x: disp[0] * zoom,
y: disp[1] * zoom
});

} else if( rdist2 >= r.tapThreshold2 ){
r.swipePanning = true;

cy.panBy({
x: dx * zoom,
y: dy * zoom
});
}

if( start ){
start.unactivate();

Expand All @@ -1663,9 +1758,6 @@

r.touchData.start = null;
}

cy.panBy({x: disp[0] * cy.zoom(), y: disp[1] * cy.zoom()});
r.swipePanning = true;

// Re-project
var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
Expand Down

0 comments on commit ed9772e

Please sign in to comment.