前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >angular 模态框 模拟windows 拖动改变大小 指令

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

原创
作者头像
treeNewBe
修改2020-06-17 11:07:21
1.2K0
修改2020-06-17 11:07:21
举报
代码语言: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;
  }
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档