Skip to content

Commit 1c9410d

Browse files
committed
* Fix Issue #4: Support any HTML Element as drag image
* Fix Issue #5: Always use Drag Image Polyfill for IE9 drags
1 parent d7b910f commit 1c9410d

File tree

3 files changed

+65
-39
lines changed

3 files changed

+65
-39
lines changed

lib/draggable.dart

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,13 @@ class DraggableGroup extends Group {
4444
};
4545

4646
/**
47-
* Function to create a [DragImage] for this draggable. Default function
48-
* returns null.
47+
* Function to create a [DragImage] for this draggable. If null is returned
48+
* from the function (the default), the browser's drag image is used instead.
4949
*/
5050
DragImageFunction dragImageFunction = (Element draggable) {
5151
return null;
5252
};
5353

54-
/**
55-
* If set to true, a custom drag image is drawn even if the browser supports
56-
* the setting a custom drag image. The polyfill is a bit slower but allows
57-
* opacity settings on [DragImage] to have an effect.
58-
*/
59-
bool alwaysUseDragImagePolyfill = false;
60-
6154
// -------------------
6255
// Draggable Events
6356
// -------------------
@@ -205,16 +198,38 @@ class DraggableGroup extends Group {
205198

206199
dragImage = dragImageFunction(element);
207200
if (dragImage != null) {
208-
if (alwaysUseDragImagePolyfill || !html5.supportsSetDragImage) {
201+
if (html5.supportsSetDragImage) {
202+
mouseEvent.dataTransfer.setDragImage(dragImage.element, dragImage.x,
203+
dragImage.y);
204+
} else {
209205
usingDragImagePolyfill = true;
210206
// Install the polyfill.
211207
polyfillDragOverSubscription = _polyfillSetDragImage(element,
212208
mouseEvent, dragImage);
213-
209+
}
210+
} else if (!html5.supportsDraggable) {
211+
// Browser does not create a drag image by default --> polyfill it.
212+
_logger.finest('Manually creating drag image from current drag element.');
213+
usingDragImagePolyfill = true;
214+
215+
// Install the polyfill with a clone of the current drag element as drag image.
216+
if (element is ImageElement) {
217+
// Calc the mouse position relative to the draggable.
218+
num mouseRelativeLeft = mouseEvent.page.x - css.getLeftOffset(element);
219+
num mouseRelativeTop = mouseEvent.page.y - css.getTopOffset(element);
220+
dragImage = new DragImage(new ImageElement(src: element.src,
221+
width: element.width, height: element.height),
222+
mouseRelativeLeft.round(), mouseRelativeTop.round());
214223
} else {
215-
mouseEvent.dataTransfer.setDragImage(dragImage.image, dragImage.x,
216-
dragImage.y);
224+
// Not an image --> Make sure mouse is outside of drag image.
225+
Element clone = element.clone(true);
226+
clone.attributes.remove('id');
227+
clone.style.width = element.getComputedStyle().width;
228+
clone.style.height = element.getComputedStyle().height;
229+
dragImage = new DragImage(clone, 0, -5);
217230
}
231+
polyfillDragOverSubscription = _polyfillSetDragImage(element,
232+
mouseEvent, dragImage);
218233
}
219234

220235
if (_onDragStart != null) {
@@ -305,10 +320,11 @@ class DraggableGroup extends Group {
305320
*/
306321
StreamSubscription _polyfillSetDragImage(Element element, MouseEvent mouseEvent,
307322
DragImage dragImage) {
323+
_logger.finest('Polyfilling setDragImage function.');
308324
_preventDefaultDragImage(element, mouseEvent);
309325

310326
// Manually add the drag image polyfill with absolute position.
311-
document.body.children.add(dragImage.polyfill);
327+
element.parent.children.add(dragImage.polyfill);
312328
dragImage.polyfill.style.position = 'absolute';
313329
dragImage.polyfill.style.visibility = 'hidden';
314330

@@ -332,10 +348,10 @@ class DraggableGroup extends Group {
332348
// Set drag image to
333349
mouseEvent.dataTransfer.setDragImage(
334350
new ImageElement(src: DragImage.EMPTY), 0, 0);
335-
} else {
351+
} else if (html5.supportsDraggable) {
336352
// To force the browser not to display the default drag image, which is the
337353
// html element beeing dragged, we must set display to 'none'. Visibility
338-
// 'hidden' won't work (IE drags a white box). To still keep the space
354+
// 'hidden' won't work (IE10 drags a white box). To still keep the space
339355
// of the display='none' element, we create a clone as a temporary
340356
// replacement.
341357
Element tempReplacement = element.clone(true);
@@ -368,14 +384,21 @@ class DraggableEvent {
368384
}
369385

370386
/**
371-
* A drag feedback [image] element. The [x] and [y] define where the image
372-
* should appear relative to the mouse cursor.
387+
* A drag feedback image. The [x] and [y] define where the drag image should
388+
* appear relative to the mouse cursor.
389+
*
390+
* The drag image [element] can be an HTML img element, an HTML canvas element
391+
* or any visible HTML node on the page.
392+
*
393+
* **Important:** In IE9 and IE10, mouse events can only be passed through
394+
* [ImageElement]s. If another HTML element is provided, the mouse must be
395+
* positioned outside of the drag image with [x] and [y].
373396
*/
374397
class DragImage {
375398
/// A small transparent gif.
376399
static const String EMPTY = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
377400

378-
final ImageElement image;
401+
final Element element;
379402
final int x;
380403
final int y;
381404

@@ -384,7 +407,7 @@ class DragImage {
384407

385408
Element _polyfill;
386409

387-
DragImage(this.image, this.x, this.y);
410+
DragImage(this.element, this.x, this.y);
388411

389412
/**
390413
* Returns the element that is used for the polyfill drag image.
@@ -399,37 +422,40 @@ class DragImage {
399422
if (_polyfill == null) {
400423
// Make sure that mouse events are forwarded to the layer below.
401424
if (html5.supportsPointerEvents) {
402-
_polyfill = image;
403-
} else {
425+
_polyfill = element;
426+
_polyfill.style.pointerEvents = 'none';
427+
} else if (element is ImageElement) {
404428
// IE9 and IE10 support pointer-events on SVGs only.
405-
_polyfill = _createSvgElement();
429+
_polyfill = _createSvgFromImage(element as ImageElement);
430+
_polyfill.style.pointerEvents = 'none';
431+
} else {
432+
// No support for pointer-events.
433+
_logger.finest('pointer-events not supported: mouse must be outside of drag image.');
434+
_polyfill = element;
406435
}
407-
_polyfill.style.pointerEvents = 'none';
408436

409437
// Add some transparency.
410438
_polyfill.style.opacity = polyfillOpacity;
411439
}
412440
return _polyfill;
413441
}
414442

415-
416443
/**
417444
* Creates an SVG tag containing the [image].
418445
*/
419-
Element _createSvgElement() {
446+
Element _createSvgFromImage(ImageElement image) {
420447
return new svg.SvgElement.svg("""
421448
<svg xmlns="http://www.w3.org/2000/svg"
422449
xmlns:xlink="http://www.w3.org/1999/xlink"
423-
424450
width="${image.width}"
425451
height="${image.height}">
426452
<image xlink:href="${image.src}"
427-
x="0"
428-
y="0"
429-
width="${image.width}"
430-
height="${image.height}"
431-
/>
453+
x="0"
454+
y="0"
455+
width="${image.width}"
456+
height="${image.height}"
457+
/>
432458
</svg>
433459
""");
434-
}
460+
}
435461
}

lib/sortable.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,12 +411,12 @@ class _Placeholder {
411411
newPosition.insert(placeholderElement);
412412

413413
// Make sure the draggable element is removed.
414-
currentDraggable.remove();
414+
draggable.remove();
415415

416416
// When placeholder is shown, we clear the drag over elements so
417417
// we have a fresh start of the counter whenever any other element is entered.
418418
_logger.finest('clearing dragOverElements {dragOverElements.length before clearing: ${currentDragOverElements.length}}');
419-
currentDragOverElements.clear();
419+
Timer.run(() => currentDragOverElements.clear());
420420
}
421421

422422
/**

lib/src/css_utils.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,17 @@ num getTopOffset(Element element) {
1919
- document.documentElement.client.top;
2020
}
2121

22-
num getAbsoluteLeft(Element element) {
23-
num absoluteLeft = element.offsetLeft;
22+
int getAbsoluteLeft(Element element) {
23+
int absoluteLeft = element.offsetLeft;
2424
while (element.offsetParent != null) {
2525
element = element.offsetParent;
2626
absoluteLeft += element.offsetLeft - element.scrollLeft;
2727
}
2828
return absoluteLeft;
2929
}
3030

31-
num getAbsoluteTop(Element element) {
32-
num absoluteTop = element.offsetTop;
31+
int getAbsoluteTop(Element element) {
32+
int absoluteTop = element.offsetTop;
3333
while (element.offsetParent != null) {
3434
element = element.offsetParent;
3535
absoluteTop += element.offsetTop - element.scrollTop;

0 commit comments

Comments
 (0)