angular 模态框 模拟windows 拖动改变大小 指令

2020-06-17 11:07:21 浏览数 (1)

代码语言:typescript复制
import { Directive, ElementRef, OnInit, Renderer2, Input } from '@angular/core';

/**
 * @param area 要resize的元素
 * @param minWidth 最小宽度
 * @param minHeight 最小高度
 * @param maxWidth 最大宽度
 * @param maxHeight 最大高度
 */
export interface Resize {
  area?: string;
  minWidth?: number;
  minHeight?: number;
  maxWidth?: number;
  maxHeight?: number;
}
/**
 * 鼠标所在点对应的鼠标指针
 */
const cursor = {
  upleft: 'nw-resize',
  upRight: 'ne-resize',
  lowerRight: 'nw-resize',
  leftLower: 'ne-resize',
  up: 'n-resize',
  right: 'e-resize',
  down: 'n-resize',
  left: 'e-resize',
  defaut: null
};
/**
 * resize前的状态
 */
interface StyleStatus {
  startX?: number;
  startY?: number;
  left?: number;
  top?: number;
  width?: number;
  height?: number;
}

@Directive({
  // tslint:disable-next-line:directive-selector
  selector: '[resize]'
})
export class ResizeDirective implements OnInit {
  constructor(private el: ElementRef, private render: Renderer2) { }
  @Input() resize: Resize ;
  /**
   * resize限制
   */
  limitResize: Resize;
  /**
   * 要resize的元素
   */
  area = null;
  /**
   * 精确度
   */
  precision = 3;
  /**
   * 范围
   */
  range = 18;
  direction: string = null;
  /**
   * 是否可以改变大小, 或者正在改变大小
   */
  canResize = false;
  /**
   * resize前的状态
   */
  oldStatus: StyleStatus = {
    startX: 0,
    startY: 0,
    left: 0,
    top: 0,
    width: 0,
    height: 0
  };
  ngOnInit(): void {
    this.getElement();
  }
  /**
   * 根据指令传入的参数,找到所需的元素
   * @param isGet 是否找到
   * @description 如果找到,就初始化
   */
  getElement(isGet = false) {
    if (isGet) {
      this.init();
      this.listen();
      return true;
    }
    setTimeout(() => {
      if (this.resize.hasOwnProperty('area')) {
        this.area = this.el.nativeElement.querySelector(this.resize.area);
      } else {
        this.area = this.el.nativeElement;
      }
      this.getElement(!!(this.area));
    }, 500);
  }
  init() {
    const style = window.getComputedStyle(this.area, null);
    this.limitResize = {
      // tslint:disable-next-line:radix
      minWidth: this.resize.minWidth ? this.resize.minWidth : parseInt(style.width),
      // tslint:disable-next-line:radix
      minHeight: this.resize.minHeight ? this.resize.minHeight : parseInt(style.height),
      maxWidth: this.resize.maxWidth ? this.resize.maxWidth : window.innerWidth,
      maxHeight: this.resize.maxHeight ? this.resize.maxHeight : window.innerHeight,
    };
  }
  /**
   * 监听鼠标move事件。根据鼠标move的值,判断鼠标当前所处的点。根据当前的点来设置对应的鼠标指针状态
   */
  listen() {
    this.render.listen(this.area, 'mousemove', (e: MouseEvent) => {
      const top = this.getElementToPageTop(this.area);
      const left = this.getElementToPageLeft(this.area);
      const width = this.area.clientWidth;
      const height = this.area.clientHeight;
      const x = e.clientX;
      const y = e.clientY;
      if ( (x >= left && x <= left   this.precision) && (y >= top && y <= top   this.range) &&
          (x >= left && x <= left   this.range) && (y >= top && y <= top   this.precision) ) { // 左上
        this.direction = 'upleft';
      } else if ( ((x >= left   width - this.range && x <= left   width) && (y >= top && y <= top   this.precision)) &&
          ((x >= left   width - this.precision && x <= left   width) && (y >= top && y <= top   this.range)) ) { // 右上
        this.direction = 'upRight';
      } else if ( ((x >= left   width - this.precision && x <= left   width) && (y >= top   height - this.range && y <= top   height)) &&
          ((x >= left   width - this.range && x <= left   width) && (y >= top   height - this.precision && y <= top   height)) ) { // 右下
        this.direction = 'lowerRight';
      } else if ( ((x >= left && x <= left   this.range) && (y >= top   height - this.precision && y <= top   height)) &&
          ((x >= left && x <= left   this.precision) && (y >= top   height - this.range && y <= top   height)) ) { // 左下
        this.direction = 'leftLower';
      } else if ((x > left   this.range && x < left   width - this.range) && (y >= top && y <= top   this.precision)) { // 上
        this.direction = 'up';
      } else if ( (x >= left   width - this.precision && x <= left   width) &&
        (y > top   this.range && y < top   height - this.range)) { // 右
        this.direction = 'right';
      } else if ( (x > left   this.range && x < left   width - this.range) &&
        (y >= top   height - this.precision && y <= top   height)) { // 下
        this.direction = 'down';
      } else if ( (x >= left && x <= left   this.precision) &&
        (y > top   this.range && y < top   height - this.range)) { // 左
        this.direction = 'left';
      } else {
        if (this.canResize) {
          return;
        }
        this.direction = 'defaut';
        this.render.removeAttribute(this.el.nativeElement, 'resizing');
      }
      this.setCursor();
    });
  }
  /**
   * 获取HTMLElement 到页面顶部的距离
   * @param el HTMLElement
   */
  getElementToPageTop(el: HTMLElement) {
    if (el.parentElement) {
      return this.getElementToPageTop(el.parentElement)   el.offsetTop;
    }
    return el.offsetTop;
  }
  /**
   * 获取HTMLElement 到页面左边的距离
   * @param el HTMLElement
   */
  getElementToPageLeft(el: HTMLElement) {
    if (el.parentElement) {
      return this.getElementToPageLeft(el.parentElement)   el.offsetLeft;
    }
    return el.offsetLeft;
  }
  /**
   * 设置光标,监听事件并处理事件
   */
  setCursor() {
    this.render.setStyle(this.area, 'cursor', cursor[this.direction]);
    const mousedown = this.render.listen(this.area, 'mousedown', (e: MouseEvent) => {
      if (this.direction !== 'defaut') {
        this.canResize = true;
        this.render.setAttribute(this.el.nativeElement, 'resizing', 'true');
      }
      this.oldStatus.top = this.getElementToPageTop(this.area);
      this.oldStatus.left = this.getElementToPageLeft(this.area);
      this.oldStatus.width = this.area.clientWidth;
      this.oldStatus.height = this.area.clientHeight;
      this.oldStatus.startX = e.clientX;
      this.oldStatus.startY = e.clientY;
      if (this.area.hasOwnProperty('setCapture')) {
        this.area.setCapture();
      }
    });
    const mousemove = this.render.listen(document, 'mousemove', (e: MouseEvent) => {
      if (this.canResize) {
        switch (this.direction) {
          case 'up': this.resize2Up(e, this.oldStatus); break;
          case 'right': this.resize2Right(e, this.oldStatus); break;
          case 'down': this.resize2Down(e, this.oldStatus); break;
          case 'left': this.resize2Left(e, this.oldStatus); break;
          case 'upleft': this.resize2Upleft(e, this.oldStatus); break;
          case 'upRight': this.resize2UpRight(e, this.oldStatus); break;
          case 'lowerRight': this.resize2LowerRight(e, this.oldStatus); break;
          case 'leftLower': this.resize2LeftLower(e, this.oldStatus); break;
        }
      }
    });
    const mouseup  = this.render.listen(document, 'mouseup', (e: MouseEvent) => {
      this.canResize = false;
      this.render.removeAttribute(this.el.nativeElement, 'resizing');
      if (this.area.hasOwnProperty('releaseCapture')) {
        this.area.releaseCapture();
      }
      mousedown(); // 解除事件监听
      mousemove(); // 解除事件监听
      mouseup(); // 解除事件监听
    });
  }
  /**
   * 上边resize
   * @param e mousemove事件
   * @param status resize前 元素的状态
   */
  resize2Up(e: MouseEvent, status: StyleStatus) {
    const moveLengthY = this.oldStatus.startY - e.clientY;
    if (!this.recalculateBounds(status, 0, moveLengthY)) { return; }
    this.render.setStyle(this.area, 'height', status.height   moveLengthY   'px');
    this.render.setStyle(this.area, 'margin-top', -moveLengthY   'px');
  }
  /**
   * 右边resize
   * @param e mousemove事件
   * @param status resize前 元素的状态
   */
  resize2Right(e: MouseEvent, status: StyleStatus) {
    const moveLengthX = e.clientX - this.oldStatus.startX;
    if (!this.recalculateBounds(status, moveLengthX, 0)) { return; }
    this.render.setStyle(this.area, 'width', status.width   moveLengthX   'px');
  }
  /**
   * 下边resize
   * @param e mousemove事件
   * @param status resize前 元素的状态
   */
  resize2Down(e: MouseEvent, status: StyleStatus) {
    const moveLengthY = e.clientY - this.oldStatus.startY;
    if (!this.recalculateBounds(status, 0, moveLengthY)) { return; }
    this.render.setStyle(this.area, 'height', status.height   moveLengthY   'px');
  }
  /**
   * 左边resize
   * @param e mousemove事件
   * @param status resize前 元素的状态
   */
  resize2Left(e: MouseEvent, status: StyleStatus) {
    const moveLengthX = this.oldStatus.startX - e.clientX;
    if (!this.recalculateBounds(status, moveLengthX, 0)) { return; }
    this.render.setStyle(this.area, 'width', status.width   moveLengthX   'px');
    this.render.setStyle(this.area, 'margin-left', -moveLengthX   'px');
  }
  /**
   * 左上角resize
   * @param e mousemove事件
   * @param status resize前 元素的状态
   */
  resize2Upleft(e: MouseEvent, status: StyleStatus) {
    const moveLengthX =  this.oldStatus.startX - e.clientX;
    const moveLengthY =  this.oldStatus.startY - e.clientY;
    if (!this.recalculateBounds(status, moveLengthX, moveLengthY)) { return; }
    this.render.setStyle(this.area, 'width', status.width   moveLengthX   'px');
    this.render.setStyle(this.area, 'height', status.height   moveLengthY   'px');
    this.render.setStyle(this.area, 'margin-left', -moveLengthX   'px');
    this.render.setStyle(this.area, 'margin-top', -moveLengthY   'px');
  }
  /**
   * 右上角resize
   * @param e mousemove事件
   * @param status resize前 元素的状态
   */
  resize2UpRight(e: MouseEvent, status: StyleStatus) {
    const moveLengthX = e.clientX - this.oldStatus.startX;
    const moveLengthY = this.oldStatus.startY - e.clientY;
    if (!this.recalculateBounds(status, moveLengthX, moveLengthY)) { return; }
    this.render.setStyle(this.area, 'width', status.width   moveLengthX   'px');
    this.render.setStyle(this.area, 'height', status.height   moveLengthY   'px');
    this.render.setStyle(this.area, 'margin-top', -moveLengthY   'px');
  }
  /**
   * 右下角resize
   * @param e mousemove事件
   * @param status resize前 元素的状态
   */
  resize2LowerRight(e: MouseEvent, status: StyleStatus) {
    const moveLengthX = e.clientX - this.oldStatus.startX;
    const moveLengthY = e.clientY - this.oldStatus.startY;
    if (!this.recalculateBounds(status, moveLengthX, moveLengthY)) { return; }
    this.render.setStyle(this.area, 'width', status.width   moveLengthX   'px');
    this.render.setStyle(this.area, 'height', status.height   moveLengthY   'px');
  }
  /**
   * 左下角resize
   * @param e mousemove事件
   * @param status resize前 元素的状态
   */
  resize2LeftLower(e: MouseEvent, status: StyleStatus) {
    const moveLengthX = this.oldStatus.startX - e.clientX;
    const moveLengthY = e.clientY - this.oldStatus.startY;
    if (!this.recalculateBounds(status, moveLengthX, moveLengthY)) { return; }
    this.render.setStyle(this.area, 'width', status.width   moveLengthX   'px');
    this.render.setStyle(this.area, 'height', status.height   moveLengthY   'px');
    this.render.setStyle(this.area, 'margin-left', -moveLengthX   'px');
  }
  /**
   * 预先计算下一次resize的宽高,如果超过最大,最小宽高,就不让resize
   * @param status resize前的状态
   * @param moveLengthX 要变化的宽度
   * @param moveLengthY 要变化的高度
   */
  recalculateBounds(status: StyleStatus, moveLengthX = 0, moveLengthY = 0): boolean {
    const nextWith = status.width   moveLengthX;
    const nextHeight = status.height   moveLengthY;
    if (
      nextWith < this.limitResize.minWidth || nextHeight < this.limitResize.minHeight ||
      nextWith > this.limitResize.maxWidth || nextHeight > this.limitResize.maxHeight
    ) {
      return false;
    }
    return true;
  }
}

0 人点赞