Adding resize functionality to an element in angul

2019-08-31 01:45发布

问题:

I am adding a resize functionality following a codepen example mentioned in the comments using MARGIN=4 (here I was not able to add the codepen link) The codepen was the best working example which I could find.

Have added 4 elements on all the 4 edges of the box.

I Have added hostlisteners to pointerdown , pointermove and pointerup but I am stuck with executing the animate() function present in Resizable directive.

In the directive the code has mainly 3 functions doing all the calculations onDown() , calc(e), animate()

import { Directive,
         ElementRef,
         HostListener } from '@angular/core';
import { DraggableDirective } from './draggable.directive';

@Directive({
 selector: '[appResizable]' // Attribute selector
})

export class ResizableDirective extends DraggableDirective{

constructor(public element:ElementRef){
super(element);
}

minWidth = 60;
minHeight = 40;
// Thresholds
MARGINS = 4;

//End Of whats configurable
clicked = null;
public onRightEdge; onBottomEdge; onLeftEdge; onTopEdge;
public b; x; y;

redraw = false;
e;
clickedDragging = false;

ngOnInit(){
 this.animate() 
}

@HostListener('dragStart', ['$event'])
 onDragStart(e:PointerEvent): void{
 this.clickedDragging = true;
 this.onDown(e);
 e.preventDefault();
 }

@HostListener('dragMove', ['$event'])
  onDragMove(ee): void{
   if (!this.dragging || ee.pointerId !== this.pointerId) {
    return;
   }
   if((<HTMLElement>event.srcElement).id === ('side-top')){
   this.onMove(ee);
   ee.preventDefault();
   }
 }

 @HostListener('dragEnd', ['$event'])
     ondragend(ev): void{
     this.onUp(ev);
  }

onMove(ee){
    if (!this.dragging || ee.pointerId !== this.pointerId) {
     return;
   }
   if(this.clickedDragging){
     this.calc(ee);
     this.e = ee;
     this.redraw = true;
    }
   }

onDown(e){
    this.calc(e);

    let isResizing = this.onRightEdge || this.onBottomEdge || 
                     this.onLeftEdge || this.onTopEdge;

    this.clicked = {
      x: this.x,
      y: this.y,
      cx: e.clientX,
      cy: e.clientY,
      w: this.b.width,
      h: this.b.height,
      isResizing: isResizing,
      onRightEdge: this.onRightEdge,
      onBottomEdge: this.onBottomEdge,
      onLeftEdge: this.onLeftEdge,
      onTopEdge: this.onTopEdge,
   }

 }

calc(e){
   this.b = this.element.nativeElement.getBoundingClientRect();
   this.x = e.clientX - this.b.left;
   this.y = e.clientY - this.b.top;

   this.onRightEdge = this.x >= this.b.width - this.MARGINS;
   this.onBottomEdge = this.y >= this.b.height - this.MARGINS;
   this.onLeftEdge = this.x < this.MARGINS;
   this.onTopEdge = this.y < this.MARGINS;
  }

animate(){
  requestAnimationFrame(this.animate);
  if(!this.redraw)return;
  this.redraw = false;

  if(this.clicked && this.clicked.isResizing){

    if(this.clicked.onRightEdge){ 
      this.element.nativeElement.style.width = Math.max(this.x, 
      this.minWidth) + 'px';
    }

   if(this.clicked.onBottomEdge){
       this.element.nativeElement.style.height = Math.max(this.y, 
       this.minHeight) + 'px';
    }

   if(this.clicked.onLeftEdge){
     let currentWidth = Math.max(this.clicked.cx - 
         this.e.clientX + this.clicked.w, this.minWidth);

     if(currentWidth > this.minWidth){
      this.element.nativeElement.style.width = currentWidth + 'px';
      this.element.nativeElement.style.left = this.e.clientX + 'px';
     }
    }

    if (this.clicked.onTopEdge) {
     var currentHeight = Math.max(this.clicked.cy - 
                                  this.e.clientY  +
                                   this.clicked.h, this.minHeight);
     if (currentHeight > this.minHeight) {
      this.element.nativeElement.style.height = currentHeight + 'px';
      this.element.nativeElement.style.top = this.e.clientY + 'px'; 
     }
    }

  return;
 }
}

onUp(ev) {
   this.calc(ev);
   this.clicked = null;
   }  
}

HTML snippet on which directive has been applied

<div class="box" *ngFor="let box of dropzone1" appDroppable (dragStart)="currentBox = box" appMovable>
    {{ box.dis }}

    <div class="side side-h" id="side-top" (dragStart)=(e) (dragMove)=(e) (dragEnd)=(e) appResizable></div>
    <div class="side side-v" id="side-right" (click)="clcikme(e)" ></div>
    <div class="side side-h" id="side-bottom" (click)="clcikme(e)"></div>
    <div class="side side-v" id="side-left" (click)="clcikme(e)"></div>
</div>

In the codepen example for b

this.b = this.element.nativeElement.getBoundingClientRect();

the whole element has been taken which I'll also have to do, in my case I have the border on which the directive is there

I am attaching a working stackblitz the resizable directive is present in draggable folder and used in hello component In the stackblitz console logging of pointerdown, pointermove can also be seen.

https://stackblitz.com/edit/angular-pcpev1?file=src/app/hello.component.html

Error in console animate of undefined

Please refer to the codepen example from the comments.

Update

https://stackblitz.com/edit/angular-pcpev1?file=src%2Fapp%2Fhello.component.html

回答1:

Sorry I can't fix your code because it's a mess, you just do this completely wrong, you just did two approaches I suggest in one code. Also you're leaking resize internals outside of directive, the divs need to be hidden, outside you only should use resize attribute and everything should be created inside directive.

Here, this is started for one top side:

@Directive({
    selector: '[resize]'
})
class Resizable implements OnInit, OnDestroy {
    private nodes: HtmlElement[] = [];
    private data: {x: number, y: number, rect: ClientRect, direction: string};
    constructor(@Inject(ElementRef) private element: ElementRef) {
        this.mousemove = this.mousemove.bind(this);
        this.mouseup = this.mouseup.bind(this);
    }
    mousemove(e) {
        if (this.data) {
            switch(this.data.direction) {
                case 'top':
                    var offset = this.data.y - e.clientY;
                    var height = this.data.rect.height;
                    var top = this.data.rect.top;
                    var style = this.element.nativeElement.style;
                    style.height = height + offset + 'px';
                    style.top = top - offset + 'px';
                    break;
            }
        }
    }
    ngOnInit() {
        var node = document.createElement('div');
        node.classList.add('border-top', 'border');
        this.element.nativeElement.appendChild(node);
        this.nodes.push(node);
        window.addEventListener('mousemove', this.mousemove);
        window.addEventListener('mouseup', this.mouseup);
    }
    @HostListener('mousedown', ['$event'])
    mousedown(e) {
        if (e.target.classList.contains('border')) {
            var rect = this.element.nativeElement.getBoundingClientRect();
            this.data = {
                x: e.clientX,
                y: e.clientY,
                rect,
                direction: e.target.className.match(/border-([^ ]+)/)[1]
            };
            e.preventDefault();
        } else {
            delete this.data;
        }
    }
    mouseup(e) {
         delete this.data;
    }
    ngOnDestroy() {
        this.nodes.forEach(n => n.remove());
        window.removeEventListener('mousemove', this.mousemove);
        window.removeEventListener('mouseup', this.mouseup);
    }
}

here is CodePen demo