本篇文章记录仿写一个
el-drawer
组件细节,从而有助于大家更好了解饿了么 ui 对应组件具体工作细节。本文是 elementui 源码学习仿写系列的又一篇文章,后续闲暇了会不断更新并仿写其余组件。源码在 github 上,大家能够拉下来,npm start 运行跑起来,联合正文有助于更好的了解。github 仓库地址如下:https://github.com/shuirongsh…
什么是抽屉 drawer 组件
- 同弹框 dialog 组件相似,UI 展现略有不同
- 个别抽屉是左右防线弹出和发出,高低方向不多
- 可在抽屉外部进行代码补充操作
- 某些状况下,抽屉组件比弹框组件更加好用一些
笔者对于抽屉组件的封装,就不写太多的解析阐明了,大家能够间接复制粘贴代码,搭配代码中的正文进行应用(联合本人公司业务封装)
笔者的抽屉组件实现,抛砖引玉。实现次要罕用的性能,道友们能够进行思维发散
效果图
先看一下抽屉组件的效果图
代码
应用时的代码
<template>
<div>
<h4>isShowDrawer.sync 属性管制是否显示抽屉 </h4>
<h4>title 属性管制抽屉的头部题目 </h4>
<h4>direction 属性管制抽屉的 4 个方向 </h4>
<h4>beforeClose 函数属性敞开抽屉前的操作动作 </h4>
<h4>showCloseIcon 属性管制是否显示抽屉的敞开小按钮 </h4>
<h4>isShowHeader 属性管制是否显示抽屉的头部内容 </h4>
<h4>mask 属性管制是否显示抽屉的背景遮罩层 </h4>
<h4>slot="title" 具名插槽管制头部的题目内容 </h4>
<h4>clickMaskClose 属性管制是否可能点击背景遮罩层敞开抽屉 </h4>
<br />
<my-drawer
:isShowDrawer.sync="isShowDrawer1"
title="上方弹出 direction='top'"direction="top":beforeClose="handleClose":showCloseIcon="false"
></my-drawer>
<my-drawer
:isShowDrawer.sync="isShowDrawer2"
title="下方弹出"
direction="bottom"
:isShowHeader="false"
>
<h1>:isShowHeader="false" 去掉抽屉的头部内容 </h1>
<h1>:isShowHeader="false" 去掉抽屉的头部内容 </h1>
<h1>:isShowHeader="false" 去掉抽屉的头部内容 </h1>
<h1>:isShowHeader="false" 去掉抽屉的头部内容 </h1>
<h1>:isShowHeader="false" 去掉抽屉的头部内容 </h1>
<h1>:isShowHeader="false" 去掉抽屉的头部内容 </h1>
<h1>:isShowHeader="false" 去掉抽屉的头部内容 </h1>
<h1>:isShowHeader="false" 去掉抽屉的头部内容 </h1>
<h1>:isShowHeader="false" 去掉抽屉的头部内容 </h1>
<h1>:isShowHeader="false" 去掉抽屉的头部内容 </h1>
<h1>:isShowHeader="false" 去掉抽屉的头部内容 </h1>
</my-drawer>
<my-drawer
:isShowDrawer.sync="isShowDrawer3"
direction="left"
:mask="false"
>
<span slot="title"> 左侧命名插槽弹出哦 ^_^</span>
<span> 没有背景遮罩层 </span>
</my-drawer>
<my-drawer
:isShowDrawer.sync="isShowDrawer4"
direction="right"
:clickMaskClose="false"
>
<span slot="title"> 右侧命名插槽弹出哦 ^_^</span>
<span> 设置点击背景遮罩层不敞开,只能点击小箭头,或自定义按钮敞开 </span>
<br />
<br />
<br />
<br />
<el-button
@click="isShowDrawer4 = false"
type="success"
size="small"
plain
> 自定义敞开 </el-button
>
</my-drawer>
<el-button @click="topOpen" type="success" plain> 上方弹出 </el-button>
<el-button @click="bottomOpen" type="success" plain> 下方弹出 </el-button>
<el-button @click="leftOpen" type="success" plain> 左侧弹出 </el-button>
<el-button @click="rightOpen" type="success" plain> 右侧弹出 </el-button>
</div>
</template>
<script>
export default {data() {
return {
isShowDrawer1: false,
isShowDrawer2: false,
isShowDrawer3: false,
isShowDrawer4: false,
};
},
methods: {topOpen() {this.isShowDrawer1 = true;},
bottomOpen() {this.isShowDrawer2 = true;},
leftOpen() {this.isShowDrawer3 = true;},
rightOpen() {this.isShowDrawer4 = true;},
handleClose(close) {this.$confirm("确认敞开 close()函数敞开")
.then((_) => {close();
})
.catch((_) => {});
},
},
};
</script>
封装的抽屉组件代码
<template>
<!-- 抽屉关上敞开过渡成果依据 name 去指定 -->
<transition :name="computedName">
<!-- clickMaskCloseFn 搭配 @click.stop -->
<div
@click="clickMaskCloseFn"
class="myDrawerWrap"
:class="{isShowDrawerMask: mask}"
v-show="isShowDrawer"
>
<div
ref="drawerContentRef"
:class="['drawerContent']"
:style="computedDrawerPosition"
@click.stop
>
<header v-show="isShowHeader" class="drawerHeader">
<slot name="title">
<span>{{title}}</span>
</slot>
<i class="el-icon-close" @click="closeDrawer" v-show="showCloseIcon">
</i>
</header>
<section class="drawerBody">
<slot></slot>
</section>
</div>
</div>
</transition>
</template>
<script>
const directionArr = ["top", "bottom", "left", "right"]; // "ttb","btt","ltr","rtl"
const moveObj = {
top: "topMove",
bottom: "bottomMove",
left: "leftMove",
right: "rightMove",
};
export default {
name: "myDrawer",
props: {
// 是否显示抽屉
isShowDrawer: {
type: Boolean,
default: false,
},
// 是否显示抽屉头部内容
isShowHeader: {
type: Boolean,
default: true,
},
// 父组件传过来的抽屉题目值
title: {
type: String,
default: "我是 title",
},
// 是否显示敞开小图标
showCloseIcon: {
type: Boolean,
default: true,
},
// 是否开启抽屉背景遮罩层
mask: {
type: Boolean,
default: true,
},
// 点击遮罩层敞开默认为 true
clickMaskClose: {
type: Boolean,
default: true,
},
// 校验抽屉的 4 个方向
direction: {
type: String,
default: "right",
validator(val) {return directionArr.includes(val);
},
},
// 接管父组件传递过去的敞开函数,会中断敞开抽屉的操作
beforeClose: {type: Function,},
},
computed: {
// 动态控制上下左右的抽屉内容区的地位以及抽屉的宽度
computedDrawerPosition() {
let positionObj = {
width:
(this.direction == "left") | (this.direction == "right")
? "30%"
: "100%",
height:
(this.direction == "top") | (this.direction == "bottom")
? "30%"
: "100%",
};
positionObj[this.direction] = 0;
return positionObj;
},
// 动态控制抽屉从上下左右进入和退出
computedName() {return moveObj[this.direction]; // topMove、bottomMove、leftMove、rightMove
},
},
methods: {
// 点击遮罩层敞开弹框
clickMaskCloseFn() {if (this.clickMaskClose == true) {this.closeDrawer();
} else {
/* 这里要管制一下冒泡事件,留神第十行应用 @click.stop
不管制冒泡的话,点击内容区也会导致弹出框敞开 */
return;
}
},
// 筹备敞开抽屉弹出框
closeDrawer() {console.log(888);
// 若传递了 beforeClose 函数,就抛出敞开函数,供内部应用
if (this.beforeClose) {this.beforeClose(this.close);
}
// 没有 beforeClose 函数,间接敞开即可
else {this.close();
}
},
// 敞开抽屉弹出框
close() {this.$emit("update:isShowDrawer", false); // 敞开
this.$emit("shutDown"); // 并抛出一个 shutDown 告诉事件
},
},
};
</script>
<style lang='less' scoped>
// 根本款式
.myDrawerWrap {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 999;
overflow: hidden;
.drawerContent {
// 搭配定位的形式管制在上下左右的那个方位
position: absolute;
background-color: #fff;
box-shadow: 2px 2px 12px 0 rgba(0, 0, 0, 0.24);
display: flex;
flex-direction: column;
// 抽屉头部
.drawerHeader {
width: 100%;
height: 48px;
box-sizing: border-box;
padding: 12px;
display: flex;
align-items: center;
justify-content: space-between;
font-weight: bolder;
color: #333;
i {cursor: pointer;}
}
// 抽屉内容体局部
.drawerBody {
width: 100%;
box-sizing: border-box;
padding: 12px;
flex: 1;
overflow-y: auto;
}
}
}
// 遮罩层即为背景色
.isShowDrawerMask {background-color: rgba(0, 0, 0, 0.3);
}
/*
下方是抽屉过渡动画的重点
*/
// 上方进入和退出
.topMove-enter-active,
.topMove-leave-active {
transition: all 0.36s ease-in-out;
transform: translateY(0%);
opacity: 1;
}
.topMove-enter,
.topMove-leave {transform: translateY(-100%);
opacity: 0;
}
.topMove-leave-to {transform: translateY(-100%);
opacity: 0;
}
// 下方进入和退出
.bottomMove-enter-active,
.bottomMove-leave-active {
transition: all 0.36s ease-in-out;
transform: translateY(0);
opacity: 1;
}
.bottomMove-enter,
.bottomMove-leave {transform: translateY(100%);
opacity: 0;
}
.bottomMove-leave-to {transform: translateY(100%);
opacity: 0;
}
// 左侧进入和退出
.leftMove-enter-active,
.leftMove-leave-active {
transition: all 0.36s ease-in-out;
transform: translateX(0%);
opacity: 1;
}
.leftMove-enter,
.leftMove-leave {transform: translateX(-100%);
opacity: 0;
}
.leftMove-leave-to {transform: translateX(-100%);
opacity: 0;
}
// 右侧进入和退出
.rightMove-enter-active,
.rightMove-leave-active {
transition: all 0.36s ease-in-out;
transform: translateX(0);
opacity: 1;
}
.rightMove-enter,
.rightMove-leave {transform: translateX(100%);
opacity: 0;
}
.rightMove-leave-to {transform: translateX(100%);
opacity: 0;
}
</style>
总结
A bad pen is better than a good memory
- 残缺代码在 github 上哦(还有其余笔者封装的组件)