Moving elements by dragging in Dart

烈酒焚心 提交于 2019-12-11 00:56:46

问题


I am trying to move an element using drag and drop. I want to be able to drag and element to a different location, and when I drop it, the element moves to the dropped location. Super basic, and nothing fancy. This is what I have so far:

html:

<input type='button' id='drag' class='draggable' value='drag me' draggable='true'>

Dart code:

Element drag = querySelector('.draggable');
drag.onDragEnd.listen((MouseEvent e) {
  drag.style.left = '${e.client.x}px';
  drag.style.top = '${e.client.y}px';
});

This doesn't quite do what I want it to do. The element is slightly off from where I drop it. I see examples in javascript with appendChild, clone(), parentNode, but none of the examples that I have seen can be reproduced in Dart. What is the best way to accomplish this? I don't want to use the DND package, since I am really trying to personally understand the concepts better.


回答1:


index.html

<!doctype html>
<html>
  <head>
    <style>
      #dropzone {
        position: absolute;
        top: 50px;
        left: 50px;
        width: 300px;
        height: 150px;
        border: solid 1px lightgreen;
      }
      #dropzone.droptarget {
        background-color: lime;
      }
    </style>
  </head>
  <body>
    <input type='button' id='drag' class='draggable' value='drag me'
           draggable='true'>

    <div id="dropzone"></div>
    <script type="application/dart" src="index.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html>

index.dart

library _template.web;

import 'dart:html' as dom;
import 'dart:convert' show JSON;

main() async {
  dom.Element drag = dom.querySelector('.draggable');
  drag.onDragStart.listen((event) {
    final startPos = (event.target as dom.Element).getBoundingClientRect();
    final data = JSON.encode({
      'id': (event.target as dom.Element).id,
      'x': event.client.x - startPos.left,
      'y': event.client.y - startPos.top
    });
    event.dataTransfer.setData('text', data);
  });

  dom.Element dropTarget = dom.querySelector('#dropzone');
  dropTarget.onDragOver.listen((event) {
    event.preventDefault();
    dropTarget.classes.add('droptarget');
  });

  dropTarget.onDragLeave.listen((event) {
    event.preventDefault();
    dropTarget.classes.remove('droptarget');
  });

  dropTarget.onDrop.listen((event) {
    event.preventDefault();
    final data = JSON.decode(event.dataTransfer.getData('text'));
    final drag = dom.document.getElementById(data['id']);
    event.target.append(drag);
    drag.style
      ..position = 'absolute'
      ..left = '${event.offset.x - data['x']}px'
      ..top = '${event.offset.y - data['y']}px';
    dropTarget.classes.remove('droptarget');
  });
}



回答2:


The answer above is correct, and I didn't want to edit it for that reason. However, I wanted to also offer another answer that I derived from the above. It is a lot more basic compared to the above, and so easier to follow the basic concepts for beginners. As mentioned below, I don't think you can move elements unless they are within a droppable area.

index.html:

<!DOCTYPE html>
<html>
<head>
  <style>
  #dropzone {
    position: absolute;
    top: 100px;
    left: 50px;
    width: 300px;
    height: 150px;
    border: solid 1px;
    color: lightgreen;
  }</style>
</head>
<body>
  <div  id="dropzone">
    <input type='button' id='drag' class='draggable' value='drag me'
     draggable='true'>
  </div>
  <script type="application/dart" src="main.dart"></script>
</body>
</html>

main.dart:

import 'dart:html';
main() {
  Element drag = querySelector('.draggable');
  Element drop = querySelector('#dropzone');
  drag.onDragStart.listen((MouseEvent e) {
    var startPos = (e.target as Element).getBoundingClientRect();
    String xPos = "${e.client.x - startPos.left}";
    String yPos = "${e.client.y - startPos.top}";
    e.dataTransfer.setData('x', xPos);
    e.dataTransfer.setData('y', yPos);
  });
  drop.onDragOver.listen((MouseEvent e) {
    e.preventDefault();
  });
  drop.onDrop.listen((MouseEvent e) {
    e.stopPropagation();
    String xPos = e.dataTransfer.getData('x');
    String yPos = e.dataTransfer.getData('y');
    int x = num.parse(xPos);
    int y = num.parse(yPos);
    drag.style.position = 'absolute';
    drag.style
      ..left = '${e.offset.x - x}px'
      ..top = '${e.offset.y - y}px';
  });
}



回答3:


I had the same question and since the answers above did not meet my needs in:

  1. Element drag-gable by itself(No drop zone)
  2. Reusable

For a wrapper based solution, this package could be the answer:https://pub.dartlang.org/packages/dnd

Custom element based approach(Currently cursor styling is not working):

  main(){
  document.registerElement('draggable-element',
    DraggableElement);
  querySelector('body').append(new DraggableElement()..text='draggable');
  }
  class DraggableElement extends HtmlElement with Draggability{
  DraggableElement.created():super.created(){
    learn_custom_draggability();
  }
  factory DraggableElement(){
    return new Element.tag('draggable-element');
  }
}
class Draggability{
  bool __custom_mouseDown = false;

  //The Coordinates of the mouse click
  //relative to the left top of the
  //element.
  Point<int> __custom_relative_mouse_position;
  void learn_custom_draggability(){
    if(this is! HtmlElement ){
      throw ("Draggability mixin "
          "is not compatible with"
          ' non-HtmlElement.');
    }
    var self = (this as HtmlElement);
    self.onMouseDown.listen(mouseDownEventHandler);
    self.onMouseUp.listen(mouseUpEventHandler);
    //styling
    self.style.position = 'absolute';

    window.onMouseMove
        .listen(mouseMoveEventHandler);
  }

  void mouseMoveEventHandler(MouseEvent e){
    if(!__custom_mouseDown) return;
    int xoffset = __custom_relative_mouse_position.x,
      yoffset = __custom_relative_mouse_position.y;

    var self = (this as HtmlElement);
    int x = e.client.x-xoffset,
        y = e.client.y-yoffset;
    print(x);
    if(y == 0) return;
    self.style
      ..top = y.toString() +'px'
      ..left = x.toString()+'px';
  }

  void mouseDownEventHandler(MouseEvent e){
    print('mouse down');
    __custom_mouseDown = true;
    var self = (this as HtmlElement);
    self.style.cursor = 'grabbing';

    __custom_relative_mouse_position =
      e.offset;
  }

  void mouseUpEventHandler(MouseEvent e){
    print('mouse up');
    __custom_mouseDown = false;
    var self = (this as HtmlElement);
    self.style.cursor = 'default';
  }
}

Edit:

Yay, Thank you Günter Zöchbauer for informing me about reflectable. It's so small and compiles fast.
A little off the topic but posting since mixins and the below pattern goes hand in hand.

import 'package:reflectable/reflectable.dart';
class Reflector extends Reflectable{
  const Reflector():
        super(
          instanceInvokeCapability,
          declarationsCapability
      );
}
const reflector = const Reflector();
@reflector
class CustomBase extends HtmlElement{
  CustomBase.created():super.created(){
    learn();
  }
  learn(){
    InstanceMirror selfMirror = reflector.reflect(this);
    var methods = selfMirror.type.instanceMembers;
    RegExp pattern = new RegExp('^learn_custom_.*bility\$');
    for(var k in methods.keys){
      if(pattern.firstMatch(k.toString()) != null){
        selfMirror.invoke(k,[]);
      }
    }
  }
}

Include: "reflectable: ^0.5.0" under dependencies and "- reflectable: entry_points: web/index.dart" etc under transformers in the pubspec.yaml and extend a custom class like the above instead of a HtmlElement and selfMirror.invoke magically calls your initializer as long as their names match the given pattern. Useful when your classes have a quite few abilities.



来源:https://stackoverflow.com/questions/33360151/moving-elements-by-dragging-in-dart

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!