Skip to content

Commit

Permalink
fix(cdk/drag-drop): pointer position calculation for SVG with viewBox (
Browse files Browse the repository at this point in the history
…#19863)

* fix(cdk/drag-drop): pointer position calculation for SVG with viewBox

Fixes a bug in the Angular Material `drag-drop` component where the pointer position is incorrectly
calculated for SVG with viewBox. This is because mouse coordinates retrieved using the screen
coordinate system, but coordinates must be in SVG space, which is defined by the viewBox attribute.

* save ownerSVGElement in a property
  • Loading branch information
NiyazNz authored and mmalerba committed Jul 8, 2020
1 parent 3db41f6 commit 190500a
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 5 deletions.
24 changes: 24 additions & 0 deletions src/cdk/drag-drop/directives/drag.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ describe('CdkDrag', () => {
expect(dragElement.getAttribute('transform')).toBe('translate(50 100)');
}));

it('should drag an SVG element freely to a particular position in SVG viewBox coordinates',
fakeAsync(() => {
const fixture = createComponent(StandaloneDraggableSvgWithViewBox);
fixture.detectChanges();
const dragElement = fixture.componentInstance.dragElement.nativeElement;

expect(dragElement.getAttribute('transform')).toBeFalsy();
dragElementViaMouse(fixture, dragElement, 50, 100);
expect(dragElement.getAttribute('transform')).toBe('translate(100 200)');
}));

it('should drag an element freely to a particular position when the page is scrolled',
fakeAsync(() => {
const fixture = createComponent(StandaloneDraggable);
Expand Down Expand Up @@ -5330,6 +5341,19 @@ class StandaloneDraggableSvg {
@ViewChild('dragElement') dragElement: ElementRef<SVGElement>;
}

@Component({
template: `
<svg width="400px" height="400px" viewBox="0 0 800 800"><g
cdkDrag
#dragElement>
<circle fill="red" r="50" cx="50" cy="50"/>
</g></svg>
`
})
class StandaloneDraggableSvgWithViewBox {
@ViewChild('dragElement') dragElement: ElementRef<SVGElement>;
}

@Component({
template: `
<div #dragElement cdkDrag
Expand Down
31 changes: 26 additions & 5 deletions src/cdk/drag-drop/drag-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ export class DragRef<T = any> {
*/
private _rootElement: HTMLElement;

/**
* Nearest ancestor SVG, relative to which coordinates are calculated if dragging SVGElement
*/
private _ownerSVGElement: SVGSVGElement | null;

/**
* Inline style value of `-webkit-tap-highlight-color` at the time the
* dragging was started. Used to restore the value once we're done dragging.
Expand Down Expand Up @@ -380,6 +385,10 @@ export class DragRef<T = any> {
this._rootElement = element;
}

if (typeof SVGElement !== 'undefined' && this._rootElement instanceof SVGElement) {
this._ownerSVGElement = this._rootElement.ownerSVGElement;
}

return this;
}

Expand Down Expand Up @@ -427,7 +436,7 @@ export class DragRef<T = any> {
this._dropContainer = undefined;
this._resizeSubscription.unsubscribe();
this._parentPositions.clear();
this._boundaryElement = this._rootElement = this._placeholderTemplate =
this._boundaryElement = this._rootElement = this._ownerSVGElement = this._placeholderTemplate =
this._previewTemplate = this._anchor = null!;
}

Expand Down Expand Up @@ -1049,10 +1058,22 @@ export class DragRef<T = any> {
// we can get away with it. See https://bugzilla.mozilla.org/show_bug.cgi?id=1615824.
(event.touches[0] || event.changedTouches[0] || {pageX: 0, pageY: 0}) : event;

return {
x: point.pageX - scrollPosition.left,
y: point.pageY - scrollPosition.top
};
const x = point.pageX - scrollPosition.left;
const y = point.pageY - scrollPosition.top;

// if dragging SVG element, try to convert from the screen coordinate system to the SVG
// coordinate system
if (this._ownerSVGElement) {
const svgMatrix = this._ownerSVGElement.getScreenCTM();
if (svgMatrix) {
const svgPoint = this._ownerSVGElement.createSVGPoint();
svgPoint.x = x;
svgPoint.y = y;
return svgPoint.matrixTransform(svgMatrix.inverse());
}
}

return {x, y};
}


Expand Down

0 comments on commit 190500a

Please sign in to comment.