保存一下
This commit is contained in:
@@ -84,6 +84,7 @@ export class DesktopProcess extends ProcessImpl {
|
|||||||
dom.style.width = `${this._width}px`
|
dom.style.width = `${this._width}px`
|
||||||
dom.style.height = `${this._height}px`
|
dom.style.height = `${this._height}px`
|
||||||
dom.style.position = 'relative'
|
dom.style.position = 'relative'
|
||||||
|
dom.style.overflow = 'hidden'
|
||||||
this._desktopRootDom = dom
|
this._desktopRootDom = dom
|
||||||
|
|
||||||
const app = createApp(DesktopComponent, { process: this })
|
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 { IWindowFormConfig } from '@/core/window/types/IWindowFormConfig.ts'
|
||||||
import type { WindowFormPos } from '@/core/window/types/WindowFormTypes.ts'
|
import type { WindowFormPos } from '@/core/window/types/WindowFormTypes.ts'
|
||||||
import { processManager } from '@/core/process/ProcessManager.ts'
|
import { processManager } from '@/core/process/ProcessManager.ts'
|
||||||
|
import { Draggable } from '@/core/utils/Draggable.ts'
|
||||||
|
|
||||||
export default class WindowFormImpl implements IWindowForm {
|
export default class WindowFormImpl implements IWindowForm {
|
||||||
private readonly _id: string = uuidV4();
|
private readonly _id: string = uuidV4();
|
||||||
@@ -45,6 +46,11 @@ export default class WindowFormImpl implements IWindowForm {
|
|||||||
dom.style.height = `${this.height}px`;
|
dom.style.height = `${this.height}px`;
|
||||||
dom.style.zIndex = '100';
|
dom.style.zIndex = '100';
|
||||||
dom.style.backgroundColor = 'white';
|
dom.style.backgroundColor = 'white';
|
||||||
|
new Draggable( {
|
||||||
|
handle: dom,
|
||||||
|
target: dom,
|
||||||
|
mode: 'position',
|
||||||
|
})
|
||||||
|
|
||||||
this.desktopRootDom.appendChild(dom);
|
this.desktopRootDom.appendChild(dom);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user