本篇文章记录仿写一个
el-drawer
组件细节,然后有助于我们更好了解饿了么ui对应组件具体工作细节。本文是elementui源码学习仿写系列的又一篇文章,后续闲暇了会不断更新并仿写其他组件。源码在github上,我们可以拉下来,npm start运行跑起来,结合注释有助于更好的了解。github仓库地址如下:github.com/shuirongshu…
什么是抽屉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上哦(还有其他笔者封装的组件)