Monday, July 4, 2016

OpenLayers Touch Screen DragBox

I had a frustrating time with this one!

Here's the problem:
I needed a way to multi-select features on touch screen / mobile devices via drag select. On the desktop, this is easily achievable using the built-in DragBox interaction and following the Box Selection example on the OL website.

I search the Google for a long time for a solution. Most just eluded to this line in the DragBox documentation: "This interaction is only supported for mouse devices." and offered no alternatives.

I looked into the OL code for this and found that, yes, there are explicit checks in the code to only work with mouse. Particularly, these 2 lines:

I didn't see why this was a restriction, and I still don't! If you do, please enlighten me! Touch inputs are perfectly capable of doing dragging stuff, I mean, that's how panning works, right?

I got the crazy idea of creating my own interaction to do multi-select with both mouse and touch inputs. I studied OL's DragBox and Pointer classes. Then realized, why not first try to do copy and paste programming and just take out the restriction bits and see if that works. I created TouchDragBox interaction that does exactly that and it seems to work fine so far!

You can see the class on our GitHub project.

Of course, the interaction itself can't just be activated all the time, because we won't be able to do panning! When adding this interaction to the map, I created a custom condition to either detect "CTRL" on desktop or if the user clicks on a button that toggles the touch drag box interaction mode on mobile platforms. Here's the code for that.

This approach could be bad if OL decided to change how interaction works, but really that's a normal process of using an API. In the TouchDragBox implementation, everything used was public API, as far as I can tell. So we should be good for awhile.

Now, there were plenty of other ideas and attempts to resolve this problem at first. I tried to use the Draw interaction to do multi-select, which was kindda quirky, because most folks are used to dragging and not click 2 points to create a rectangle for selection. Furthermore, on the mobile, you don't even see the box that was drawn for the selection. But, ultimately, the thing that killed this idea was how this tool was to be activated. It got weird to a point where I was implementing my own Select interaction. It all went down hill quite quickly. I left some remnants of this attempt in the code, commented out. See the link above if you're interested...

4 comments:

  1. For future reference, there is an easier way:

    var originalHandleEvent = dragBox.handleEvent.bind(this.dragBox);
    dragBox.handleEvent = function(e) {
    if (e.pointerEvent && e.pointerEvent.pointerType === 'touch') {
    e.pointerEvent.pointerType = 'mouse';
    }
    return handleEvent(e);
    };

    ReplyDelete
    Replies
    1. Correction, last return handleEvent(e); should be return originalHandleEvent(e);

      Delete
  2. This seemed to work for me:

    var _handleEvent = ol.interaction.DragBox.handleEvent;
    dragBox.handleEvent = function(e) {
    if (e.pointerEvent && e.pointerEvent.pointerType === 'touch') {
    e.pointerEvent.pointerType = 'mouse';
    }
    return _handleEvent.apply(this, arguments);
    };

    ReplyDelete