Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Touch backend #16

Closed
KyleAMathews opened this issue Oct 27, 2014 · 25 comments
Closed

Touch backend #16

KyleAMathews opened this issue Oct 27, 2014 · 25 comments
Milestone

Comments

@KyleAMathews
Copy link

It'd be killer if there was a touch backend that would be switched-in automatically for touch devices.

@gaearon
Copy link
Member

gaearon commented Oct 28, 2014

Indeed! I'll look into this next week.

@nelix
Copy link
Collaborator

nelix commented Oct 28, 2014

I did something like this using pointer-events: none; so the elements under the item being dragged could catch mouse over events; i'm interested in seeing what other ways there are to tackle this

@gaearon
Copy link
Member

gaearon commented Oct 28, 2014

What are state-of-the-art drag and drop libraries using touchmove? jQuery UI? We need to look at a good solution and adopt its tricks.

@KyleAMathews
Copy link
Author

I haven't used any of these on projects but Googling found:

@gaearon
Copy link
Member

gaearon commented Oct 28, 2014

I think it would be best and in the spirit of this library to avoid any DOM manipulation (like popular libs do, whether by cloning node for preview, changing its translateX/Y, etc), and instead let the consumer draw "drag layer" and give consumer necessary data for this.

In other words if Card component is dragged with touch backend, react-dnd won't try to somehow move it itself. It's up to you to render some "layer" component at the top of your app. This component will use DragLayerMixin provided by react-dnd to learn about current element, its position, etc. It decides how to render the dragged item based by its type and is 100% React as well.

var App = React.createClass({
  render() {
    return (
      <div>
       {this.props.activeRouteHandler />
       <DragDropLayer />
      </div>
    );
  }
});

var { DragLayerMixin } = require('react-dnd');

var DragDropLayer = React.createClass({
  mixins: [DragLayerMixin],

  render() {

    // With non-native dragging backend, rendering items in the process of dragging is your responsibility. 
    // It's up to you to use translate3d or top/left, render thumbnails or real components, etc.

    var {
      draggedItem,
      draggedItemType,
      clientX,
      clientY
    } = this.getDragLayerState(); // provided by mixin

    if (!draggedItem) {
      return null;
    }

    return (
      <div style={{zIndex: 10000, pointerEvents: 'none', position: 'absolute'}}>
        {draggedItem &&
          <div style={{transform: `translate3d(${clientX}, ${clientY}, 0)`}}>
            {this.renderItem(draggedItemType, draggedItem)}
          </div>
        }
      </div>
    );
  },

  renderItem(type, item) {

    // It's completely up to the consumer to specify how to render different types

    switch (type) {
    case ItemTypes.Card:
      return <Card title={item.title} />;
    case ItemTypes.List:
      return <img src='thumbnail.jpg' />;
  }
});

@gaearon
Copy link
Member

gaearon commented Oct 28, 2014

BTW this approach opens up really cool possibilities because for some apps drag layer is what contains draggable items. Drag layer doesn't have to be separate, although it can, if neccessary.

For apps where items are dragged upon some container, this container can use DragLayerMixin and thus learn about current drag position and move real elements as drag occurs:

screen shot 2014-10-28 at 20 30 15

For apps such as Trello where items are dragged across the screen across different containers, it can be useful to have a separate top-level component using DragLayerMixin to render absolutely positioned currently dragged item.

@KyleAMathews
Copy link
Author

This makes a lot of sense. Almost any browser, whether desktop or mobile, should be able to smoothly animate an item around the screen. This would make it easy then to add cool 3d effects like Trello does when moving a card.

@nelix
Copy link
Collaborator

nelix commented Oct 28, 2014

This would also allow setting the mouse cursor while dragging and more flexible placeholder rendering... Things you can't really do with the native DND api

@gaearon
Copy link
Member

gaearon commented Oct 30, 2014

I just realized we can also make this "drag layer" API available for browser D&D API. Just set dragImage to a transparent pixel like in this example and draw whatever you want.

@gaearon
Copy link
Member

gaearon commented Nov 20, 2014

@nelix @KyleAMathews

I have a proof of concept here: http://gaearon.github.io/react-dnd/#/drag-around-experimental

It will blink while you move it :-) (won't work in Firefox yet)

Relevant source code:

https://github.com/gaearon/react-dnd/blob/experiments/examples/_drag-around-experimental/index.js#L14

https://github.com/gaearon/react-dnd/blob/experiments/examples/_drag-around-experimental/DragLayer.js

https://github.com/gaearon/react-dnd/blob/experiments/examples/_drag-around-experimental/Box.js#L69

As you can see, it's up to the consumer to draw drag layer and components on it, so it's really flexible.

I'll try to finish this up this week as a stepping stone to touch support, and after that it shouldn't be difficult to do the same for touch mode.

@nelix
Copy link
Collaborator

nelix commented Nov 20, 2014

We use a drag layer much like this, but we use onMouseMove etc so that you can change the mouse cursor...
A minor but very important concern for us. It also allows use to use mostly the same code for touch and pointer events.
Still the DragLayer stuff is a step in the right direction for me, once you do touch support I'd be willing to try and extend that to support pointer and mouse events if you are interested in having those as features?

@gaearon
Copy link
Member

gaearon commented Nov 20, 2014

I'd like to have HTML5 D&D API as the default backend and then make touch-based/mousemove-based backends opt-in.

HTML5 D&D would be the simplest one (no need for drag layer, everything managed by browser). Opting in to mousemove/touchmove ones would require you to implement a drag layer to draw whatever you like.

Makes sense?

@nelix
Copy link
Collaborator

nelix commented Nov 20, 2014

Sure does, thats what I hoped would happen.
HTML5 D&D is fast and (besides the quirks) pretty simple to use so its the obvious choice by default.

@Georgette
Copy link

So I just had a request to make this drag and drop touch enabled. Has anyone done this and want to share tips?

@gaearon
Copy link
Member

gaearon commented Feb 4, 2015

@Georgette

@nelix started work on mousemove backend, we'll do touchmove backend after that.

You can look at work in progress here: nelix@8de7f7f (it's still far from being done and doesn't really work at this point). We still need to figure out which parts stay in DragDropMixin and which need to be moved into backends/HTML5 (and other backends).

If you'd like to work on that, you can start with that commit and keep implementing mousemove backend. When it works, it shouldn't be hard to implement a similar touchmove backend.

@gaearon
Copy link
Member

gaearon commented Feb 4, 2015

Please, if you'd like to work on this, keep us updated here or in Gitter room, so we don't end up with two implementations.

@gaearon gaearon mentioned this issue Feb 6, 2015
14 tasks
@gaearon gaearon modified the milestone: v1.0 Feb 8, 2015
@gaearon gaearon modified the milestones: v1.x, v1.0, v1.1 Apr 20, 2015
@gaearon
Copy link
Member

gaearon commented Apr 21, 2015

Here's a proof of concept of touch support: #143

You can play with it: http://rawgit.com/gaearon/react-dnd/touch-poc/examples/index.html

It uses HTML5 backend on desktop and touch backend on mobile.
Only custom rendering example will show a drag preview on mobile.

@jareware
Copy link

This seems quite promising! Any ideas on when this might land? :)

@gaearon
Copy link
Member

gaearon commented Jun 14, 2015

I'm currently busy with another project and will be until July.
I might be able to work on this somewhere in July or August, but I'll be happy if somebody beats me to it :-)

@flyon
Copy link

flyon commented Jul 15, 2015

since 1.1 was mentioned in this thread and v1.1.4 is out. I'm just wondering if the proof of concept above is already included in 1.1.4?

@gaearon
Copy link
Member

gaearon commented Jul 15, 2015

Nope.

I shouldn't have used a precise version number cause you never know when you're going to bump :-)

I currently don't have time to work on this but the proof of concept is in the PR. I'll probably update the PR to reflect the current API and wait for somebody to actually implement it.

@longlho longlho mentioned this issue Jul 20, 2015
@gaearon
Copy link
Member

gaearon commented Sep 13, 2015

Let's continue the discussion in #240.

@gaearon gaearon closed this as completed Sep 13, 2015
@gaearon
Copy link
Member

gaearon commented Oct 16, 2015

FYI: https://github.com/yahoo/react-dnd-touch-backend.

@haf
Copy link

haf commented Aug 20, 2017

For those who come after me, here seems to be the path forward https://github.com/LouisBrunner/react-dnd-multi-backend

@WearyMonkey
Copy link
Contributor

I wrote this snippet:

function multiBackends(...backendFactories) {
  return function(manager) {
    const backends = backendFactories.map(b => b(manager));
    return {
      setup: (...args) =>
          backends.forEach(b => b.setup.apply(b, args)),
      teardown: (...args) =>
          backends.forEach(b => b.teardown.apply(b, args)),
      connectDropTarget: (...args) =>
          backends.forEach(b => b.connectDropTarget.apply(b, args)),
      connectDragPreview: (...args) =>
          backends.forEach(b => b.connectDragPreview.apply(b, args)),
      connectDragSource: (...args) =>
          backends.forEach(b => b.connectDragSource.apply(b, args)),
    };
  };
}

Which I use like this:

DragDropContext(multiBackends(
      ReactDnDHTML5Backend,
      ReactDnDTouchBackend,
  ))

react-dnd-html5-backend and react-dnd-touch-backend listen to a disjoint set of events. (dragstart, dragend, dragenter, dragleave dragover, and drop vs touchstart, touchend and touchmove).

Some basic testing and it works fine, my drag and drop now supports touch and mouse events at the same time.

Are there any reasons it wouldn't work?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants