原生微信小程序,实现 下拉菜单 ,带遮罩层,出现隐藏动画(dropDownMenu思路总结)
实现效果
先上代码,下面开始做总结
.wxml 代码
<!-- 导航栏循环 -->
<view class="wrap">
<view
data-id="{{item.id}}"
bind:tap="handleClick"
wx:for="{{navItem}}"
class="item {{item.isActive?'active':''}}"
>
{{item.name}}
</view>
</view>
<!-- 选项循环 -->
<view class="selectWrap {{showOrHide?'slidown':'slidup'}}">
<!-- 每项 -->
<view
class="selectItem {{item.itemIsActive?'active':''}}"
wx:for="{{selectedItem}}"
data-contant="{{item.name}}"
bind:tap="closeMask"
>
{{item.name}}
</view>
</view>
<!-- 遮罩层 -->
<view class="{{showOrHide?'shadow':''}}" bind:tap="closeMask" />
<scroll-view class="numwrap" scroll-y="{{true}}">
<view>1</view>
<view>2</view>
<view>3</view>
<view>4</view>
<view>5</view>
<view>6</view>
<view>7</view>
<view>8</view>
<view>9</view>
<view>10</view>
<view>11</view>
<view>12</view>
<view>13</view>
<view>14</view>
<view>15</view>
<view>16</view>
<view>17</view>
<view>18</view>
<view>19</view>
<view>20</view>
<view>21</view>
<view>22</view>
<view>23</view>
<view>24</view>
<view>25</view>
<view>26</view>
<view>27</view>
<view>28</view>
<view>29</view>
<view>30</view>
<view>31</view>
<view>32</view>
<view>33</view>
<view>34</view>
<view>35</view>
<view>36</view>
<view>37</view>
<view>38</view>
<view>39</view>
<view>40</view>
<view>41</view>
<view>42</view>
<view>43</view>
<view>44</view>
<view>45</view>
<view>46</view>
<view>47</view>
<view>48</view>
<view>49</view>
<view>50</view>
</scroll-view>
.wxss代码(less 和 wxss 都发一下)
.wxss
/* pages/character/character.wxss */
page {
position: relative;
}
.wrap {
border-bottom: 1px solid #000;
background-color: #fff;
position: fixed;
color: #777777;
width: 100vw;
display: flex;
z-index: 100;
}
.wrap .item {
display: flex;
justify-content: center;
align-items: center;
padding: 20rpx;
flex: 1;
}
.wrap .active {
color: #F75194;
}
.selectWrap {
color: #777777;
position: absolute;
width: 100%;
top: 82rpx;
z-index: 90;
background-color: #fff;
}
.selectWrap .selectItem {
padding: 20rpx;
border-bottom: 1px solid #dddddd;
}
.selectWrap .active {
color: #F75194;
}
.shadow {
position: absolute;
top: 0;
width: 100%;
height: 100vh;
z-index: 4;
background-color: #4C4C4C;
opacity: 0.6;
}
.numwrap {
padding-top: 82rpx;
height: calc(100vh - 82rpx);
}
@keyframes slidown {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(0);
}
}
.slidown {
display: block;
animation: slidown 0.1s ease-in both;
}
@keyframes slidup {
from {
transform: translateY(0);
}
to {
transform: translateY(-100%);
}
}
.slidup {
display: block;
animation: slidup 0.1s ease-in both;
}
记得在 app.wxss 中配置一下默认样式
view,
swiper,
swiper-item,
navigator {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.less
/* pages/character/character.wxss */
page {
position: relative;
}
.wrap {
border-bottom: 1px solid #000;
background-color: #fff;
position: fixed;
color: #777777;
width: 100vw;
display: flex;
z-index: 100;
.item {
display: flex;
justify-content: center;
align-items: center;
padding: 20rpx;
flex: 1;
}
.active {
color: #F75194;
}
}
.selectWrap {
color: #777777;
position: absolute;
width: 100%;
top: 82rpx;
z-index: 90;
background-color: #fff;
.selectItem {
padding: 20rpx;
border-bottom: 1px solid #dddddd;
}
.active {
color: #F75194;
}
}
.shadow {
position: absolute;
top: 0;
width: 100%;
height: 100vh;
z-index: 4;
background-color: #4C4C4C;
opacity: 0.6;
}
.numwrap {
padding-top: 82rpx;
height: calc(100vh - 82rpx);
}
@keyframes slidown {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(0);
}
}
.slidown {
display: block;
animation: slidown 0.1s ease-in both;
}
@keyframes slidup {
from {
transform: translateY(0);
}
to {
transform: translateY(-100%);
}
}
.slidup {
display: block;
animation: slidup 0.1s ease-in both;
}
.js 文件
// pages/character/character.js
Page({
data: {
// 菜单栏原始数据
navItem: [
{
id: 0,
name: "初始星级",
isActive: false
},
{
id: 1,
name: "输出占位",
isActive: false
},
{
id: 2,
name: "攻击类型",
isActive: false
}
],
// 下拉选项原始数据
selectItem: [
{
id: 0,
rank: [
{
name: "初始星级",
itemIsActive: true
},
{
name: "初始一星",
itemIsActive: false
},
{
name: "初始二星",
itemIsActive: false
},
{
name: "初始三星",
itemIsActive: false
},
],
},
{
id: 1,
rank: [
// "输出占位", "前卫", "中卫", "后卫"
{
name: "输出占位",
itemIsActive: true
},
{
name: "前卫",
itemIsActive: false
},
{
name: "中卫",
itemIsActive: false
},
{
name: "后卫",
itemIsActive: false
}
],
},
{
id: 2,
rank: [
// "攻击类型", "魔法攻击", "物理攻击"
{
name: "攻击类型",
itemIsActive: true
},
{
name: "魔法攻击",
itemIsActive: false
},
{
name: "物理攻击",
itemIsActive: false
},
]
}
],
// 下拉选项,被选中那组的数据,由下面的js控制赋值
selectedItem: [],
// 记录菜单栏第几项被点开,方便对样式的绑定
listNum: -1,
// 下拉选项的隐藏和显示,默认隐藏
showOrHide: false
},
// 点击菜单栏触发的事件函数
handleClick: function (e) {
const index = e.currentTarget.dataset.id;
const { selectItem, navItem } = this.data;
// 点击事件,开始时一定会执行的,先令所有下拉选项先隐藏
const promise = new Promise((res) => {
this.setData({
showOrHide: false
})
res()
})
promise.then(() => {
// 定义一个延迟0.1秒的函数,和函数动画事件对上
setTimeout(() => {
// 当点击的是已经点开的菜单项,则隐藏
if (this.data.listNum === index) {
navItem[index].isActive = false
// 重置所有样式
this.setData({
navItem,
listNum: -1
})
return;
} else {
// 否则就令当前的index = listNum方便下一次判断
this.setData({
listNum: index
})
}
// 令当前点击的菜单栏高亮
navItem.forEach((v, i) => i === index ? v.isActive = true : v.isActive = false)
this.setData({
showOrHide: true,
selectedItem: selectItem[index].rank,
navItem
})
}, 100)
})
},
// 关闭下拉选项的函数
// 两种情况下会关闭下拉选项
// 1. 选择下拉选项,选择后被选的下拉选项高亮,关闭下拉选项列表
// 2. 点击遮罩层关闭
closeMask: function (e) {
let { navItem, listNum, selectedItem } = this.data;
// 获取点击的下拉选项内容
const { contant } = e.currentTarget.dataset
// 被点击的下拉选项高亮
selectedItem.forEach((v) => v.name === contant ? v.itemIsActive = true : v.itemIsActive = false)
// 判断点击的是遮罩层还是下拉选项,如果是下拉选项则把选择的内容赋值到导航栏上,并且把v.isActive = false,让菜单栏高亮消失
// 如果不是,不用赋值,直接把v.isActive = false,让菜单栏高亮消失
contant ? (navItem.forEach((v, i) => {
(i === listNum ? (v.name = contant) : (v.name = v.name));
v.isActive = false
})) : (navItem.forEach((v) => v.isActive = false))
this.setData({
listNum: -1,
showOrHide: false,
navItem,
selectedItem
})
}
})
下面开始 技术 和 思路 总结
( 1 ). wxml 部分基本上正常排版就行,后面通过 wxss 和 js都能实现功能,这部分比较灵活
( 2 ). wxss 部分需要注意的是
- 1 . 在全局 app.wxss 设置初始化样式 p0+m0+bz
- 2 . 设置菜单栏的
position: fixed;
固定定位display: flex;
弹性盒子,菜单栏的每个子项设置
display: flex; justify-content: center; align-items: center; padding: 20rpx; flex: 1;
使每项水平垂直居中,并且每项分配的距离相等 - 3 . 选择项的
position: absolute;
一定要设置为绝对定位,并设置背景颜色background-color: #fff;
如果不设置背景颜色就会出现这种情况
- 4 . 遮罩层设置,这里一定要设置的是
position: absolute;
宽高width: 100%; height: 100vh;
vh表示的是屏幕的视口高度(同理vw表示视口宽度),opacity: 0.6;
不设置透明度会导致下面的内容看不到 - 5 . 最重要的就是
z-index:
属性的设置,导航栏 > 选项栏 > 遮罩层,这三个地方都要设置z-index
属性,且层级大小一定要注意
( 3 ). js 部分,每个人思路不一样,但基本上都八九不离十,这里大体说一下我自己的思路
- 1 . 定义点击显示下拉选项函数
- 2 . 定义点击关闭下拉选项函数
( 这部分可以看js代码的注释,写的很清楚了,主要大量运用了 forEach 遍历数据,并对数据进行修改,熟练使用 三元表达式 )
代码gitee地址:https://gitee.com/chenminghuisir/wechat-applet-component
代码保存在仓库,PrincessConnect文件里