曾经有个梦想,那就是实现一个自己的桌面环境,我对gnome桌面是比较喜欢的,于是我打算复刻一个gnome的web版桌面页面,具有的功能包括workspace切换,桌面内应用的打开,键盘+鼠标实现类似switch gnome工作区,效果如下
桌面状态
工作区切换状态
应用程序小窗口[窗口实现layear聚焦提升]
带应用程序的缩略框
程序最大化
实现思路
我将该界面分为了如下几个区,分别是
-
WorkspaceContainer(存放workspace的容器)
-
Workspace: 主要的工作区,也是应用创建的目标位置
-
Application: 应用程序,可挂载页面或链接
- TopBar 应用顶部的导航栏,负责关闭,最大化,最小化页面
-
TopBar: 顶部导航栏,展示工作区的位置和时间信息,顶部导航栏分为了left right content,采用响应式布局进行分区,每个区需要单独实现组件,方便管理.
对于应用的管理则是采用了redux状态管理,应用则是根据我定义的JSON进行序列化,每个工作区都会根据自己的工作区id创建和管理属于自己的应用程序
应用程序状态的抽象
一个应用程序主要包括一下属性,
- 程序所在的工作区,这也是为什么每个工作区可以单独渲染应用的原因
- 应用名称
- 最大化标志位
- 是否展示在顶层
- 创建的唯一标识,通过id可以精确销毁应用
- 窗口的尺寸信息,包括宽度,高度等,如果后期需要也能记录位置信息
- 窗口的所在层级,如果需要实现聚焦展示第一层,则需要对json进行遍历并去获取当前最大zIndex,并设置Zindex+1,提升需要展示在第一层的应用
export interface IAppContainer {
workspace: string | number; // 工作区
appName?: string; // app名称
big: 0 | 1; // 是否最大化
showTop?: 0 | 1; // 是否显示在第一层
id: string | number; // 唯一标识
width?: string; // 窗口宽度
height?: string; // 窗口高度
zIndex: 1; // 窗口层级
}
状态管理方法
抽象了app的属性,那么我们需要对这些属性进行操作,我们需要提供对程序内容数组的增删改查进行设计,包括
- 根据id删除窗口
- 根据id最大化窗口
- 根据id取消最大化
- 根据id和工作区id设置窗口对象
- 根据id最大化窗口
- 添加一个App应用数据
interface InitialState {
appContainer: IAppContainer[];
}
const initialState: InitialState = {
appContainer: [],
};
const counterSlice = createSlice({
name: "container",
initialState,
reducers: {
/**
* 根据id删除窗口
* @param state // 状态
* @param prop // 参数
*/
removeById(state: InitialState, prop) {
const index = state.appContainer.findIndex(
(item) => item.id === prop.payload.id && item.workspace === prop.payload.workspace
);
console.log(index);
if (-1 !== index) {
state.appContainer.splice(index, 1);
}
},
/**
* 根据id最大化窗口
* @param state
* @param prop
*/
big1ById(state: InitialState, prop) {
const obj = state.appContainer.find(
(item) => item.id === prop.payload.id && item.workspace === prop.payload.workspace
);
if (obj) {
obj.big = 1;
}
},
/**
* 根据id取消最大化
* @param state
* @param prop
*/
big0ById(state: InitialState, prop) {
const obj = state.appContainer.find(
(item) => item.id === prop.payload.id && item.workspace === prop.payload.workspace
);
if (obj) {
obj.big = 1;
}
},
/**
* 根据id和工作区id设置窗口对象
* @param state
* @param prop
*/
setObjByIdAndWorkspaceId(state: InitialState, prop) {
const obj = state.appContainer.find(
(item) => item.id === prop.payload.id && item.workspace === prop.payload.workspace
);
if (obj) {
obj.big = prop.payload.big;
obj.height = prop.payload.height;
obj.id = prop.payload.id;
obj.width = prop.payload.width;
obj.workspace = prop.payload.workspace;
obj.zIndex = prop.payload.zIndex;
}
},
/**
* 根据id切换最大化窗口
* @param state
* @param prop
*/
toggleBigById(state: InitialState, prop) {
console.log(prop);
const obj = state.appContainer.find(
(item) => item.id === prop.payload.id && item.workspace === prop.payload.workspace
);
if (obj) {
obj.big = obj.big ? 0 : 1;
}
},
/**
* 添加一个app对象
* @param state
* @param prop
*/
add(state: InitialState, prop: { payload: IAppContainer; type: string }) {
const obj = state.appContainer.find(
(item) => item.id === prop.payload.id && prop.payload.workspace === item.workspace
);
if (!obj) {
state.appContainer.push(prop.payload);
}
},
},
});
// 控制alt标志位
export const { removeById, toggleBigById, add, setObjByIdAndWorkspaceId } = counterSlice.actions;
export default counterSlice.reducer;