fix(drag-drop): preserve previous inline transform (#13529)

This commit is contained in:
Kristiyan Kostadinov
2018-10-19 17:11:51 +02:00
committed by Joey Perrott
parent 280b0d6878
commit cae16b0f85
2 changed files with 54 additions and 19 deletions
+24
View File
@@ -144,6 +144,30 @@ describe('CdkDrag', () => {
expect(dragElement.style.transform).toBeFalsy();
}));
it('should preserve the previous `transform` value', fakeAsync(() => {
const fixture = createComponent(StandaloneDraggable);
fixture.detectChanges();
const dragElement = fixture.componentInstance.dragElement.nativeElement;
dragElement.style.transform = 'translateX(-50%)';
dragElementViaMouse(fixture, dragElement, 50, 100);
expect(dragElement.style.transform).toBe('translateX(-50%) translate3d(50px, 100px, 0px)');
}));
it('should not generate multiple own `translate3d` values', fakeAsync(() => {
const fixture = createComponent(StandaloneDraggable);
fixture.detectChanges();
const dragElement = fixture.componentInstance.dragElement.nativeElement;
dragElement.style.transform = 'translateY(-50%)';
dragElementViaMouse(fixture, dragElement, 50, 100);
expect(dragElement.style.transform).toBe('translateY(-50%) translate3d(50px, 100px, 0px)');
dragElementViaMouse(fixture, dragElement, 100, 200);
expect(dragElement.style.transform).toBe('translateY(-50%) translate3d(150px, 300px, 0px)');
}));
});
describe('touch dragging', () => {
+30 -19
View File
@@ -129,6 +129,9 @@ export class CdkDrag<T = any> implements AfterViewInit, OnDestroy {
/** CSS `transform` that is applied to the element while it's being dragged. */
private _activeTransform: Point = {x: 0, y: 0};
/** Inline `transform` value that the element had before the first dragging sequence. */
private _initialTransform?: string;
/**
* Whether the dragging sequence has been started. Doesn't
* necessarily mean that the element has been moved.
@@ -325,6 +328,12 @@ export class CdkDrag<T = any> implements AfterViewInit, OnDestroy {
return;
}
// Cache the previous transform amount only after the first drag sequence, because
// we don't want our own transforms to stack on top of each other.
if (this._initialTransform == null) {
this._initialTransform = this._rootElement.style.transform || '';
}
this._hasStartedDragging = this._hasMoved = false;
this._initialContainer = this.dropContainer;
this._pointerMoveSubscription = this._dragDropRegistry.pointerMove.subscribe(this._pointerMove);
@@ -373,13 +382,12 @@ export class CdkDrag<T = any> implements AfterViewInit, OnDestroy {
if (!this._hasStartedDragging) {
const distanceX = Math.abs(pointerPosition.x - this._pickupPositionOnPage.x);
const distanceY = Math.abs(pointerPosition.y - this._pickupPositionOnPage.y);
const minimumDistance = this._config.dragStartThreshold;
// Only start dragging after the user has moved more than the minimum distance in either
// direction. Note that this is preferrable over doing something like `skip(minimumDistance)`
// in the `pointerMove` subscription, because we're not guaranteed to have one move event
// per pixel of movement (e.g. if the user moves their pointer quickly).
if (distanceX + distanceY >= minimumDistance) {
if (distanceX + distanceY >= this._config.dragStartThreshold) {
this._hasStartedDragging = true;
this._ngZone.run(() => this._startDragSequence());
}
@@ -399,7 +407,11 @@ export class CdkDrag<T = any> implements AfterViewInit, OnDestroy {
pointerPosition.x - this._pickupPositionOnPage.x + this._passiveTransform.x;
activeTransform.y =
pointerPosition.y - this._pickupPositionOnPage.y + this._passiveTransform.y;
this._setTransform(this._rootElement, activeTransform.x, activeTransform.y);
const transform = getTransform(activeTransform.x, activeTransform.y);
// Preserve the previous `transform` value, if there was one.
this._rootElement.style.transform = this._initialTransform ?
this._initialTransform + ' ' + transform : transform;
}
// Since this event gets fired for every pixel while dragging, we only
@@ -507,9 +519,8 @@ export class CdkDrag<T = any> implements AfterViewInit, OnDestroy {
}
this.dropContainer._sortItem(this, x, y, this._pointerDirectionDelta);
this._setTransform(this._preview,
x - this._pickupPositionInElement.x,
y - this._pickupPositionInElement.y);
this._preview.style.transform =
getTransform(x - this._pickupPositionInElement.x, y - this._pickupPositionInElement.y);
}
/**
@@ -525,7 +536,8 @@ export class CdkDrag<T = any> implements AfterViewInit, OnDestroy {
preview = viewRef.rootNodes[0];
this._previewRef = viewRef;
this._setTransform(preview, this._pickupPositionOnPage.x, this._pickupPositionOnPage.y);
preview.style.transform =
getTransform(this._pickupPositionOnPage.x, this._pickupPositionOnPage.y);
} else {
const element = this._rootElement;
const elementRect = element.getBoundingClientRect();
@@ -533,7 +545,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnDestroy {
preview = element.cloneNode(true) as HTMLElement;
preview.style.width = `${elementRect.width}px`;
preview.style.height = `${elementRect.height}px`;
this._setTransform(preview, elementRect.left, elementRect.top);
preview.style.transform = getTransform(elementRect.left, elementRect.top);
}
extendStyles(preview.style, {
@@ -603,7 +615,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnDestroy {
this._preview.classList.add('cdk-drag-animating');
// Move the preview to the placeholder position.
this._setTransform(this._preview, placeholderRect.left, placeholderRect.top);
this._preview.style.transform = getTransform(placeholderRect.left, placeholderRect.top);
// If the element doesn't have a `transition`, the `transitionend` event won't fire. Since
// we need to trigger a style recalculation in order for the `cdk-drag-animating` class to
@@ -634,16 +646,6 @@ export class CdkDrag<T = any> implements AfterViewInit, OnDestroy {
});
}
/**
* Sets the `transform` style on an element.
* @param element Element on which to set the transform.
* @param x Desired position of the element along the X axis.
* @param y Desired position of the element along the Y axis.
*/
private _setTransform(element: HTMLElement, x: number, y: number) {
element.style.transform = `translate3d(${x}px, ${y}px, 0)`;
}
/**
* Helper to remove an element from the DOM and to do all the necessary null checks.
* @param element Element to be removed.
@@ -768,3 +770,12 @@ interface Point {
x: number;
y: number;
}
/**
* Gets a 3d `transform` that can be applied to an element.
* @param x Desired position of the element along the X axis.
* @param y Desired position of the element along the Y axis.
*/
function getTransform(x: number, y: number): string {
return `translate3d(${x}px, ${y}px, 0)`;
}