Перемещение элементов путем перетаскивания в 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';
});
}
У меня был тот же вопрос, и так как ответы выше не отвечали моим потребностям в:
- Элемент drag-gable сам по себе (без зоны сброса)
- многоразовый
Для решения на основе обертки этот пакет может быть ответом: 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 волшебным образом вызывает ваш инициализатор, если их имена соответствуют заданному шаблону. Полезно, когда у ваших классов довольно мало способностей.