8.5 KiB
AppIcon组件
**Referenced Files in This Document ** - [AppIcon.vue](file://src/ui/desktop-container/AppIcon.vue) - [IDesktopAppIcon.ts](file://src/ui/types/IDesktopAppIcon.ts) - [IGridTemplateParams.ts](file://src/ui/types/IGridTemplateParams.ts) - [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts)目录
简介
AppIcon 组件是桌面应用系统中的可交互图标单元,实现了基于HTML5 Drag API的拖拽功能。该组件通过精确的坐标计算和网格系统集成,允许用户将图标重新定位到桌面容器的任意有效网格位置。本文档深入解析其技术实现细节,涵盖从拖拽事件处理、坐标换算、网格定位到状态持久化的完整流程。
Section sources
核心数据结构
IDesktopAppIcon 接口
定义了桌面图标的元数据结构,包含名称、图标资源、启动路径及在网格布局中的位置坐标。
export interface IDesktopAppIcon {
name: string; // 图标显示名称
icon: string; // 图标资源路径或标识符
path: string; // 关联的应用程序启动路径
x: number; // 在grid布局中的列索引(从1开始)
y: number; // 在grid布局中的行索引(从1开始)
}
该设计意图明确:x 和 y 字段直接映射到CSS Grid的grid-column和grid-row属性,实现了数据驱动的UI布局。
Section sources
拖拽交互机制
AppIcon 组件利用原生HTML5 Drag API实现拖拽功能:
- 启用拖拽:通过设置
draggable="true"属性激活元素的拖拽能力。 - 事件监听:
@dragstart:拖拽开始时触发,当前实现为空函数,保留未来扩展空间。@dragend:拖拽结束时触发,执行核心的坐标计算与位置更新逻辑。
此机制无需依赖第三方库,轻量且兼容性好。
Section sources
网格定位原理
动态样式绑定
组件通过内联样式动态设置其在CSS Grid中的位置:
:style="`grid-column: ${iconInfo.x}/${iconInfo.x + 1}; grid-row: ${iconInfo.y}/${iconInfo.y + 1};`"
此表达式将 iconInfo.x 和 iconInfo.y 的值转换为Grid的列/行跨度声明,确保图标精准占据一个网格单元。
坐标换算过程
onDragEnd 事件处理器的核心任务是将鼠标绝对坐标转换为相对网格坐标:
- 获取容器边界:使用
getBoundingClientRect()获取父容器的绝对位置和尺寸。 - 计算相对坐标:通过
e.clientX - rect.left和e.clientY - rect.top得到鼠标相对于容器左上角的偏移量。 - 确定目标网格:利用
cellRealWidth和cellRealHeight进行除法运算并向上取整(Math.ceil),得到目标网格的行列索引。
flowchart TD
A[拖拽结束] --> B{获取鼠标<br/>clientX/clientY}
B --> C{获取容器<br/>getBoundingClientRect}
C --> D[计算鼠标相对<br/>容器坐标]
D --> E[用cellRealWidth/Height<br/>计算网格索引]
E --> F[更新iconInfo.x/y]
F --> G[触发UI重渲染]
**Diagram sources **
Section sources
事件过滤逻辑
为了防止图标被错误地放置在其他图标之上,onDragEnd 方法实现了关键的事件过滤:
- 使用
document.elementFromPoint(e.clientX, e.clientY)检测鼠标终点位置的DOM元素。 - 如果该元素是另一个
.icon-container,则立即返回,不执行任何位置更新。
此逻辑确保了“空位投放”原则,提升了用户体验,避免了图标的视觉重叠。
Section sources
容器响应式布局
IGridTemplateParams 接口
该接口定义了网格系统的动态参数,是实现响应式布局的关键。
export interface IGridTemplateParams {
cellExpectWidth: number; // 单元格预设宽度
cellExpectHeight: number; // 单元格预设高度
cellRealWidth: number; // 单元格实际宽度
cellRealHeight: number; // 单元格实际高度
gapX: number; // 列间距
gapY: number; // 行间距
colCount: number; // 总列数
rowCount: number; // 总行数
}
实际尺寸计算
useDesktopContainerInit 函数通过 ResizeObserver 监听容器尺寸变化,并动态计算 cellRealWidth 和 cellRealHeight:
const w = containerRect.width - (gridTemplate.gapX * (gridTemplate.colCount - 1));
gridTemplate.cellRealWidth = Number((w / gridTemplate.colCount).toFixed(2));
此计算考虑了所有列间距的总和,确保了网格单元的实际尺寸能完美填充容器,无边距误差。
Section sources
图标重排策略
当网格的行列数发生变化时,rearrangeIcons 函数负责智能地重新分配图标位置:
- 优先保留原位:遍历所有图标,若其原位置在新网格范围内且未被占用,则保留在原位。
- 寻找空位:对于超出范围或位置冲突的图标,从
(1,1)开始扫描,为其寻找第一个可用的空网格。 - 隐藏溢出图标:如果所有网格均被占用,则将无法安置的图标放入
hideAppIcons数组。
此策略保证了用户自定义布局的最大程度保留,同时优雅地处理了空间不足的情况。
sequenceDiagram
participant DC as DesktopContainer
participant UDCI as useDesktopContainerInit
participant RI as rearrangeIcons
DC->>UDCI : 监听gridTemplate变化
UDCI->>RI : 调用rearrangeIcons()
loop 遍历每个图标
RI->>RI : 检查是否在新网格内且位置空闲
alt 是
RI->>RI : 保留在原位
else 否
RI->>RI : 加入临时队列
end
end
loop 处理临时队列
RI->>RI : 扫描网格寻找空位
alt 找到空位
RI->>RI : 分配新位置
else 无空位
RI->>RI : 加入hideAppIcons
end
end
RI-->>UDCI : 返回appIcons和hideAppIcons
UDCI->>DC : 更新appIconsRef
**Diagram sources **
Section sources
持久化存储
用户的图标布局偏好通过 localStorage 实现持久化:
- 写入:使用
watch监听appIconsRef.value的变化,一旦有更新,立即将整个数组序列化为JSON字符串并存入localStorage键名为'desktopAppIconInfo'的条目中。 - 读取:在初始化时,尝试从
localStorage中读取'desktopAppIconInfo',若存在则使用存储的位置信息覆盖默认布局。
这确保了用户关闭页面后再次打开时,仍能看到上次调整好的桌面布局。
Section sources
扩展与最佳实践
自定义图标样式
可通过修改 <style scoped> 中的 .icon-container 类来定制图标外观,例如添加背景图片、阴影或动画效果。
扩展拖拽行为
可在 onDragStart 回调中添加逻辑,如设置拖拽图像 (e.dataTransfer.setDragImage) 或传输自定义数据。
避免跨容器拖拽冲突
最佳方案:为每个可拖拽区域(容器)维护独立的 gridTemplate 状态和图标集合。在 onDragEnd 事件中,首先确认 pointTarget 是否属于当前容器的 .desktop-icons-container,如果不是,则忽略此次拖拽操作,防止图标被错误地投放到其他容器中。