侧滑菜单作为一种常见的 UI 交互模式,广泛应用于各种网站和移动应用中。今天,我们就来一步步解析如何使用 JavaScript 创建一个功能完备、易于使用的侧滑抽屉菜单插件。
一、插件目标
我们希望实现的侧滑菜单插件具备以下功能:
- 平滑的动画效果: 菜单的滑入和滑出都应有流畅的过渡效果。
- 自定义滑出方向: 可以设置菜单从左侧或右侧滑出。
- 外部点击关闭: 点击菜单区域以外的地方,菜单会自动关闭。
- 按钮切换动画: 菜单按钮可以设置点击时的动画效果。
- 易于集成: 插件应易于集成到现有的项目中,并提供简单的 API 进行调用。
二、HTML 结构
首先,我们需要在 HTML 中定义菜单按钮和菜单内容的结构:
<button class="drawer-toggle" data-target="#my-drawer" data-side="left">
<span class="drawer-icon">☰</span> 打开菜单
</button>
<div id="my-drawer" class="drawer">
<button class="drawer-close">×</button>
<ul>
<li><a href="#">菜单项 1</a></li>
<li><a href="#">菜单项 2</a></li>
<li><a href="#">菜单项 3</a></li>
</ul>
</div>
drawer-toggle
:菜单按钮的类名,用于绑定点击事件。data-target
:指定要控制的菜单容器的 ID。data-side
:指定菜单滑出的方向,可选值left
或right
。drawer
:菜单容器的类名,用于设置样式和控制显示状态。drawer-close
:菜单关闭按钮的类名。
三、CSS 样式
我们可以使用 CSS 来定义菜单的外观和动画效果:
.drawer-toggle {
/* 自定义按钮样式 */
position: relative;
background-color: #4CAF50;
color: white;
padding: 10px 15px;
border: none;
cursor: pointer;
}
.drawer {
position: fixed;
top: 0;
width: 250px;
height: 100%;
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
transition: transform 0.3s ease;
z-index: 100;
}
.drawer.left {
left: -250px; /* 初始时隐藏在左侧 */
}
.drawer.right {
right: -250px; /* 初始时隐藏在右侧 */
}
.drawer.open {
transform: translateX(250px);
}
.drawer.open.right {
transform: translateX(-250px);
}
.drawer-close {
/* 自定义关闭按钮样式 */
position: absolute;
top: 10px;
right: 10px;
background-color: transparent;
border: none;
font-size: 20px;
cursor: pointer;
}
- 我们使用
transform: translateX()
来实现菜单的滑入和滑出效果。 transition
属性确保动画过渡平滑。.drawer.left
和.drawer.right
分别定义了菜单从左侧和右侧滑出的初始状态。
四、 JavaScript 实现
现在,我们来编写 JavaScript 代码来实现插件的逻辑:
/**
* @description 创建一个侧滑抽屉菜单插件
* @name Drawer
* @version 1.0.2
*/
(function (window, document, undefined) {
'use strict';
// CSS 样式内容
const drawerCSS = `
/* ... 上面定义的 CSS 样式 ... */
`;
// 插件构造函数
function Drawer(options) {
this.options = {
side: 'left',
openClass: 'open',
toggleAnimation: 'rotate(90deg)',
duration: 300
};
// 合并自定义选项
for (let key in options) {
if (this.options.hasOwnProperty(key)) {
this.options[key] = options[key];
}
}
// 初始化
this.init();
}
// 初始化方法
Drawer.prototype.init = function () {
this.addCSS(); // 添加 CSS 样式
this.cacheDOM(); // 缓存 DOM 元素
this.setInitialSide(); // 设置初始滑出方向
this.bindEvents(); // 绑定事件
};
// 添加 CSS 样式
Drawer.prototype.addCSS = function () {
// 判断是否已存在相同内容的 style 标签
if (!document.querySelector(`style[data-drawer-css]`)) {
const style = document.createElement('style');
style.setAttribute('data-drawer-css', '');
style.innerHTML = drawerCSS;
document.head.appendChild(style);
}
};
// 缓存 DOM 元素
Drawer.prototype.cacheDOM = function () {
this.toggleButtons = document.querySelectorAll('.drawer-toggle');
this.drawers = document.querySelectorAll('.drawer');
};
//在 drawerCSS 中,我们为 .drawer.left 和 .drawer.right 添加了默认的隐藏样式:
//.drawer.left 的 left 属性设置为 -250px,使其隐藏在屏幕左侧外部。
//.drawer.right 的 right 属性设置为 -250px,使其隐藏在屏幕右侧外部。
//新增 setInitialSide 方法,在初始化时为每个抽屉菜单添加对应的 left 或 right 类,确保初始状态下是隐藏的。
// 设置初始滑出方向
Drawer.prototype.setInitialSide = function () {
this.drawers.forEach(drawer => {
drawer.classList.add(this.options.side);
});
};
// 绑定事件
Drawer.prototype.bindEvents = function () {
const self = this;
this.toggleButtons.forEach(button => {
button.addEventListener('click', function (event) {
// 阻止事件冒泡,防止触发 document 的点击事件
event.stopPropagation();
const targetId = this.dataset.target;
const targetDrawer = document.querySelector(targetId);
self.toggleDrawer(targetDrawer);
self.toggleAnimation(this);
});
});
this.drawers.forEach(drawer => {
const closeButton = drawer.querySelector('.drawer-close');
if (closeButton) {
closeButton.addEventListener('click', function (event) {
// 阻止事件冒泡
event.stopPropagation();
self.closeDrawer(drawer);
});
}
//添加全局事件,点击drawer以外的区域关闭 Drawer菜单
document.addEventListener('click', function (event) {
if (!drawer.contains(event.target)) {
self.closeDrawer(drawer);
}
});
});
};
// 切换抽屉菜单状态
Drawer.prototype.toggleDrawer = function (drawer) {
drawer.classList.toggle(this.options.openClass);
};
// 关闭抽屉菜单
Drawer.prototype.closeDrawer = function (drawer) {
drawer.classList.remove(this.options.openClass);
};
// 切换按钮动画
Drawer.prototype.toggleAnimation = function (button) {
button.style.transition = `transform ${this.options.duration}ms ease`;
button.style.transform = button.style.transform === this.options.toggleAnimation ? '' : this.options.toggleAnimation;
};
// 将 Drawer 构造函数暴露到全局作用域
window.Drawer = Drawer;
})(window, document);
- 插件封装: 我们使用 IIFE (Immediately Invoked Function Expression) 将代码封装成一个独立的插件,避免污染全局作用域。
- 构造函数:
Drawer
构造函数用于创建新的抽屉菜单实例,并接受一个可选的options
对象,用于自定义插件的行为。 - 初始化:
init
方法用于初始化插件,包括添加 CSS 样式、缓存 DOM 元素、设置初始滑出方向以及绑定事件。 - 事件处理:
bindEvents
方法用于绑定按钮点击事件和文档点击事件,实现菜单的打开、关闭以及外部点击关闭功能。 - 在
bindEvents
方法中,我们为document
添加了一个点击事件监听器。 - 在事件处理函数中,我们使用
event.target
获取到点击的目标元素,并使用drawer.contains(event.target)
判断目标元素是否在当前drawer
元素内。 - 如果目标元素不在
drawer
元素内,则调用closeDrawer
方法关闭抽屉菜单。 - 现在,当您点击 Drawer 以外的区域时,抽屉菜单就会自动关闭了!
- 动画控制:
toggleAnimation
方法用于控制按钮的动画效果,toggleDrawer
和closeDrawer
方法用于控制菜单的打开和关闭。
五、使用方法
在 HTML 文件中引入 JavaScript 文件后,我们可以创建一个新的 Drawer
实例来使用插件:
<script src="drawer.js"></script>
<script>
// 创建一个新的抽屉菜单实例,内部还可以添加自定义参数new Drawer({side: 'left'})
var myDrawer = new Drawer();
</script>
六、总结
通过以上步骤,我们就完成了一个功能完善、易于使用的 JavaScript 侧滑抽屉菜单插件。你可以根据自己的需求修改代码,自定义样式和动画效果,打造独一无二的交互体验。