# 布局初始化逻辑 **Referenced Files in This Document** - [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts) - [DesktopContainer.vue](file://src/ui/desktop-container/DesktopContainer.vue) - [IGridTemplateParams.ts](file://src/ui/types/IGridTemplateParams.ts) ## 目录 1. [简介](#简介) 2. [核心组件分析](#核心组件分析) 3. [生命周期与容器初始化](#生命周期与容器初始化) 4. [DOM查询机制与containerStr参数](#dom查询机制与containerstr参数) 5. [网格模板初始状态设计](#网格模板初始状态设计) 6. [ResizeObserver响应式尺寸监听](#resizeobserver响应式尺寸监听) 7. [图标重排逻辑](#图标重排逻辑) 8. [依赖关系图](#依赖关系图) ## 简介 `useDesktopContainerInit` 是一个 Vue 3 组合式 API Hook,用于在桌面容器组件挂载时初始化其布局系统。该 Hook 负责建立基于 CSS Grid 的动态网格布局,并通过 `ResizeObserver` 实现响应式行为,确保桌面图标能根据容器尺寸自动调整排列方式。 ## 核心组件分析 ### useDesktopContainerInit 功能概览 此 Hook 封装了桌面容器的核心初始化逻辑,包括: - 容器元素的 DOM 查询与绑定 - 网格布局参数的初始化与响应式管理 - 容器尺寸变化的监听与处理 - 桌面图标的持久化存储与位置管理 - 图标超出可视区域时的隐藏策略 **Section sources** - [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L14-L94) ## 生命周期与容器初始化 ### onMounted 中的初始化流程 `useDesktopContainerInit` 利用 Vue 的 `onMounted` 生命周期钩子,在组件挂载完成后立即执行容器初始化操作。这一时机确保了对应的 DOM 元素已经存在于页面中,可以安全地进行查询和观察。 在 `onMounted` 回调中,Hook 首先通过 `document.querySelector(containerStr)` 获取指定的容器元素引用,并将其赋值给局部变量 `container`。随后,立即调用 `ResizeObserver` 的 `observe` 方法开始监听该容器的尺寸变化。 当组件被卸载时,`onUnmounted` 钩子会确保清理工作正确执行:停止对容器的观察(`unobserve`)并断开 `ResizeObserver` 实例(`disconnect`),防止内存泄漏。 ```mermaid sequenceDiagram participant Component as "DesktopContainer组件" participant Hook as "useDesktopContainerInit" participant DOM as "浏览器DOM" participant Observer as "ResizeObserver" Component->>Hook : setup() Hook->>Hook : 初始化gridTemplate等响应式数据 Component->>Component : 渲染完成 Component->>Hook : onMounted触发 Hook->>DOM : querySelector(containerStr) DOM-->>Hook : 返回容器元素 Hook->>Observer : new ResizeObserver(callback) Hook->>Observer : observe(container) Note over Hook,Observer : 开始监听容器尺寸变化 loop 每次窗口或容器大小改变 Observer->>Observer : 触发回调 Observer->>Hook : 执行回调函数 Hook->>Hook : 重新计算gridTemplate参数 Hook->>Hook : 更新gridStyle end Component->>Component : 组件即将销毁 Component->>Hook : onUnmounted触发 Hook->>Observer : unobserve(container) Hook->>Observer : disconnect() Note over Hook,Observer : 清理资源,防止内存泄漏 ``` **Diagram sources** - [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L70-L85) **Section sources** - [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L70-L85) ## DOM查询机制与containerStr参数 ### containerStr 参数的作用 `containerStr` 是传递给 `useDesktopContainerInit` 函数的一个字符串参数,它代表一个 CSS 选择器。该选择器用于定位需要初始化的桌面容器 DOM 元素。 在当前实现中,`DesktopContainer.vue` 组件传入的值为 `.desktop-icons-container`,这是一个类选择器,指向模板中具有该类名的 `
` 元素。这种设计使得 Hook 具有良好的通用性,可以在不同的容器上复用,只需传入相应的选择器即可。 ```typescript // 在 DesktopContainer.vue 中的调用示例 const { appIconsRef, gridStyle, gridTemplate } = useDesktopContainerInit('.desktop-icons-container') ``` **Section sources** - [DesktopContainer.vue](file://src/ui/desktop-container/DesktopContainer.vue#L10-L13) - [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L14) ## 网格模板初始状态设计 ### gridTemplate 初始值的设计意图 `gridTemplate` 对象使用 Vue 的 `reactive` 函数创建,使其成为一个响应式对象。其初始状态的设计体现了以下几个关键考量: | 属性 | 默认值 | 设计意图 | |------|--------|----------| | `cellExpectWidth` | 90 | 预设每个单元格的理想宽度(像素),作为网格列宽的基础 | | `cellExpectHeight` | 110 | 预设每个单元格的理想高度(像素),作为网格行高的基础 | | `gapX` / `gapY` | 4 | 设置行列之间的间隙,提供视觉呼吸空间,避免图标紧贴 | | `colCount` / `rowCount` | 1 | 初始行列数设为1,表示最小的网格结构,将在首次ResizeObserver回调中被实际尺寸覆盖 | 这些默认值共同定义了一个合理的起始布局,即使在 `ResizeObserver` 回调执行前,也能保证界面有一个基本的、可预测的显示状态。 ```mermaid classDiagram class IGridTemplateParams { +readonly cellExpectWidth : number +readonly cellExpectHeight : number +cellRealWidth : number +cellRealHeight : number +gapX : number +gapY : number +colCount : number +rowCount : number } class useDesktopContainerInit { -container : HTMLElement -gridTemplate : IGridTemplateParams -gridStyle : ComputedRef -ro : ResizeObserver +useDesktopContainerInit(containerStr : string) : Object } useDesktopContainerInit --> IGridTemplateParams : "包含" ``` **Diagram sources** - [IGridTemplateParams.ts](file://src/ui/types/IGridTemplateParams.ts#L4-L20) - [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L16-L32) **Section sources** - [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L16-L32) - [IGridTemplateParams.ts](file://src/ui/types/IGridTemplateParams.ts#L4-L20) ## ResizeObserver响应式尺寸监听 ### 回调中的尺寸计算逻辑 `ResizeObserver` 的回调函数是实现响应式布局的核心。每当容器的尺寸发生变化时,该回调都会被触发,执行以下关键步骤: 1. **获取容器实际尺寸**:通过 `container.getBoundingClientRect()` 获取容器当前的精确几何信息。 2. **计算行列数量**:利用容器宽度和预设单元格宽度(含间隙)计算出应显示的列数和行数。 ```javascript colCount = Math.floor((width + gapX) / (expectWidth + gapX)) ``` 3. **计算实际单元格尺寸**:在确定了行列数后,重新分配容器内部空间,计算出每个单元格的实际宽高,确保网格完全填充容器且无多余空白。 ```javascript realWidth = (totalWidth - totalGapX) / colCount ``` 这种“期望尺寸 -> 计算行列 -> 反推实际尺寸”的模式,既保证了图标的相对大小一致性,又实现了完美的空间利用率。 **Section sources** - [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L45-L65) ## 图标重排逻辑 ### rearrangeIcons 函数的工作机制 当网格的行列数发生变化时,`watch` 监听器会触发 `rearrangeIcons` 函数,负责重新安排所有桌面图标的可见性和位置。 该函数的主要逻辑如下: 1. 创建一个 `Set` 来记录已被占用的网格坐标 `(x,y)`。 2. 遍历所有图标,优先将位于新网格范围内的图标保留在原位。 3. 对于超出新网格范围的图标,尝试在网格内寻找空闲位置进行安置。 4. 如果网格已满,则将无法放置的图标放入 `hideAppIcons` 数组,这些图标将被隐藏。 这确保了用户界面的连续性,尽可能保留用户的原有布局习惯。 ```mermaid flowchart TD A[开始重排图标] --> B{遍历所有图标} B --> C["检查(x,y)是否在maxCol/maxRow范围内"] C --> |是| D["检查该位置是否已被占用"] D --> |否| E["将图标加入appIcons数组
标记位置为已占用"] D --> |是| F["跳过,不添加"] C --> |否| G["将图标暂存到temp数组"] G --> H{遍历temp数组} H --> I["检查appIcons数组是否已满"] I --> |否| J["在网格中寻找第一个空位"] J --> K["将图标放入空位
标记位置为已占用"] K --> L["加入appIcons数组"] I --> |是| M["加入hideAppIcons数组"] M --> N[结束] L --> N E --> N F --> N ``` **Diagram sources** - [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L102-L156) **Section sources** - [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L102-L156) ## 依赖关系图 ```mermaid graph TB subgraph "UI层" DesktopContainerVue["DesktopContainer.vue"] AppIconVue["AppIcon.vue"] UseHook["useDesktopContainerInit.ts"] end subgraph "类型定义" IGridTemplate["IGridTemplateParams.ts"] IDesktopAppIcon["IDesktopAppIcon.ts"] end DesktopContainerVue --> UseHook : "调用" UseHook --> IGridTemplate : "导入接口" UseHook --> IDesktopAppIcon : "导入接口" DesktopContainerVue --> AppIconVue : "使用组件" UseHook --> AppIconVue : "通过返回值传递数据" ``` **Diagram sources** - [DesktopContainer.vue](file://src/ui/desktop-container/DesktopContainer.vue) - [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts) - [IGridTemplateParams.ts](file://src/ui/types/IGridTemplateParams.ts)