|
|
|
|
@@ -51,8 +51,8 @@ interface IDraggableResizableOptions {
|
|
|
|
|
target: HTMLElement;
|
|
|
|
|
/** 拖拽句柄 */
|
|
|
|
|
handle?: HTMLElement;
|
|
|
|
|
/** 拖拽边界或容器元素 */
|
|
|
|
|
boundary?: IBoundaryRect | HTMLElement;
|
|
|
|
|
/** 拖拽边界容器元素 */
|
|
|
|
|
boundaryElement?: HTMLElement;
|
|
|
|
|
/** 移动步进(网格吸附) */
|
|
|
|
|
snapGrid?: number;
|
|
|
|
|
/** 关键点吸附阈值 */
|
|
|
|
|
@@ -110,7 +110,7 @@ interface IBoundaryRect {
|
|
|
|
|
export class DraggableResizableWindow {
|
|
|
|
|
private handle?: HTMLElement;
|
|
|
|
|
private target: HTMLElement;
|
|
|
|
|
private boundary?: HTMLElement | IBoundaryRect;
|
|
|
|
|
private boundaryElement: HTMLElement;
|
|
|
|
|
private snapGrid: number;
|
|
|
|
|
private snapThreshold: number;
|
|
|
|
|
private snapAnimation: boolean;
|
|
|
|
|
@@ -153,7 +153,7 @@ export class DraggableResizableWindow {
|
|
|
|
|
private maxWidth: number;
|
|
|
|
|
private maxHeight: number;
|
|
|
|
|
|
|
|
|
|
private containerRect?: DOMRect;
|
|
|
|
|
private containerRect: DOMRect;
|
|
|
|
|
private resizeObserver?: ResizeObserver;
|
|
|
|
|
private mutationObserver: MutationObserver;
|
|
|
|
|
private animationFrame?: number;
|
|
|
|
|
@@ -174,7 +174,7 @@ export class DraggableResizableWindow {
|
|
|
|
|
constructor(options: IDraggableResizableOptions) {
|
|
|
|
|
this.handle = options.handle;
|
|
|
|
|
this.target = options.target;
|
|
|
|
|
this.boundary = options.boundary;
|
|
|
|
|
this.boundaryElement = options.boundaryElement ?? document.body;
|
|
|
|
|
this.snapGrid = options.snapGrid ?? 1;
|
|
|
|
|
this.snapThreshold = options.snapThreshold ?? 0;
|
|
|
|
|
this.snapAnimation = options.snapAnimation ?? false;
|
|
|
|
|
@@ -193,15 +193,6 @@ export class DraggableResizableWindow {
|
|
|
|
|
this.onResizeEnd = options.onResizeEnd;
|
|
|
|
|
this.onWindowStateChange = options.onWindowStateChange;
|
|
|
|
|
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
this.targetBounds = {
|
|
|
|
|
width: this.target.offsetWidth,
|
|
|
|
|
height: this.target.offsetHeight,
|
|
|
|
|
top: this.target.offsetTop,
|
|
|
|
|
left: this.target.offsetLeft,
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.taskbarElementId = options.taskbarElementId;
|
|
|
|
|
|
|
|
|
|
this.target.style.position = "absolute";
|
|
|
|
|
@@ -210,6 +201,19 @@ export class DraggableResizableWindow {
|
|
|
|
|
this.target.style.transform = "translate(0px, 0px)";
|
|
|
|
|
|
|
|
|
|
this.init();
|
|
|
|
|
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
this.targetBounds = {
|
|
|
|
|
width: this.target.offsetWidth,
|
|
|
|
|
height: this.target.offsetHeight,
|
|
|
|
|
top: this.target.offsetTop,
|
|
|
|
|
left: this.target.offsetLeft,
|
|
|
|
|
};
|
|
|
|
|
this.containerRect = this.boundaryElement.getBoundingClientRect();
|
|
|
|
|
const x = this.containerRect.width / 2 - this.target.offsetWidth / 2;
|
|
|
|
|
const y = this.containerRect.height / 2 - this.target.offsetHeight / 2;
|
|
|
|
|
this.target.style.transform = `translate(${x}px, ${y}px)`;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private init() {
|
|
|
|
|
@@ -220,7 +224,7 @@ export class DraggableResizableWindow {
|
|
|
|
|
this.target.addEventListener('mouseleave', this.onMouseLeave);
|
|
|
|
|
document.addEventListener('mousemove', this.onDocumentMouseMoveCursor);
|
|
|
|
|
|
|
|
|
|
if (this.boundary instanceof HTMLElement) this.observeResize(this.boundary);
|
|
|
|
|
this.observeResize(this.boundaryElement);
|
|
|
|
|
|
|
|
|
|
this.mutationObserver = new MutationObserver(mutations => {
|
|
|
|
|
for (const mutation of mutations) {
|
|
|
|
|
@@ -236,7 +240,9 @@ export class DraggableResizableWindow {
|
|
|
|
|
|
|
|
|
|
private onMouseDownDrag = (e: MouseEvent) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
if (e.target !== this.handle) return;
|
|
|
|
|
if (!this.handle?.contains(e.target as Node)) return;
|
|
|
|
|
const target = e.target as HTMLElement;
|
|
|
|
|
if (target.classList.contains('btn')) return;
|
|
|
|
|
if (this.getResizeDirection(e)) return;
|
|
|
|
|
|
|
|
|
|
this.startX = e.clientX;
|
|
|
|
|
@@ -380,19 +386,12 @@ export class DraggableResizableWindow {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private applyBoundary() {
|
|
|
|
|
if (!this.boundary || this.allowOverflow) return;
|
|
|
|
|
if (this.allowOverflow) return;
|
|
|
|
|
let { x, y } = { x: this.currentX, y: this.currentY };
|
|
|
|
|
|
|
|
|
|
if (this.boundary instanceof HTMLElement && this.containerRect) {
|
|
|
|
|
const rect = this.target.getBoundingClientRect();
|
|
|
|
|
x = Math.min(Math.max(x, 0), this.containerRect.width - rect.width);
|
|
|
|
|
y = Math.min(Math.max(y, 0), this.containerRect.height - rect.height);
|
|
|
|
|
} else if (!(this.boundary instanceof HTMLElement) && this.boundary) {
|
|
|
|
|
if (this.boundary.minX !== undefined) x = Math.max(x, this.boundary.minX);
|
|
|
|
|
if (this.boundary.maxX !== undefined) x = Math.min(x, this.boundary.maxX);
|
|
|
|
|
if (this.boundary.minY !== undefined) y = Math.max(y, this.boundary.minY);
|
|
|
|
|
if (this.boundary.maxY !== undefined) y = Math.min(y, this.boundary.maxY);
|
|
|
|
|
}
|
|
|
|
|
const rect = this.target.getBoundingClientRect();
|
|
|
|
|
x = Math.min(Math.max(x, 0), this.containerRect.width - rect.width);
|
|
|
|
|
y = Math.min(Math.max(y, 0), this.containerRect.height - rect.height);
|
|
|
|
|
|
|
|
|
|
this.currentX = x;
|
|
|
|
|
this.currentY = y;
|
|
|
|
|
@@ -411,16 +410,9 @@ export class DraggableResizableWindow {
|
|
|
|
|
|
|
|
|
|
private getSnapPoints() {
|
|
|
|
|
const snapPoints = { x: [] as number[], y: [] as number[] };
|
|
|
|
|
if (this.boundary instanceof HTMLElement && this.containerRect) {
|
|
|
|
|
const rect = this.target.getBoundingClientRect();
|
|
|
|
|
snapPoints.x = [0, this.containerRect.width - rect.width];
|
|
|
|
|
snapPoints.y = [0, this.containerRect.height - rect.height];
|
|
|
|
|
} else if (this.boundary && !(this.boundary instanceof HTMLElement)) {
|
|
|
|
|
if (this.boundary.minX !== undefined) snapPoints.x.push(this.boundary.minX);
|
|
|
|
|
if (this.boundary.maxX !== undefined) snapPoints.x.push(this.boundary.maxX);
|
|
|
|
|
if (this.boundary.minY !== undefined) snapPoints.y.push(this.boundary.minY);
|
|
|
|
|
if (this.boundary.maxY !== undefined) snapPoints.y.push(this.boundary.maxY);
|
|
|
|
|
}
|
|
|
|
|
const rect = this.target.getBoundingClientRect();
|
|
|
|
|
snapPoints.x = [0, this.containerRect.width - rect.width];
|
|
|
|
|
snapPoints.y = [0, this.containerRect.height - rect.height];
|
|
|
|
|
return snapPoints;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -515,7 +507,7 @@ export class DraggableResizableWindow {
|
|
|
|
|
newHeight = Math.max(this.minHeight, Math.min(this.maxHeight, newHeight));
|
|
|
|
|
|
|
|
|
|
// 边界限制
|
|
|
|
|
if (!this.boundary || this.allowOverflow) {
|
|
|
|
|
if (this.allowOverflow) {
|
|
|
|
|
this.currentX = newX;
|
|
|
|
|
this.currentY = newY;
|
|
|
|
|
this.target.style.width = `${newWidth}px`;
|
|
|
|
|
@@ -524,15 +516,8 @@ export class DraggableResizableWindow {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.boundary instanceof HTMLElement && this.containerRect) {
|
|
|
|
|
newX = Math.min(Math.max(0, newX), this.containerRect.width - newWidth);
|
|
|
|
|
newY = Math.min(Math.max(0, newY), this.containerRect.height - newHeight);
|
|
|
|
|
} else if (!(this.boundary instanceof HTMLElement) && this.boundary) {
|
|
|
|
|
if (this.boundary.minX !== undefined) newX = Math.max(newX, this.boundary.minX);
|
|
|
|
|
if (this.boundary.maxX !== undefined) newX = Math.min(newX, this.boundary.maxX);
|
|
|
|
|
if (this.boundary.minY !== undefined) newY = Math.max(newY, this.boundary.minY);
|
|
|
|
|
if (this.boundary.maxY !== undefined) newY = Math.min(newY, this.boundary.maxY);
|
|
|
|
|
}
|
|
|
|
|
newX = Math.min(Math.max(0, newX), this.containerRect.width - newWidth);
|
|
|
|
|
newY = Math.min(Math.max(0, newY), this.containerRect.height - newHeight);
|
|
|
|
|
|
|
|
|
|
this.currentX = newX;
|
|
|
|
|
this.currentY = newY;
|
|
|
|
|
|