保存一下
This commit is contained in:
@@ -84,6 +84,7 @@ export class DesktopProcess extends ProcessImpl {
|
||||
dom.style.width = `${this._width}px`
|
||||
dom.style.height = `${this._height}px`
|
||||
dom.style.position = 'relative'
|
||||
dom.style.overflow = 'hidden'
|
||||
this._desktopRootDom = dom
|
||||
|
||||
const app = createApp(DesktopComponent, { process: this })
|
||||
|
||||
199
src/core/utils/Draggable.ts
Normal file
199
src/core/utils/Draggable.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
type TDragStartCallback = (x: number, y: number) => void;
|
||||
type TDragMoveCallback = (x: number, y: number) => void;
|
||||
type TDragEndCallback = (x: number, y: number) => void;
|
||||
|
||||
/**
|
||||
* 拖拽参数
|
||||
*/
|
||||
interface IDraggableOptions {
|
||||
/** 用来触发拖拽的元素、句柄 */
|
||||
handle: HTMLElement;
|
||||
/** 真正移动的元素 */
|
||||
target: HTMLElement;
|
||||
/** 拖拽模式 */
|
||||
mode?: 'transform' | 'position';
|
||||
/** 拖拽的边界或容器元素 */
|
||||
boundary?: IBoundary | HTMLElement;
|
||||
/** 拖拽开始回调 */
|
||||
onStart?: TDragStartCallback;
|
||||
/** 拖拽移动中回调 */
|
||||
onMove?: TDragMoveCallback;
|
||||
/** 拖拽结束回调 */
|
||||
onEnd?: TDragEndCallback;
|
||||
}
|
||||
|
||||
/** 拖拽的范围边界 */
|
||||
interface IBoundary {
|
||||
/** 最小 X 坐标 */
|
||||
minX?: number;
|
||||
/** 最大 X 坐标 */
|
||||
maxX?: number;
|
||||
/** 最小 Y 坐标 */
|
||||
minY?: number;
|
||||
/** 最大 Y 坐标 */
|
||||
maxY?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拖拽功能通用类
|
||||
*/
|
||||
export class Draggable {
|
||||
private handle: HTMLElement;
|
||||
private target: HTMLElement;
|
||||
private mode: 'transform' | 'position';
|
||||
private boundary?: IBoundary;
|
||||
private containerElement?: HTMLElement;
|
||||
private containerBounds?: IBoundary;
|
||||
|
||||
private startX = 0;
|
||||
private startY = 0;
|
||||
private originX = 0;
|
||||
private originY = 0;
|
||||
private currentX = 0;
|
||||
private currentY = 0;
|
||||
private dragging = false;
|
||||
|
||||
private onStart?: TDragStartCallback;
|
||||
private onMove?: TDragMoveCallback;
|
||||
private onEnd?: TDragEndCallback;
|
||||
|
||||
private resizeObserver?: ResizeObserver;
|
||||
|
||||
constructor(options: IDraggableOptions) {
|
||||
this.handle = options.handle;
|
||||
this.target = options.target;
|
||||
this.mode = options.mode ?? 'transform';
|
||||
this.onStart = options.onStart;
|
||||
this.onMove = options.onMove;
|
||||
this.onEnd = options.onEnd;
|
||||
|
||||
// 判断 boundary 类型
|
||||
if (options.boundary instanceof HTMLElement) {
|
||||
this.containerElement = options.boundary;
|
||||
this.observeResize(); // 监听容器和目标变化
|
||||
} else {
|
||||
this.boundary = options.boundary;
|
||||
}
|
||||
|
||||
if (this.mode === 'position') {
|
||||
const computed = window.getComputedStyle(this.target);
|
||||
if (computed.position === 'static') {
|
||||
this.target.style.position = 'absolute';
|
||||
}
|
||||
}
|
||||
|
||||
this.handle.addEventListener('mousedown', this.onMouseDown);
|
||||
}
|
||||
|
||||
/** 监听容器和目标大小变化 */
|
||||
private observeResize() {
|
||||
this.resizeObserver = new ResizeObserver(() => {
|
||||
this.updateContainerBounds();
|
||||
});
|
||||
|
||||
if (this.containerElement) {
|
||||
this.resizeObserver.observe(this.containerElement);
|
||||
}
|
||||
|
||||
// 监听目标大小变化
|
||||
this.resizeObserver.observe(this.target);
|
||||
|
||||
this.updateContainerBounds();
|
||||
}
|
||||
|
||||
/** 更新边界 */
|
||||
private updateContainerBounds() {
|
||||
if (!this.containerElement) return;
|
||||
|
||||
const containerRect = this.containerElement.getBoundingClientRect();
|
||||
const targetRect = this.target.getBoundingClientRect();
|
||||
|
||||
if (this.mode === 'transform') {
|
||||
this.containerBounds = {
|
||||
minX: containerRect.left + window.scrollX,
|
||||
maxX: containerRect.right + window.scrollX - targetRect.width,
|
||||
minY: containerRect.top + window.scrollY,
|
||||
maxY: containerRect.bottom + window.scrollY - targetRect.height,
|
||||
};
|
||||
} else {
|
||||
this.containerBounds = {
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
maxX: containerRect.width - targetRect.width,
|
||||
maxY: containerRect.height - targetRect.height,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private onMouseDown = (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
this.handle.style.cursor = 'move';
|
||||
this.dragging = true;
|
||||
|
||||
const rect = this.target.getBoundingClientRect();
|
||||
this.originX = rect.left + window.scrollX;
|
||||
this.originY = rect.top + window.scrollY;
|
||||
|
||||
if (this.mode === 'position') {
|
||||
const style = window.getComputedStyle(this.target);
|
||||
this.originX = parseFloat(style.left) || 0;
|
||||
this.originY = parseFloat(style.top) || 0;
|
||||
}
|
||||
|
||||
this.startX = e.clientX;
|
||||
this.startY = e.clientY;
|
||||
|
||||
this.currentX = this.originX;
|
||||
this.currentY = this.originY;
|
||||
|
||||
this.onStart?.(this.currentX, this.currentY);
|
||||
|
||||
document.addEventListener('mousemove', this.onMouseMove);
|
||||
document.addEventListener('mouseup', this.onMouseUp);
|
||||
};
|
||||
|
||||
private onMouseMove = (e: MouseEvent) => {
|
||||
if (!this.dragging) return;
|
||||
|
||||
let newX = this.originX + (e.clientX - this.startX);
|
||||
let newY = this.originY + (e.clientY - this.startY);
|
||||
|
||||
const bounds = this.containerBounds || this.boundary;
|
||||
if (bounds) {
|
||||
if (bounds.minX !== undefined) newX = Math.max(newX, bounds.minX);
|
||||
if (bounds.maxX !== undefined) newX = Math.min(newX, bounds.maxX);
|
||||
if (bounds.minY !== undefined) newY = Math.max(newY, bounds.minY);
|
||||
if (bounds.maxY !== undefined) newY = Math.min(newY, bounds.maxY);
|
||||
}
|
||||
|
||||
if (this.mode === 'transform') {
|
||||
this.target.style.transform = `translate(${newX}px, ${newY}px)`;
|
||||
} else {
|
||||
this.target.style.left = `${newX}px`;
|
||||
this.target.style.top = `${newY}px`;
|
||||
}
|
||||
|
||||
this.currentX = newX;
|
||||
this.currentY = newY;
|
||||
|
||||
this.onMove?.(this.currentX, this.currentY);
|
||||
};
|
||||
|
||||
private onMouseUp = () => {
|
||||
this.dragging = false;
|
||||
this.handle.style.cursor = 'default';
|
||||
|
||||
this.onEnd?.(this.currentX, this.currentY);
|
||||
|
||||
document.removeEventListener('mousemove', this.onMouseMove);
|
||||
document.removeEventListener('mouseup', this.onMouseUp);
|
||||
};
|
||||
|
||||
public destroy() {
|
||||
this.handle.removeEventListener('mousedown', this.onMouseDown);
|
||||
document.removeEventListener('mousemove', this.onMouseMove);
|
||||
document.removeEventListener('mouseup', this.onMouseUp);
|
||||
this.resizeObserver?.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { IWindowForm } from '@/core/window/IWindowForm.ts'
|
||||
import type { IWindowFormConfig } from '@/core/window/types/IWindowFormConfig.ts'
|
||||
import type { WindowFormPos } from '@/core/window/types/WindowFormTypes.ts'
|
||||
import { processManager } from '@/core/process/ProcessManager.ts'
|
||||
import { Draggable } from '@/core/utils/Draggable.ts'
|
||||
|
||||
export default class WindowFormImpl implements IWindowForm {
|
||||
private readonly _id: string = uuidV4();
|
||||
@@ -45,6 +46,11 @@ export default class WindowFormImpl implements IWindowForm {
|
||||
dom.style.height = `${this.height}px`;
|
||||
dom.style.zIndex = '100';
|
||||
dom.style.backgroundColor = 'white';
|
||||
new Draggable( {
|
||||
handle: dom,
|
||||
target: dom,
|
||||
mode: 'position',
|
||||
})
|
||||
|
||||
this.desktopRootDom.appendChild(dom);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user