最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

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

前言

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

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

組件說明

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

  • @property {String} direction 彈出方向,btt:bottom to top。
  • @property {String, Number} size 窗體大小, 不是傳數(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)此項為 false 時, title 屬性和插槽 均不生效
  • @event {Function} open 打開時的回調(diào)
  • @event {Function} close 關(guān)閉時的回調(diào)
  • @event {Function} opened 打開動畫結(jié)束后的回調(diào)
  • @event {Function} closed 關(guān)閉動畫結(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ù)字時必須傳百分比
?* @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)此項為 false 時, title 屬性和插槽 均不生效
?* @event {Function} open 打開時的回調(diào)
?* @event {Function} close 關(guān)閉時的回調(diào)
?* @event {Function} opened 打開動畫結(jié)束后的回調(diào)
?* @event {Function} closed 關(guān)閉動畫結(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時展開,此時isShowBaseDrawer如果也為true就觸發(fā)不了展開動畫,所以要重置為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)
? ? ? // 寫這個是為了在mounted時默認(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)
? ? ? ? // 展開動畫結(jié)束后
? ? ? ? if (this.visible) {
? ? ? ? ? this.$emit('opened')
? ? ? ? } else {
? ? ? ? ? this.isShowBaseDrawer = false
? ? ? ? ? this.$emit('closed')
? ? ? ? }
? ? ? }
? ? },
? ? handleWrapperClick() {
? ? ? this.$emit('update:visible', false)
? ? ? // 當(dāng)前處于展示狀態(tài)時才做隱藏操作
? ? ? 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(),因為它可以把代碼推遲到下一次重繪之前執(zhí)行,而不是立即要求頁面重繪。
? ? ? ? window.requestAnimationFrame(() => {
? ? ? ? ? this.$emit('open')
? ? ? ? ? // 打開遮罩層
? ? ? ? ? this.$modal({ show: true, zIndex: this.$JJUI.zIndex - 1 })
? ? ? ? ? // 強制觸發(fā)瀏覽器重繪,不寫這句瀏覽器會合并繪制,不能觸發(fā)動畫
? ? ? ? ? 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的,組件銷毀時移除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()

項目目錄結(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 }) 顯示遮罩,遮罩存在時再次調(diào)用 this.$modal() 會移除遮罩
?* @param {Object} options 可選
?* @returns
?*/
const modalFunc = (options) => {
? // 為show時創(chuàng)建
? if (options.show) {
? ? const instance = new ModalConstructor({
? ? ? data: options,
? ? }).$mount()
? ? instanceArr.push(instance)
? ? // 如果 $mount() 沒有提供 elementOrSelector 參數(shù),模板將被渲染為文檔之外的的元素 (可以理解為未掛載狀態(tài)的vue實例對象) ,并且你必須使用原生 DOM API 把它插入文檔中
? ? document.body.appendChild(instance.$el)
? ? return instance
? } else {
? ? const instance = instanceArr.pop()
? ? // 否則銷毀實例
? ? 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) 時會執(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組件實現(xiàn)的文章就介紹到這了,更多相關(guān)Vue Drawer組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue 實現(xiàn)拖拽動態(tài)生成組件的需求

    vue 實現(xiàn)拖拽動態(tài)生成組件的需求

    這篇文章主要介紹了vue 如何實現(xiàn)拖拽動態(tài)生成組件的需求,幫助大家更好的理解和學(xué)習(xí)使用vue框架,感興趣的朋友可以了解下
    2021-05-05
  • vue3.0 搭建項目總結(jié)(詳細步驟)

    vue3.0 搭建項目總結(jié)(詳細步驟)

    這篇文章主要介紹了vue3.0 搭建項目總結(jié)(詳細步驟),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • vue動態(tài)路由匹配和路由懶加載多種方法詳解

    vue動態(tài)路由匹配和路由懶加載多種方法詳解

    這篇文章主要介紹了vue動態(tài)路由匹配和路由懶加載,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-06-06
  • 淺析Vue2/Vue3中響應(yīng)式的原理

    淺析Vue2/Vue3中響應(yīng)式的原理

    這篇文章主要是來和大家一起討論一下Vue2與Vue3中響應(yīng)式的原理,文中的相關(guān)示例代碼簡潔易懂,對我們深入了解Vue有一定的幫助,需要的可以參考下
    2023-07-07
  • webpack如何將vue3單頁面應(yīng)用改造成多頁面應(yīng)用

    webpack如何將vue3單頁面應(yīng)用改造成多頁面應(yīng)用

    這篇文章主要介紹了webpack如何將vue3單頁面應(yīng)用改造成多頁面應(yīng)用,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-05-05
  • Vue父子組件傳值&自定義事件方式

    Vue父子組件傳值&自定義事件方式

    這篇文章主要介紹了Vue父子組件傳值&自定義事件方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • 聊聊vue生命周期鉤子函數(shù)有哪些,分別什么時候觸發(fā)

    聊聊vue生命周期鉤子函數(shù)有哪些,分別什么時候觸發(fā)

    這篇文章主要介紹了聊聊vue生命周期鉤子函數(shù)有哪些,分別什么時候觸發(fā)?具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • 圖文詳解Vue3沒有代碼提示問題的解決辦法

    圖文詳解Vue3沒有代碼提示問題的解決辦法

    最近在使用Vue.js時候沒有自動提示,就很難受,下面這篇文章主要給大家介紹了關(guān)于Vue3沒有代碼提示問題的解決辦法,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2023-01-01
  • Vue ElementUI this.$confirm async await封裝方式

    Vue ElementUI this.$confirm async await封

    這篇文章主要介紹了Vue ElementUI this.$confirm async await封裝方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • vue中如何修改props傳進來的值

    vue中如何修改props傳進來的值

    大家應(yīng)該都知道vue是單向數(shù)據(jù)流,一般我們也不會在子組件里面修改父組件傳進來的值,但總有需要修改的時候,這篇文章主要介紹了vue中修改props傳進來的值,需要的朋友可以參考下
    2022-12-12

最新評論

海林市| 香港| 铜鼓县| 南木林县| 西城区| 乐业县| 柯坪县| 滕州市| 乐昌市| 恩平市| 浙江省| 玉门市| 收藏| 大邑县| 昌邑市| 太仓市| 山阳县| 洪雅县| 西城区| 香港| 瑞丽市| 江都市| 仙游县| 宣恩县| 金秀| 政和县| 海伦市| 金平| 广平县| 山西省| 齐河县| 鄂州市| 祁阳县| 竹山县| 额尔古纳市| 竹山县| 顺昌县| 门源| 灵川县| 深圳市| 商水县|