Перемещение элементов путем перетаскивания в Dart

Я пытаюсь переместить элемент с помощью перетаскивания. Я хочу иметь возможность перетаскивать и элемент в другое место, и когда я удаляю его, элемент перемещается в выбранное место. Супер простой, и ничего особенного. Это то, что я до сих пор:

HTML:

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

Код дротика:

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

Это не совсем то, что я хочу. Элемент немного не в том месте, откуда я его бросаю. Я вижу примеры в javascript с appendChild, clone(), parentNode, но ни один из примеров, которые я видел, не может быть воспроизведен в Dart. Каков наилучший способ сделать это? Я не хочу использовать пакет DND, так как я действительно стараюсь лучше понять концепции.

3 ответа

Решение

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');
  });
}

Ответ выше верен, и я не хотел редактировать его по этой причине. Однако я хотел также предложить другой ответ, который я получил из вышеизложенного. Это намного более простой по сравнению с вышеупомянутым, и поэтому легче следовать основным понятиям для начинающих. Как упомянуто ниже, я не думаю, что вы можете перемещать элементы, если они не находятся в зоне падения.

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';
  });
}

У меня был тот же вопрос, и так как ответы выше не отвечали моим потребностям в:

  1. Элемент drag-gable сам по себе (без зоны сброса)
  2. многоразовый

Для решения на основе обертки этот пакет может быть ответом: https://pub.dartlang.org/packages/dnd

Подход на основе пользовательских элементов (в настоящее время стилизация курсора не работает):

  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';
  }
}

Редактировать:

Да, спасибо, Гюнтер Цохбауэр, за то, что сообщил мне о рефлексии. Он такой маленький и быстро компилируется.
Немного не по теме, но постинг, так как миксин и шаблон ниже идут рука об руку.

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,[]);
      }
    }
  }
}

Включите: отражаемый: ^0.5.0"в зависимости и"- отражаемый: entry_points: web/index.dart"и т. Д. В преобразователи в pubspec.yaml и расширьте пользовательский класс, подобный приведенному выше, вместо HtmlElement и selfMirror.invoke волшебным образом вызывает ваш инициализатор, если их имена соответствуют заданному шаблону. Полезно, когда у ваших классов довольно мало способностей.

Другие вопросы по тегам