components icon indicating copy to clipboard operation
components copied to clipboard

CDK Drag boundary is not working for SVG element

Open JigneshGothadiya opened this issue 3 years ago • 7 comments

I am working on one project which having functionality to draw shape and drag it in it's container but when I used cdkBoundary for rect element and drag it then transform property updated base on top most parent's coordinate.

JigneshGothadiya avatar Feb 27 '21 06:02 JigneshGothadiya

Can you post an example of your code? It's hard to tell what could be wrong based on the description.

crisbeto avatar Mar 01 '21 16:03 crisbeto

Please find below sample code of my project Or you can get it from stackbliz

.boundary{ width: 200px; height: 200px; border: 1px solid red; }

.bluebox { height: 40px; width: 40px; background-color: blue; }

svg{ border: 1px solid green; width: 100%; }

rect, circle{ fill: darkgray; }

"dependencies": { "@angular/animations": "11.0.0", "@angular/cdk": "~11.0.0", "@angular/common": "~11.0.0", "@angular/compiler": "~11.0.0", "@angular/core": "~11.0.0", "@angular/forms": "~11.0.0", "@angular/platform-browser": "~11.0.0", "@angular/platform-browser-dynamic": "~11.0.0", "@angular/router": "~11.0.0", "core-js": "2.6.9", "rxjs": "~6.5.5", "zone.js": "0.10.3" },

JigneshGothadiya avatar Mar 02 '21 04:03 JigneshGothadiya

Thank you for your reply,

Now I am getting 'Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://jmg1-sandbox.ideas.aha.io') does not match the recipient window's origin' error.

On Mon, Mar 1, 2021 at 10:23 PM Kristiyan Kostadinov < [email protected]> wrote:

Can you post an example of your code? It's hard to tell what could be wrong based on the description.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/angular/components/issues/22051#issuecomment-788103069, or unsubscribe https://github.com/notifications/unsubscribe-auth/AGNHP3OAGKX2WW376IN7TEDTBPBCLANCNFSM4YJTKOLQ .

-- Regards,

  • Jignesh Gothadiya Software Developer

JigneshGothadiya avatar Mar 03 '21 07:03 JigneshGothadiya

@NiyazNz This bug is due to your changes in https://github.com/angular/components/pull/19863 where you are returning an SVGPoint object whose x/y coordinates are no longer relative to other items on the page (which needs to respected for boundary elements and I'm assuming this would mess up sorting as well, but haven't actually checked).

I experimented with a solution that doesn't modify the pointer position value and instead calculates a ratio to multiply the default transform translate value by

      let transformRatioX = 1.0;
      let transformRatioY = 1.0;

      if (typeof SVGElement !== 'undefined' && this._rootElement instanceof SVGElement) {
        const svgElement = this._ownerSVGElement;
        const svgViewBoxRect = svgElement.viewBox.baseVal;

        if (svgElement.clientWidth !== 0 && svgElement.clientHeight !== 0) {
          const preserveAspectRatio = svgElement.preserveAspectRatio;
          const aspectRatio = svgViewBoxRect.width / svgViewBoxRect.height;
          let widthRatio = svgViewBoxRect.width / svgElement.clientWidth;
          let heightRatio = svgViewBoxRect.height / svgElement.clientHeight;
          if (preserveAspectRatio.baseVal.align !== preserveAspectRatio.baseVal.SVG_PRESERVEASPECTRATIO_NONE) {
            if (preserveAspectRatio.baseVal.meetOrSlice == preserveAspectRatio.baseVal.SVG_MEETORSLICE_MEET) {
              // meet (scale-down)
              if (widthRatio > heightRatio) {
                transformRatioX = widthRatio;
                const height = svgElement.clientWidth / aspectRatio;
                transformRatioY = svgViewBoxRect.height / height;
                
              } else if (heightRatio > widthRatio) {
                transformRatioY = heightRatio;
                const width = svgElement.clientHeight * aspectRatio;
                transformRatioX = svgViewBoxRect.width / width;
              }
            } else {
              // slice (scale-up)
              if (widthRatio > heightRatio) {
                
                transformRatioY = heightRatio;
                const width = svgElement.clientHeight * aspectRatio;
                transformRatioX = svgViewBoxRect.width / width;
                
              } else if (heightRatio > widthRatio) {
                transformRatioX = widthRatio;
                const height = svgElement.clientWidth / aspectRatio;
                transformRatioY = svgViewBoxRect.height / height;
              }
            }
          } else {
            if (svgViewBoxRect.width !== 0) {
              transformRatioX = widthRatio;
            }
          
            if (svgViewBoxRect.height !== 0) {
              transformRatioY = heightRatio;
            }
          }
        }
      }
      
      const activeTransform = this._activeTransform;
      activeTransform.x =
          (constrainedPointerPosition.x - this._pickupPositionOnPage.x) * transformRatioX + this._passiveTransform.x;
      activeTransform.y =
          (constrainedPointerPosition.y - this._pickupPositionOnPage.y) * transformRatioY + this._passiveTransform.y;
      
      this._applyRootElementTransform(activeTransform.x, activeTransform.y);

This is totally unoptimized (perhaps the transform ratio could be cached) and possibly only working in Chrome (noticed clientHeight/clientWidth of an SVG can return 0 in Firefox) and assumes the SVG does not include padding, and probably issues with more edge cases, not the ideal place to put this code, etc...so really just a proof-of-concept.

This code was inserted here: https://github.com/angular/components/blob/ad248652e7da31398176fe11878b59805e440b5a/src/cdk/drag-drop/drag-ref.ts#L688-L704

along with reverting the change from your PR that modifies the returned pointer position.

Achilles1515 avatar Mar 24 '21 17:03 Achilles1515

Better repro case from #22593: https://stackblitz.com/edit/angular-wmvtan-co9kyn?file=package.json

crisbeto avatar May 02 '21 08:05 crisbeto

Same #22592 issue in Angular11. Is there any workaround?

qwe852147 avatar Aug 26 '21 00:08 qwe852147

you could add a div outside the svg and set boundary to that, and after view init or whenever the svg size changes:

const svgRectangle = this.svg.nativeElement.getBoundingClientRect();
const boundary = this.boundary.nativeElement;
boundary.style.position = 'absolute';
boundary.style.visibility = 'hidden';
boundary.style.top = '0px';
boundary.style.left = '0px';
boundary.style.width = `${svgRectangle.width}px`;
boundary.style.height = `${svgRectangle.height}px`;

gizm0bill avatar Aug 30 '22 07:08 gizm0bill