欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于Vue的Drawer組件實(shí)現(xiàn)

 更新時(shí)間:2023年05月23日 09:28:59   作者:傑丶  
本文將從零實(shí)現(xiàn)一個(gè)Drawer抽屜組件,組件用 vue2 語法寫的,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

不知平時(shí)用慣了組件庫的小伙伴們會不會好奇那些通用組件到底是如何實(shí)現(xiàn)的,本文將從零實(shí)現(xiàn)一個(gè)Drawer抽屜組件,組件用 vue2 語法寫的,不過框架都是一通百通,我相信當(dāng)你熟知了其實(shí)現(xiàn)原理,用任何框架都可以信手拈來!

組件演示及文檔地址:https://wangjunjie000.github.io/jj-ui/#/component/drawer
github地址:github.com/wangjunjie000/jj-ui

前言

眾所周知,drawer組件是 Web 端項(xiàng)目中經(jīng)常要用到的組件,ElementUI 組件庫中也有此組件,為了熟知其實(shí)現(xiàn)原理,以及盡可能的定制化,所以花了點(diǎn)時(shí)間寫了一個(gè)。項(xiàng)目使用的vue版本為 2.6.10,vue-cli版本為 3.12.1,node版本為 14.17.5。因本人能力水平有限,如有錯(cuò)誤和建議,歡迎在評論區(qū)指出。若本篇文章有幫助到了您,不要吝嗇您的小手還請點(diǎn)個(gè)贊再走哦!

※注:本文代碼區(qū)域每行開頭的“+”表示新增,“-”表示刪除,“M”表示修改;代碼中的“...”表示省略。

組件說明

@property 為父組件傳給子組件props中的屬性,@event為 組件中觸發(fā)的事件函數(shù),@slot為組件中的插槽

  • @property {String} direction 彈出方向,btt:bottom to top。
  • @property {String, Number} size 窗體大小, 不是傳數(shù)字時(shí)必須傳百分比
  • @property {Boolean} visible 是否顯示drawer,默認(rèn)false不顯示
  • @property {String} title Drawer 的標(biāo)題,也可通過具名 slot (見下方slot)傳入,
  • @property {Boolean} append-to-body Drawer 自身是否插入至 body 元素上。默認(rèn)false
  • @property {Boolean} show-title 控制是否顯示 title 部分, 默認(rèn)為 true, 當(dāng)此項(xiàng)為 false 時(shí), title 屬性和插槽 均不生效
  • @event {Function} open 打開時(shí)的回調(diào)
  • @event {Function} close 關(guān)閉時(shí)的回調(diào)
  • @event {Function} opened 打開動(dòng)畫結(jié)束后的回調(diào)
  • @event {Function} closed 關(guān)閉動(dòng)畫結(jié)束后的回調(diào)
  • @slot {element} title 標(biāo)題部分的插槽

Drawer組件代碼

drawer.vue:

<template>
? <div
? ? @click.self="handleWrapperClick"
? ? class="base-drawer_wrapper"
? ? :style="{ zIndex: $JJUI.zIndex }"
? ? v-show="isShowBaseDrawer"
? >
? ? <div :class="`base-drawer base-drawer-${_uid}`" :style="drawerStyle">
? ? ? <header class="drawer_header" v-if="showTitle">
? ? ? ? <slot name="title">
? ? ? ? ? <span :title="title" class="title">{{ title }}</span>
? ? ? ? </slot>
? ? ? </header>
? ? ? <section class="drawer_body">
? ? ? ? <slot></slot>
? ? ? </section>
? ? </div>
? </div>
</template>
<script>
/**
?* @property {String} direction 彈出方向,btt:bottom to top。
?* @property {String, Number} ?size 窗體大小, 不是傳數(shù)字時(shí)必須傳百分比
?* @property {Boolean} visible 是否顯示drawer,默認(rèn)false不顯示
?* @property {String} title Drawer 的標(biāo)題,也可通過具名 slot (見下方slot)傳入,
?* @property {Boolean} append-to-body Drawer 自身是否插入至 body 元素上。默認(rèn)false
?* @property {Boolean} show-title 控制是否顯示 title 部分, 默認(rèn)為 true, 當(dāng)此項(xiàng)為 false 時(shí), title 屬性和插槽 均不生效
?* @event {Function} open 打開時(shí)的回調(diào)
?* @event {Function} close 關(guān)閉時(shí)的回調(diào)
?* @event {Function} opened 打開動(dòng)畫結(jié)束后的回調(diào)
?* @event {Function} closed 關(guān)閉動(dòng)畫結(jié)束后的回調(diào)
?* @slot {element} title 標(biāo)題部分的插槽
?*/
export default {
? name: 'jj-drawer',
? props: {
? ? direction: {
? ? ? type: String,
? ? ? default: 'btt',
? ? ? validator(val) {
? ? ? ? return ['ltr', 'rtl', 'ttb', 'btt'].includes(val)
? ? ? },
? ? },
? ? size: {
? ? ? type: [String, Number],
? ? ? default: '30%',
? ? },
? ? visible: {
? ? ? type: Boolean,
? ? ? default: false,
? ? },
? ? title: {
? ? ? type: String,
? ? },
? ? showTitle: {
? ? ? type: Boolean,
? ? ? default: true,
? ? },
? ? appendToBody: {
? ? ? type: Boolean,
? ? ? default: true,
? ? },
? },
? computed: {
? ? drawerStyle() {
? ? ? let obj = {}
? ? ? switch (this.direction) {
? ? ? ? case 'btt':
? ? ? ? ? obj.transform = 'translate3d(0, 100%, 0)'
? ? ? ? ? obj.bottom = 0
? ? ? ? ? break
? ? ? ? case 'ttb':
? ? ? ? ? obj.transform = 'translate3d(0, -100%, 0)'
? ? ? ? ? obj.top = 0
? ? ? ? ? break
? ? ? ? case 'ltr':
? ? ? ? ? obj.transform = 'translate3d(-100%, 0, 0)'
? ? ? ? ? obj.left = 0
? ? ? ? ? obj.width = this.computedSize
? ? ? ? ? break
? ? ? ? case 'rtl':
? ? ? ? ? obj.transform = 'translate3d(100%, 0, 0)'
? ? ? ? ? obj.right = 0
? ? ? ? ? break
? ? ? ? default:
? ? ? ? ? break
? ? ? }
? ? ? if (this.direction === 'btt' || this.direction === 'ttb') {
? ? ? ? obj.left = 0
? ? ? ? obj.height = this.computedSize
? ? ? ? obj.width = '100%'
? ? ? }
? ? ? if (this.direction === 'ltr' || this.direction === 'rtl') {
? ? ? ? obj.top = 0
? ? ? ? obj.width = this.computedSize
? ? ? ? obj.height = '100%'
? ? ? }
? ? ? return {
? ? ? ? ...obj,
? ? ? }
? ? },
? ? computedSize() {
? ? ? if (typeof this.size === 'number') {
? ? ? ? return this.size + 'px'
? ? ? } else {
? ? ? ? return this.size
? ? ? }
? ? },
? },
? data() {
? ? return {
? ? ? isShowBaseDrawer: false,
? ? ? drawerEle: null,
? ? }
? },
? watch: {
? ? visible: {
? ? ? handler(val) {
? ? ? ? // console.log(val, oldVal);
? ? ? ? // val 為true時(shí)展開,此時(shí)isShowBaseDrawer如果也為true就觸發(fā)不了展開動(dòng)畫,所以要重置為false
? ? ? ? if (val && this.isShowBaseDrawer) {
? ? ? ? ? this.isShowBaseDrawer = false
? ? ? ? }
? ? ? ? // console.log(this.$el);
? ? ? ? if (val && this.appendToBody) {
? ? ? ? ? document.body.appendChild(this.$el)
? ? ? ? }
? ? ? ? this.handleToogleShow(val)
? ? ? },
? ? },
? },
? mounted() {
? ? this.drawerEle = document.querySelector(`.base-drawer-${this._uid}`)
? ? this.handleTransitionend = this.handleTransitionend.bind(this)
? ? if (this.drawerEle) {
? ? ? this.drawerEle.addEventListener('transitionend', this.handleTransitionend)
? ? ? // 寫這個(gè)是為了在mounted時(shí)默認(rèn)展開
? ? ? if (this.visible) {
? ? ? ? if (this.appendToBody) {
? ? ? ? ? document.body.appendChild(this.$el)
? ? ? ? }
? ? ? ? this.handleToogleShow()
? ? ? }
? ? }
? },
? methods: {
? ? handleTransitionend(e) {
? ? ? e.stopPropagation()
? ? ? if (e.target.classList.contains('base-drawer')) {
? ? ? ? // console.log(this.visible)
? ? ? ? // 展開動(dòng)畫結(jié)束后
? ? ? ? if (this.visible) {
? ? ? ? ? this.$emit('opened')
? ? ? ? } else {
? ? ? ? ? this.isShowBaseDrawer = false
? ? ? ? ? this.$emit('closed')
? ? ? ? }
? ? ? }
? ? },
? ? handleWrapperClick() {
? ? ? this.$emit('update:visible', false)
? ? ? // 當(dāng)前處于展示狀態(tài)時(shí)才做隱藏操作
? ? ? if (this.visible && this.isShowBaseDrawer) {
? ? ? ? // console.log(this.visible, this.isShowBaseDrawer);
? ? ? ? this.handleToogleShow()
? ? ? }
? ? },
? ? handleToogleShow() {
? ? ? if (!this.drawerEle) {
? ? ? ? this.drawerEle = document.querySelector(`.base-drawer-${this._uid}`)
? ? ? }
? ? ? // 打開
? ? ? if (this.visible && !this.isShowBaseDrawer) {
? ? ? ? this.isShowBaseDrawer = true
? ? ? ? // 使用window.requestAnimationFrame(),因?yàn)樗梢园汛a推遲到下一次重繪之前執(zhí)行,而不是立即要求頁面重繪。
? ? ? ? window.requestAnimationFrame(() => {
? ? ? ? ? this.$emit('open')
? ? ? ? ? // 打開遮罩層
? ? ? ? ? this.$modal({ show: true, zIndex: this.$JJUI.zIndex - 1 })
? ? ? ? ? // 強(qiáng)制觸發(fā)瀏覽器重繪,不寫這句瀏覽器會合并繪制,不能觸發(fā)動(dòng)畫
? ? ? ? ? this.drawerEle.offsetWidth
? ? ? ? ? this.drawerEle.classList.remove(`fade_leave_${this.direction}`)
? ? ? ? ? this.drawerEle.classList.add(`fade_enter_${this.direction}`)
? ? ? ? })
? ? ? }
? ? ? // 關(guān)閉
? ? ? if (!this.visible && this.isShowBaseDrawer) {
? ? ? ? // 關(guān)閉遮罩層
? ? ? ? this.$modal({ show: false })
? ? ? ? this.drawerEle.classList.remove(`fade_enter_${this.direction}`)
? ? ? ? this.drawerEle.classList.add(`fade_leave_${this.direction}`)
? ? ? ? this.$emit('close')
? ? ? }
? ? },
? },
? destroyed() {
? ? // 如果DOM是插入到body的,組件銷毀時(shí)移除body中的元素
? ? if (this.appendToBody && this.$el && this.$el.parentNode) {
? ? ? this.$el.parentNode.removeChild(this.$el)
? ? }
? ? if (this.drawerEle) {
? ? ? this.drawerEle.removeEventListener(
? ? ? ? 'transitionend',
? ? ? ? this.handleTransitionend
? ? ? )
? ? }
? },
}
</script>
<style lang="scss" scoped>
.base-drawer_wrapper {
? position: fixed;
? top: 0;
? right: 0;
? bottom: 0;
? left: 0;
? overflow: hidden;
? margin: 0;
? .base-drawer {
? ? box-shadow: 0 8px 10px -5px rgba(0, 0, 0, 0.2),
? ? ? 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12);
? ? position: fixed;
? ? background-color: #fff;
? ? transition: transform 0.3s;
? ? display: flex;
? ? flex-direction: column;
? ? .drawer_header {
? ? ? padding: 20px 20px 0;
? ? ? margin-bottom: 30px;
? ? ? text-align: center;
? ? ? .title {
? ? ? }
? ? }
? ? .drawer_body {
? ? ? padding: 20px;
? ? ? flex: 1;
? ? ? overflow: auto;
? ? }
? ? &.fade_enter_btt {
? ? ? transform: translate3d(0, 0, 0) !important;
? ? }
? ? &.fade_leave_btt {
? ? ? transform: translate3d(0, 100%, 0) !important;
? ? }
? ? &.fade_enter_ttb {
? ? ? transform: translate3d(0, 0, 0) !important;
? ? }
? ? &.fade_leave_ttb {
? ? ? transform: translate3d(0, -100%, 0) !important;
? ? }
? ? &.fade_enter_ltr {
? ? ? transform: translate3d(0, 0, 0) !important;
? ? }
? ? &.fade_leave_ltr {
? ? ? transform: translate3d(-100%, 0, 0) !important;
? ? }
? ? &.fade_enter_rtl {
? ? ? transform: translate3d(0, 0, 0) !important;
? ? }
? ? &.fade_leave_rtl {
? ? ? transform: translate3d(100%, 0, 0) !important;
? ? }
? }
}
</style>

?drawer中的遮罩:函數(shù)式組件$modal()

項(xiàng)目目錄結(jié)構(gòu):@表示src目錄下

- /public
|- /src
? ? |- /plugins
? ? ? ? |- index.js
? ? ? ? |- /modal
? ? ? ? ? ? |- modal.vue
? ? ? ? ? ? |- index.js
? ? |- main.js

@/plugins/modal/modal.vue:

<template>
? <div class="base-modal" :style="{ zIndex: zIndex }" v-if="show"></div>
</template>
<script>
export default {
? data() {
? ? return {
? ? ? show: false,
? ? ? zIndex: this.$JJUI.zIndex - 1,
? ? }
? },
}
</script>
<style lang="scss" scoped>
.base-modal {
? position: fixed;
? left: 0;
? top: 0;
? width: 100%;
? height: 100%;
? opacity: 0.5;
? background: #000;
}
</style>

@/plugins/modal/index.js:

import Vue from 'vue'
import modal from './modal.vue'
const ModalConstructor = Vue.extend(modal)
let instanceArr = []
/**
?* 調(diào)用 this.$modal({ show: true, zIndex: this.zIndex - 1 }) 顯示遮罩,遮罩存在時(shí)再次調(diào)用 this.$modal() 會移除遮罩
?* @param {Object} options 可選
?* @returns
?*/
const modalFunc = (options) => {
? // 為show時(shí)創(chuàng)建
? if (options.show) {
? ? const instance = new ModalConstructor({
? ? ? data: options,
? ? }).$mount()
? ? instanceArr.push(instance)
? ? // 如果 $mount() 沒有提供 elementOrSelector 參數(shù),模板將被渲染為文檔之外的的元素 (可以理解為未掛載狀態(tài)的vue實(shí)例對象) ,并且你必須使用原生 DOM API 把它插入文檔中
? ? document.body.appendChild(instance.$el)
? ? return instance
? } else {
? ? const instance = instanceArr.pop()
? ? // 否則銷毀實(shí)例
? ? if (instance && instance.$el && instance.$el.parentNode) {
? ? ? instance.$el.parentNode.removeChild(instance.$el)
? ? }
? ? return instance
? }
}
export default modalFunc

注冊組件@/plugins/index.js:

// main.js 中引入此文件后,執(zhí)行 Vue.use(plugins) 時(shí)會執(zhí)行下方的 install 方法?
import modal from '@/plugins/modal'
export default {
??install(Vue) {
????Vue.prototype.$modal = modal
?
? }
}

@/main.js:

...
import plugins from '@/plugins'
Vue.use(plugins)
...

到此這篇關(guān)于基于Vue的Drawer組件實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Vue Drawer組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論