Esraa_92
Esraa_92

Reputation: 1568

Lost Rotate position on Drag element with Angular CDK DRAG

I'm trying to rotate an element with a range slider, but every time I drag the element it loses the value of the rotation while dragging and it also changes the position of the box and places it at the start. I am attaching a link to a simple test project that I have created in stackblitz, so that I can recreate the problem that I have.

stackblitz drag and rotate example

Could someone guide me in the solution?

I put the code here in case someone does not work well the link to the test project:

app.module.ts

import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { FormsModule } from "@angular/forms";

import { DragDropModule } from "@angular/cdk/drag-drop";

import { AppComponent } from "./app.component";
import { HelloComponent } from "./hello.component";

@NgModule({
  imports: [BrowserModule, FormsModule, DragDropModule],
  declarations: [AppComponent, HelloComponent],
  bootstrap: [AppComponent]
})
export class AppModule {}

app.component.ts

import { Component, Renderer2 } from "@angular/core";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  rotateValue = 0;
  dragPosition = { x: 0, y: 0 };

  constructor(private renderer: Renderer2) {}

  setRotate(value: string) {
    this.rotateValue = Number(value);
    this.renderer.setStyle(
      document.querySelector(".example-box"),
      "transform",
      `rotate(${this.rotateValue}deg)`
    );
  }
}

app.component.html

<h1 class="text-center m-3">Drag And Drop Project</h1>
<hr>

<div class="row m-5">

    <div class="col-sm-7">
        <div class="example-boundary">
            <div class="example-box" cdkDragBoundary=".example-boundary" cdkDrag>
                I can only be dragged within the dotted container
            </div>
        </div>
    </div>

    <div class="col-sm-5">
        <h4>SETTINGS</h4>
        <ul class="list-group mb-3">
            <li class="list-group-item d-flex justify-content-between lh-condensed">
                <div>
                    <h6 class="my-0">Rotate the box</h6>
                    <input #rotation
                   type="range"
                   class="custom-range  my-2"
                    min="-150" max="150"
                    [(ngModel)]="rotateValue"
                   [value]='rotateValue'
                   (change)="setRotate(rotation.value)"
                  >
        </div>
                    <span id="grados" class="text-muted">{{rotateValue}}º</span>
            </li>

        </ul>
    </div>
</div>

app.component.css

.example-box {
  width: 140px;
  height: 140px;
  border: solid 1px #ccc;
  color: rgba(0, 0, 0, 0.87);
  cursor: move;
  display: inline-flex;
  justify-content: center;
  align-items: center;
  text-align: center;
  background: #fff;
  border-radius: 4px;
  margin-right: 25px;
  position: relative;
  z-index: 1;
  box-sizing: border-box;
  padding: 10px;
  transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1);
  box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14),
    0 1px 5px 0 rgba(0, 0, 0, 0.12);
}

.example-box:active {
  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
    0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);
}

.example-boundary {
  width: 300px;
  height: 500px;
  max-width: 100%;
  border: dotted #ccc 2px;
}

Thank you very much in advance

Upvotes: 3

Views: 1913

Answers (2)

Alberto Chiesa
Alberto Chiesa

Reputation: 7350

The problem you have is that the Cdk Drag is implemented using a CSS transform rule, exactly like your custom rotation. So the two are basically incompatible, when applied to the exact same HTML element. The last operation, either drag or rotate, overwrites the other.

IMO the easiest workaround is to wrap the element that rotates inside a draggable wrapper.

Here the updated StackBlitz: https://stackblitz.com/edit/angular-ivy-ynzavj

The recap of the edits:

In the template, I wrap the rotatable div with a draggable (I also use [ngStyle] and avoid altogether the direct DOM manipulation, which wasn't by itself a problem, but was needless):

<div class="example-boundary">
  <div class="box-draggable-wrapper" cdkDragBoundary=".example-boundary" cdkDrag>
    <div class="example-box" [ngStyle]="{'transform':'rotate(' + rotateValue + 'deg)'}" >
      I can only be dragged within the dotted container
    </div>
  </div>
</div>

Just a bit of CSS for the box-draggable-wrapper:

.box-draggable-wrapper {
  width: 140px;
  height: 140px;
  display: block;
  border: none;
}

The component gets cleared up:

import { Component } from "@angular/core";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  rotateValue = 0;
  dragPosition = { x: 0, y: 0 };

  constructor() {}

  setRotate(value: string) {
    this.rotateValue = +value;
  }
}

Upvotes: 5

Trismuto
Trismuto

Reputation: 415

Well, as I presume you have already guessed, the drag action is messing with the box style, and every time you drag the box your previous rotation value is lost. But, at the same time, you are also messing with the box style, by overriding its current value with your new rotation value, and because of so the box location keeps resetting every time you rotate the box (you delete the translate3D value).

You can easily fix your code by obtaining the element's current style and merging it with yours before setting it back, which would fix the position problem, but you will still lose the rotation value when you drag the box again.

The good news is that there is already an open issue for this bug and a proposed fix, but the bad news is that they are still open.

Upvotes: 1

Related Questions