目录
写在开头
上期代码主要实现文字滚动效果,开发久了很多功能都是通过框架组件库来完成,但是如果组件满足不了开发需求,还需要开发人员手动封装组件,专门出这样一期文章,通过原生js实现一些特定功能,功能也比较简单,也是想借助这样一个简单的功能,然后来帮助大家了解我们JavaScript,在前端中的作用,另外也培养下我们的代码思维,那我们本次就通过由简单到复杂循序渐进,这份专栏中我们会带领大家用前端实现手风琴效果、js实现三级联动效果、视频网站中重磅推荐模块、图片放大镜、积分抽奖、拼图、无缝轮播图、图片瀑布流、读心术小游戏等等有趣的小功能,纯前端语言实现,都会陆续带给大家。
功能介绍
手风琴效果,垂直菜单,可内嵌子菜单,可以参考饿了么组件库的NavMenu 导航菜单
涉及功能
首先得有一个动画效果,让菜单展开时页面看起来很顺滑,单独写函数也能实现,但是调用的地方多了就不如封装一个函数,函数但凡封装起来,里面涉及的内容就非常多,这里面动画函数我们就简单写一下。
/**
*
* @param {fn} options
* form {number}
* to {number}
* totalMS {}
* duration {}
* onmove {fn}
* onend {fn}
*/
function createAnimate(options) {
var from = options.from; // 起始值
var to = options.to; // 结束值
var totalMS = options.totalMS || 1000; // 变化总时间
var duration = options.duration || 15; // 动画间隔时间
var times = Math.floor(totalMS / duration); // 变化的次数
var dis = (to - from) / times; // 每一次变化改变的值
var curTimes = 0; // 当前变化的次数
var timerId = setInterval(function () {
from += dis;
curTimes++; // 当前变化增加一次
if (curTimes >= times) {
// 变化的次数达到了
from = to; // 变化完成了
clearInterval(timerId); // 不再变化了
options.onend && options.onend();
}
// 无数的可能性,把form传进去方便后续
options.onmove && options.onmove(from);
}, duration);
}
简单来一个小功能测试一下功能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<style>
div{
font-size: 32px;
text-decoration: line-through;
color: #ccc;
}
</style>
<body>
<div>
原价:¥
<span>999.99</span>
</div>
<h1>
活动价:¥
<span>999.99</span>
</h1>
<script src="./index.js"></script>
<script>
var span = document.querySelector('h1 span');
//原价999.99,活动价格99.99
createAnimate({
from: 999,//初始价格
to: 99,//优惠价格
totalTime: 2000,//变化总时长
durection: 100,//动画间隔
onmove: function (n) {
span.innerHTML = n.toFixed(2);
},
onend: function () {
span.style.color = 'red';
},
});
</script>
</body>
</html>
页面创建
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>手风琴效果</title>
<link rel="stylesheet" href="./index.css">
</head>
<body>
<ul class="menu-container">
<!-- li.menu*4>(h2{菜单$}+(ul.submenu>li*4>{菜单$})) -->
<li class="menu">
<h2>菜单1</h2>
<ul class="submenu">
<li>菜单1</li>
<li>菜单2</li>
<li>菜单3</li>
<li>菜单4</li>
</ul>
</li>
<li class="menu">
<h2>菜单2</h2>
<ul class="submenu">
<li>菜单1</li>
<li>菜单2</li>
<li>菜单3</li>
<li>菜单4</li>
</ul>
</li>
<li class="menu">
<h2>菜单3</h2>
<ul class="submenu">
<li>菜单1</li>
<li>菜单2</li>
<li>菜单3</li>
<li>菜单4</li>
</ul>
</li>
<li class="menu">
<h2>菜单4</h2>
<ul class="submenu">
<li>菜单1</li>
<li>菜单2</li>
<li>菜单3</li>
<li>菜单4</li>
</ul>
</li>
</ul>
</body>
</html>
样式设置
.menu-container{
width: 200px;
margin: 0 auto;
line-height: 30px;
}
h2{
padding: 0;
margin: 0;
font-size: 100%;
font-weight: normal;
}
ul li{
margin: 0;
padding: 0;
list-style: none;
}
.menu-container h2 {
padding: 0 25px;
background-color: skyblue;
}
.submenu{
background-color: aliceblue;
height: 0;
overflow: hidden;
margin: 15px 0;
}
逻辑实现
// 交互
var titles = document.querySelectorAll('.menu h2'); // 获取所有的标题元素
var itemHeight = 30; // 每个子菜单的高度
var totalMS = 300; // 动画播放的总时长
for (var i = 0; i < titles.length; i++) {
titles[i].onclick = function () {
// 打开一个菜单,收起其他所有菜单
var beforeOpened = document.querySelector('.submenu[status=opened]');
if (beforeOpened) {
closeSubmenu(beforeOpened);
}
toggleSubmenu(this.nextElementSibling);
};
}
// 打开子菜单
function openSubmenu(subMenu) {
// 子菜单是有状态(关闭、打开、正在打开)
// 通过自定义属性status,判定它的状态
var status = subMenu.getAttribute('status');
if (status !== 'closed' && status) {
// 不是关闭状态
return; // 啥也不干
}
subMenu.setAttribute('status', 'playing');
// 将子菜单的高度从0变到?(子项数量*itemHeight)
createAnimate({
from: 0,
to: itemHeight * subMenu.children.length,
totalMS: totalMS,
onmove: function (n) {
subMenu.style.height = n + 'px';
},
onend: function () {
subMenu.setAttribute('status', 'opened');
},
});
}
// test
//var testMenu = document.querySelector('.submenu');
// 关闭子菜单
function closeSubmenu(subMenu) {
// 子菜单是有状态(关闭、打开、正在播放动画)
// 通过自定义属性status,判定它的状态
var status = subMenu.getAttribute('status');
if (status !== 'opened') {
// 不是打开状态
return; // 啥也不干
}
subMenu.setAttribute('status', 'playing');
// 将子菜单的高度从0变到?(子项数量*itemHeight)
createAnimate({
from: itemHeight * subMenu.children.length,
to: 0,
totalMS: totalMS,
onmove: function (n) {
subMenu.style.height = n + 'px';
},
onend: function () {
subMenu.setAttribute('status', 'closed');
},
});
}
// 切换子菜单
function toggleSubmenu(subMenu) {
var status = subMenu.getAttribute('status');
if (status === 'playing') {
// 正在打开,播放着动画呢
return;
} else if (status === 'opened') {
closeSubmenu(subMenu);
} else {
openSubmenu(subMenu);
}
}
标签:status,subMenu,菜单,function,附加,源码,var,手风琴,options From: https://blog.csdn.net/weixin_45840519/article/details/144409506代码量不多,主要是js部分实现逻辑略有复杂,基本上写出一个打开菜单函数,关闭菜单同理就能写出来。