代码语言: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;
}
}