1 开发目标
工具栏组件旨在模拟常见的桌面软件工具栏,所以比较适用于 electron 的开发,该组件包含工具栏按钮、工具栏分割条和工具栏容器三个主要角色,并提供一系列接口和功能,以满足用户在不同场景下的需求:
点击工具栏按钮后,可以回调用户传入的函数:
2 详细需求
2.1 工具栏按钮
(1)图标与标题: 工具栏按钮应包含一个可自定义的小图标和一个标题。图标和标题应清晰可见,且能够准确反映按钮的功能。
(2)点击动作: 用户点击工具栏按钮后,应触发相应的动作或事件。具体动作或事件由开发者在实现时定义。
(3)禁用接口: 工具栏按钮应提供禁用接口。当调用该接口后,按钮应变为禁用状态,具体表现为:
- 小图标和标题变灰,以区分于正常状态的按钮。
- 鼠标在禁用状态的按钮上移动时,不会变成手型图标。
- 点击禁用状态的按钮不会触发任何动作或事件。
2.2 工具栏分割条
(1)视觉表现: 工具栏分割条应具有明确的视觉表现,用于区分不同组别的工具栏按钮。
(2)位置与布局: 工具栏分割条应位于一组工具栏按钮的两侧,帮助用户清晰地识别按钮组。
2.3 工具栏容器
(1)保存与渲染: 工具栏容器应负责保存工具栏按钮和工具栏分割条,并根据布局规则进行渲染,确保界面美观且易用。
(2)对外接口: 工具栏容器应提供一系列对外接口,以便开发者进行动态操作,如:
- 动态增加一个工具栏按钮:提供接口允许开发者在运行时向工具栏容器中添加新的工具栏按钮。
- 动态移除一个工具栏按钮:提供接口允许开发者在运行时从工具栏容器中移除已存在的工具栏按钮。
- 动态调整工具栏按钮的位置或顺序:提供接口允许开发者在运行时调整工具栏按钮的布局。
3 代码实现
首先创建一个 neat_toolbar.js 文件,该文件用于本组件的工具类、目录处理函数的代码构建。
(1)在具体的业务代码编写之前,先实现一个工具类以及一些工具方法,方便后面调用:
class CommonUtil {
// 设置 DIV 中的文字为水平与垂直居中
static centerTextInDiv(container) {
container.style.display = 'flex';
container.style.textAlign = 'center';
container.style.justifyContent = 'center';
container.style.flexDirection = 'column';
}
// 设置 DIV 中的文字为垂直居中
static centerYTextInDiv(container) {
container.style.display = 'flex';
container.style.justifyContent = 'center';
container.style.flexDirection = 'column';
}
// 禁止文字选中
static disableSelection(container) {
if (typeof container.onselectstart != 'undefined') {
container.onselectstart = function () { return false; };
} else if (typeof container.style.MozUserSelect != 'undefined') {
container.style.MozUserSelect = 'none';
} else {
container.onmousedown = function () { return false; };
}
container.style.cursor = 'default';
}
// 设置 DIV 为带图标的 label
static setIconLabelInDiv(container, title, para) {
container.style.display = 'flex';
if (para.icon) {
let divIcon = document.createElement('div');
divIcon.style.height = '100%';
if (para.iconWidth) {
divIcon.style.width = para.iconWidth;
}
if (para.iconColor) {
divIcon.style.color = para.iconColor;
}
if (para.iconSize) {
divIcon.style.fontSize = para.iconSize;
}
container.appendChild(divIcon);
CommonUtil.centerTextInDiv(divIcon);
divIcon.innerHTML = '<i class="' + para.icon + '"></i>';
}
let divTitle = document.createElement('div');
divTitle.style.height = '100%';
if (para.fontSize) {
divTitle.style.fontSize = para.fontSize;
}
container.appendChild(divTitle);
CommonUtil.centerYTextInDiv(divTitle);
divTitle.innerHTML = title;
CommonUtil.disableSelection(container);
}
}
(2)创建工具栏按钮类型:
class NeatToolBarItem {
static NEATTOOLBARITEM_FONTSIZE = '14px'; // 工具栏按钮标题字体大小
static NEATTOOLBARITEM_ICON_WIDTH = '30px'; // 工具栏按钮图标宽度
static NEATTOOLBARITEM_FONT_COLOR = '#000'; // 工具栏按钮标题字体颜色
static NEATTOOLBARITEM_BACKGROUNDCOLOR = '#ffffff'; // 工具栏按钮默认背景颜色
static NEATTOOLBARITEM_BACKGROUNDCOLOR_MOVE = '#e5f3ff'; // 鼠标划过时的颜色
static NEATTOOLBARITEM_BACKGROUNDCOLOR_MOUSEDOWN = '#cce8ff'; // 鼠标按下时的颜色
static NEATTOOLBARITEM_COLOR_DISABLE = '#888888'; // 工具栏按钮禁用时的颜色
static NEATTOOLBARITEM_COLOR_SPLITTER = '#dddddd'; // 工具栏分割条颜色
constructor(container, title, para) {
this.container = container; // 本工具栏按钮的容器
this.title = title; // 本工具栏按钮的标题
this.para = para; // 配置参数,包含图标(为简便起见,使用font awesome)各种属性
this.disableFlag = false; // 本工具栏按钮是否被禁用
this.render();
}
上面代码定义了 NeatToolBarItem 的一些默认属性与成员变量,并且创建构造函数,该函数接收调用者传入的 DIV 容器,并且调用 render 方法。
在 render 方法,需要渲染当前工具栏按钮,并且还要控制鼠标动作事件:
render() {
this.container.style.height = '100%';
this.container.style.paddingRight = '10px';
this.para.fontSize = NeatToolBarItem.NEATTOOLBARITEM_FONTSIZE;
CommonUtil.setIconLabelInDiv(this.container, this.title, this.para);
this.container.style.cursor = 'pointer';
// 监听鼠标滑过事件
this.container.addEventListener('mouseover', function () {
// 当鼠标滑过时,改变背景颜色
if (!this.disableFlag) {
this.container.style.backgroundColor = NeatToolBarItem.NEATTOOLBARITEM_BACKGROUNDCOLOR_MOVE;
}
}.bind(this));
// 监听鼠标滑出事件
this.container.addEventListener('mouseout', function () {
// 当鼠标滑出时,改变背景颜色
if (!this.disableFlag) {
this.container.style.backgroundColor = NeatToolBarItem.NEATTOOLBARITEM_BACKGROUNDCOLOR;
}
}.bind(this));
// 监听鼠标按下事件
this.container.addEventListener('mousedown', function () {
// 当鼠标按下时,改变背景颜色
if (!this.disableFlag) {
this.container.style.backgroundColor = NeatToolBarItem.NEATTOOLBARITEM_BACKGROUNDCOLOR_MOUSEDOWN;
}
}.bind(this));
// 监听鼠标释放事件来恢复颜色
this.container.addEventListener('mouseup', function () {
// 当鼠标释放时,恢复背景颜色
if (!this.disableFlag) {
this.container.style.backgroundColor = NeatToolBarItem.NEATTOOLBARITEM_BACKGROUNDCOLOR_MOVE;
}
}.bind(this));
// 处理点击回调函数
if(this.para.onClick){
this.container.addEventListener('click', this.para.onClick);
}
}
接下来需要定义工具栏按钮的启用和禁用状态,主要通过状态变量 disableFlag 来控制:
// 启用
setEnable() {
this.container.style.color = NeatToolBarItem.NEATTOOLBARITEM_FONT_COLOR;
if (this.para.iconColor) {
this.container.childNodes[0].style.color = this.para.iconColor;
}
this.disableFlag = false;
this.container.style.cursor = 'pointer';
}
// 禁用
setDisable() {
this.container.style.color = NeatToolBarItem.NEATTOOLBARITEM_COLOR_DISABLE;
this.container.childNodes[0].style.color = NeatToolBarItem.NEATTOOLBARITEM_COLOR_DISABLE;
this.disableFlag = true;
this.container.style.cursor = 'default';
}
}
(3)创建工具栏类型:
class NeatToolBar {
constructor(container) {
this.container = container; // 本工具栏的容器
this.items = [];
this.render();
}
render() {
this.container.style.display = 'flex';
}
// 增加工具栏按钮
addToolbarItem(title, para) {
let itemContainer = document.createElement('div');
this.container.appendChild(itemContainer);
let item = new NeatToolBarItem(itemContainer, title, para);
this.items.push(item);
return item;
}
// 增加工具栏分割条
addSplitter() {
let splitterContainer = document.createElement('div');
splitterContainer.style.margin = '2px';
splitterContainer.style.border = '1px solid ' + NeatToolBarItem.NEATTOOLBARITEM_COLOR_SPLITTER;
this.container.appendChild(splitterContainer);
}
}
这是一个基础版的工具栏类,提供了添加按钮和分割条的基本功能。其包含两个核心方法:addToolbarItem(title, para) 以及 addSplitter()。
addToolbarItem(title, para) 主要作用在于:
- 创建一个新的 div 元素 itemContainer 作为工具栏按钮的容器。
- 将 itemContainer 添加到 container 中。
- 创建一个新的 NeatToolBarItem 实例并将其添加到 items 数组中。
- 返回新创建的 NeatToolBarItem 实例。
addSplitter() 主要作用在于:
- 创建一个新的 div 元素 splitterContainer 作为工具栏分割条的容器。
- 为 splitterContainer 设置样式,包括外边距(margin)和边框(border)。
- 将 splitterContainer 添加到 container 中。
(4)完成工具栏组件的代码编写后,可以创建 neater_toolbar.html 文件,调用工具栏组件::
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>header tab</title>
<style>
html {
height: 100%;
}
body {
margin: 0;
height: 100%;
}
</style>
<link rel="stylesheet" href="./font-awesome-4.7.0/css/font-awesome.css">
</head>
<body>
<div id="divMain" style="height: 100%;width: 100%;">
<div id="divToolbar"
style="margin-top: 100px;margin-left: 100px; height: 30px;width: 600px;border: 1px solid #aaaaaa;"></div>
</div>
</body>
<script src="./neat_toolbar.js"></script>
<script>
let toolbar = new NeatToolBar(document.getElementById('divToolbar'));
let para = {
"icon": "fa fa-area-chart",
"iconWidth": "30px",
"iconColor": "#1e9f75",
"onClick": function () { alert('hello toolbar') }
}
let toolbarItemChart = toolbar.addToolbarItem('查看曲线', para);
para = {
"icon": "fa fa-bar-chart",
"iconWidth": "30px",
"iconColor": "#d9534f",
}
let toolbarItemBar = toolbar.addToolbarItem('查看柱状图', para);
toolbarItemBar.setDisable();
toolbar.addSplitter();
para = {
"icon": "fa fa-picture-o",
"iconWidth": "30px",
"iconColor": "#4f9fcf",
}
let toolbarItemPic = toolbar.addToolbarItem('打开图片', para);
toolbar.addSplitter();
para = {
"icon": "fa fa-info-circle",
"iconWidth": "30px",
"iconColor": "#777777",
}
let toolbarItemInfo = toolbar.addToolbarItem('关于', para);
</script>
</html>