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

DragPreview is positioned wrong when using translate3d on child nodes #267

Closed
delijah opened this issue Aug 19, 2015 · 5 comments
Closed

Comments

@delijah
Copy link

delijah commented Aug 19, 2015

Hey!

We try to make an element draggable, which contains a translate3d css style at some point. Unfortunately this repositions the whole drag-layer. I do not really understand why, since the translate3d is not on the root element, but on a child node of it.

I assembled a very simple example, showing the problem to you. (To make it work, you need to place the ReactDnD.min.js in the "./scripts/react-dnd-1.1.4/dist" folder, since i did not find any CDN that hosts ReactDnD).

Thanks for help!

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>DragPreview with wrong position example</title>

        <script src="http://fb.me/react-with-addons-0.13.3.js"></script>
        <script src="http://fb.me/JSXTransformer-0.13.3.js"></script>
        <script src="scripts/react-dnd-1.1.4/dist/ReactDnD.min.js"></script>

        <style>
            .ReactDnDElement {
                background: red;
                overflow: hidden;
                width: 200px;
                height: 100px;
            }

            .ReactDnDElement .wrapper {
                display: flex;
                align-items: center;
                width: 600px;
                height: 100%;
                transform: translate3d(-275px, 0, 0);
            }

            .ReactDnDElement .wrapper div {
                width: 150px;
                height: 50%;
            }

            .ReactDnDElement .wrapper div.green { background: green; }
            .ReactDnDElement .wrapper div.blue { background: blue; }
            .ReactDnDElement .wrapper div.yellow { background: yellow; }
            .ReactDnDElement .wrapper div.magenta { background: magenta; }
        </style>
    </head>

    <body>
        <script type="text/jsx">
            /* ReactDnDApp */

            var ReactDnDApp = React.createClass({
                render: function() {
                    return (
                        <ReactDnDElement></ReactDnDElement>
                    );
                }
            });

            ReactDnDApp = ReactDnD.DragDropContext(ReactDnD.HTML5)(ReactDnDApp)


            /* ReactDnDElement */

            var ReactDnDElement = React.createClass({
                render: function() {
                    return this.props.connectDragSource(
                        this.props.connectDragPreview(
                            <div className="ReactDnDElement">
                                <div className="wrapper">
                                    <div className="green"></div>
                                    <div className="blue"></div>
                                    <div className="yellow"></div>
                                    <div className="magenta"></div>
                                </div>
                            </div>
                        )
                    );
                }
            });

            ReactDnDElement = ReactDnD.DragSource("dndElement", {
                beginDrag: function(props) {
                    return {};
                }
            }, function(connect, monitor) {
                return {
                    connectDragSource: connect.dragSource(),
                    connectDragPreview: connect.dragPreview()
                };
            })(ReactDnDElement);


            /* RUN IT! */

            React.render(React.createElement(ReactDnDApp), document.body);
        </script>
    </body>
</html>
@delijah
Copy link
Author

delijah commented Aug 26, 2015

Looks like there is an issue with the native html5 drag and drop functionality as well. If i try the same with the native html5 drag and drop methods, it works correctly for firefox and safari, but not for chrome and opera.

With react-dnd this problem does not exist in safari, but for chrome, opera AND FIREFOX it does.

I have opened an issue for chromium: https://code.google.com/p/chromium/issues/detail?id=525604

@delijah
Copy link
Author

delijah commented Sep 8, 2015

Any intentions to fix this bug?

@gaearon
Copy link
Member

gaearon commented Sep 17, 2015

Nasty stuff. I just spent two hours on this and I have no idea how to make it work.

This seems like one of those terrible browser inconsistencies. The same x and y values passed to dataTransfer.setDragImage are interpreted by browsers differently when any child of the element you interacted with is positioned with a negative offset to its parent. Notice how changing the transform to a positive one magically makes it work.

The reason you don't see this problem on some vanilla HTML5 drag and drop examples in Firefox is likely because they don't specify x and y, and default values turn out to “just work”. However we need to specify them for the dragged item appearing in the right spot in most cases.

I doubt there's any feasible workaround here. When any of the deeply nested children of the drag source node are out of its bounds to the left or the top edge, setDragImage(node, x, y) stops working correctly in some browsers. Again, even if we tried to fix it for some browsers, we'd surely break this in other browsers, so I don't think we can reliably fix this.

I'm closing, but feel free to look for workarounds! For now, I'd suggest avoiding negative transforms inside drag sources. Note that you can move connectDragSource inside the transformed wrapper:

.ReactDnDElement {
    background: red;
    overflow: hidden;
    width: 200px;
    height: 100px;
}

.ReactDnDElement .transform {
    transform: translate3d(-275px, 0, 0);
    width: 100%;
    height: 100%;
}

.ReactDnDElement .wrapper {
    display: flex;
    align-items: center;
    width: 600px;
    height: 100%;
}

.ReactDnDElement .wrapper div {
    width: 150px;
    height: 50%;
}

.ReactDnDElement .wrapper div.green { background: green; }
.ReactDnDElement .wrapper div.blue { background: blue; }
.ReactDnDElement .wrapper div.yellow { background: yellow; }
.ReactDnDElement .wrapper div.magenta { background: magenta; }
var ReactDnDElement = React.createClass({
    render: function() {
      return (
        <div className="ReactDnDElement">
          <div className="transform">
            {this.props.connectDragSource(
              <div className="wrapper">
                <div className="green"></div>
                <div className="blue"></div>
                <div className="yellow"></div>
                <div className="magenta"></div>
              </div>
            )}
          </div>
        </div>
      );
    }
});

However this way only the child part will be used as the drag image—not sure if you want this.

screen shot 2015-09-17 at 17 48 31

Unfortunately this is the best I can suggest. If this is a big issue for you, please consider using an alternative React DnD backend instead of HTML5: for example, a mouse backend like in #70, which wouldn't have this problem because it wouldn't rely on setDragImage. Another option is to keep using the HTML5 backend, but use a custom drag layer which doesn't rely on setDragImage.

@delijah
Copy link
Author

delijah commented Sep 23, 2015

Hey! Thanks for having a look at this. Yes nasty stuff indeed! Well i hope the browser guys fix this issue soon..

@gaearon
Copy link
Member

gaearon commented Sep 23, 2015

Well i hope the browser guys fix this issue soon..

Not likely. HTML5 drag and drop is standardized on top of IE5 behavior, and has been broken and inconsistent for ages. As web gets more complex, I suspect it will only get worse, not better. Custom mouse and touch handling is a better solution and I look forward to such React DnD backends being published.

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

2 participants