Skip to content

Commit 190500a

Browse files
NiyazNzmmalerba
authored andcommitted
fix(cdk/drag-drop): pointer position calculation for SVG with viewBox (#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
1 parent 3db41f6 commit 190500a

File tree

2 files changed

+50
-5
lines changed

2 files changed

+50
-5
lines changed

src/cdk/drag-drop/directives/drag.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,17 @@ describe('CdkDrag', () => {
9191
expect(dragElement.getAttribute('transform')).toBe('translate(50 100)');
9292
}));
9393

94+
it('should drag an SVG element freely to a particular position in SVG viewBox coordinates',
95+
fakeAsync(() => {
96+
const fixture = createComponent(StandaloneDraggableSvgWithViewBox);
97+
fixture.detectChanges();
98+
const dragElement = fixture.componentInstance.dragElement.nativeElement;
99+
100+
expect(dragElement.getAttribute('transform')).toBeFalsy();
101+
dragElementViaMouse(fixture, dragElement, 50, 100);
102+
expect(dragElement.getAttribute('transform')).toBe('translate(100 200)');
103+
}));
104+
94105
it('should drag an element freely to a particular position when the page is scrolled',
95106
fakeAsync(() => {
96107
const fixture = createComponent(StandaloneDraggable);
@@ -5330,6 +5341,19 @@ class StandaloneDraggableSvg {
53305341
@ViewChild('dragElement') dragElement: ElementRef<SVGElement>;
53315342
}
53325343

5344+
@Component({
5345+
template: `
5346+
<svg width="400px" height="400px" viewBox="0 0 800 800"><g
5347+
cdkDrag
5348+
#dragElement>
5349+
<circle fill="red" r="50" cx="50" cy="50"/>
5350+
</g></svg>
5351+
`
5352+
})
5353+
class StandaloneDraggableSvgWithViewBox {
5354+
@ViewChild('dragElement') dragElement: ElementRef<SVGElement>;
5355+
}
5356+
53335357
@Component({
53345358
template: `
53355359
<div #dragElement cdkDrag

src/cdk/drag-drop/drag-ref.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ export class DragRef<T = any> {
163163
*/
164164
private _rootElement: HTMLElement;
165165

166+
/**
167+
* Nearest ancestor SVG, relative to which coordinates are calculated if dragging SVGElement
168+
*/
169+
private _ownerSVGElement: SVGSVGElement | null;
170+
166171
/**
167172
* Inline style value of `-webkit-tap-highlight-color` at the time the
168173
* dragging was started. Used to restore the value once we're done dragging.
@@ -380,6 +385,10 @@ export class DragRef<T = any> {
380385
this._rootElement = element;
381386
}
382387

388+
if (typeof SVGElement !== 'undefined' && this._rootElement instanceof SVGElement) {
389+
this._ownerSVGElement = this._rootElement.ownerSVGElement;
390+
}
391+
383392
return this;
384393
}
385394

@@ -427,7 +436,7 @@ export class DragRef<T = any> {
427436
this._dropContainer = undefined;
428437
this._resizeSubscription.unsubscribe();
429438
this._parentPositions.clear();
430-
this._boundaryElement = this._rootElement = this._placeholderTemplate =
439+
this._boundaryElement = this._rootElement = this._ownerSVGElement = this._placeholderTemplate =
431440
this._previewTemplate = this._anchor = null!;
432441
}
433442

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

1052-
return {
1053-
x: point.pageX - scrollPosition.left,
1054-
y: point.pageY - scrollPosition.top
1055-
};
1061+
const x = point.pageX - scrollPosition.left;
1062+
const y = point.pageY - scrollPosition.top;
1063+
1064+
// if dragging SVG element, try to convert from the screen coordinate system to the SVG
1065+
// coordinate system
1066+
if (this._ownerSVGElement) {
1067+
const svgMatrix = this._ownerSVGElement.getScreenCTM();
1068+
if (svgMatrix) {
1069+
const svgPoint = this._ownerSVGElement.createSVGPoint();
1070+
svgPoint.x = x;
1071+
svgPoint.y = y;
1072+
return svgPoint.matrixTransform(svgMatrix.inverse());
1073+
}
1074+
}
1075+
1076+
return {x, y};
10561077
}
10571078

10581079

0 commit comments

Comments
 (0)