乐趣区

关于vue.js:elementui源码学习之仿写一个eldrawer

本篇文章记录仿写一个 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 上哦(还有其余笔者封装的组件)
退出移动版