👉👉項目預覽地址,能夠直接設置爲瀏覽器主頁或者桌面快捷方式進行使用javascript
徹底開源,你們能夠隨意研究,二次開發。固然仍是十分歡迎你們點個Star⭐⭐⭐
👉👉源碼連接(gitee) 👉👉源碼連接(github)css
本文記錄了流鶯書籤封裝的部分基礎組件,包括vue
⭐ Buttonjava
⭐ Overlaynode
⭐ Dialogreact
⭐ Messagegit
因爲本項目是爲了練手,因此在某些組件中可能也添加了一些實際並無用到的功能,接下來將逐個介紹這些組件的屬性,方法,以及一些設計思路.github
除了一些特殊的樣式,文章不會大量貼出CSS
代碼,因爲我SASS
功底不是很好,CSS
代碼看起來也是比較臃腫😣😣😣,感興趣的同窗能夠自行查看源碼數組
基本就是一個組件的.vue
文件和一個對應的index.ts
,index.ts
存放一些基礎數據,類型聲明等瀏覽器
├── src
├── assets // 存放靜態資源
├── baseComponents // 基礎組件
└──Button // 按鈕組件
│ └──Button.vue
└── Message // 彈窗組件
├──Message.vue
└──index.ts
......
複製代碼
先來看效果,能夠看到就是鼠標移入移出的時候會有一個動畫
🌝按鈕的背景顏色使用了vue3
新增特性,直接在css
中綁定了props
的變量backgroundColor
,不瞭解新特性的小夥伴能夠前往官網查看喲
🌝經過動態綁定class
來啓用動畫,由於css
中是給類名animation
寫的hover
事件,因此若是類名不存在了,動畫天然不生效了
<template>
<div class="wh-button-box"> <div class="btn"> <a :class="{animation:useAnimation}" href="#">{{title}}</a> </div> </div>
</template>
<script lang='ts'> import { defineComponent } from 'vue'; export default defineComponent({ name: 'Wh-button', props: { title: { type: String, default: '按鈕', }, backgroundColor: { type: String, default: 'rgba(16, 143, 111, 0.3)', }, useAnimation: { type: Boolean, default: false, }, }, setup() { return {}; }, }); </script>
<style lang='scss'> //此處省略部分CSS代碼,詳情請查看源碼 .wh-button-box { .btn { &:hover .animation::before { transform: skewX(45deg) translateX(200%); } &:hover .animation { background: v-bind('state.color'); } } } </style>
複製代碼
正常傳入屬性使用便可,這裏使用雙標籤純屬我的習慣😁😁
<wh-button @click="onConfirm" title='確認' :use-animation="true" :background-color="`rgba(160, 21, 114, 0.3)`"></wh-button>
<wh-button @click="onCancle" title='取消' :use-animation="true"></wh-button>
複製代碼
先看效果
🌝使用了vue3
的新特性teleport
,這個組件的做用是把裏面的內容插入到指定的節點當中,我這裏是插入在body
裏了
🌝添加了0.3秒的一個過渡效果,這樣顯得平滑一點,這裏須要注意的是vue3
中的transition
類名發生了一些小變化,我剛開始寫的時候沒注意到,結果過渡效果就沒生效,查了半天才發現v-enter
變成了v-enter-form
, v-leave
變成了v-leave-form
,總體的使用方式仍是跟之前同樣,更多具體的變更請移步vue3官網
🌝遮罩層通常都結合其餘組件使用,好比Dialog
,因此這裏設置了一個屬性,來配置是否能夠經過點擊遮罩層來關閉Dialog
,須要配置closeOnClickModal
爲true
,並執行父組件的一個close
方法
<template>
<!-- 傳送門 此處Dom節點會被插在body裏 -->
<teleport to='body'> <!-- 過渡動畫 添加了0.3s的淡入淡出 顯得更加平滑 --> <transition name="base"> <!-- 遮罩層 --> <div class="overly" :style="`z-index:${zIndex}`" v-if="show" @click='handleClick'></div> </transition> </teleport>
</template>
<script lang='ts'> import { defineComponent } from 'vue'; export default defineComponent({ name: 'Overlay', props: { show: { type: Boolean, default: false, }, zIndex: { type: Number, default: 2000, }, closeOnClickModal: { type: Boolean, default: false, }, }, emits: ['close'], setup(props, { emit }) { const handleClick = () => { if (props.closeOnClickModal) { emit('close'); } return; }; return { handleClick, }; }, }); </script>
<style lang='scss'> //此處省略部分CSS代碼,詳情請查看源碼 //淡入淡出 .base-enter-active, .base-leave-active { transition: all 0.3s ease; } .base-enter-to, .base-leave-from { opacity: 1; } .base-enter-from, .base-leave-to { opacity: 0; } </style>
複製代碼
配置close-on-click-modal
爲true
並傳入一個close
方法,就能夠點擊遮罩層關閉其餘組件了(如Dialog
),須要在close
方法中手動的設置:show
綁定的屬性爲false
.
<overlay :show='dialogVisible' @close='onCancle' :close-on-click-modal='true'></overlay>
複製代碼
先看效果,點擊的那個黃色圓圈是錄屏軟件自帶的,不是我寫的🤦♂️🤦♂️🤦♂️
🌝和遮罩層綁定同一個值,能夠在關閉彈窗的同時關閉遮罩層,也能夠給遮罩層傳遞一個close
方法,經過點擊遮罩層關閉彈窗
🌝使用teleport
將彈窗插入到.Dialog(class='Dialog')
中,至於爲何要插入到這裏,只是爲了練習封裝一個用來生成節點的hooks
//建立DOM節點的hook函數 在body中插入一個自定義class的div節點
//setup函數在執行時等同於created 因此不必寫入生命週期
import { onUnmounted } from 'vue';
const useDOMCreate = (nodeId: string): HTMLDivElement => {
// 生成一個div的節點
const node = document.createElement('div');
// 給賦值一個class
node.className = nodeId;
// 在body中插入div節點
document.body.appendChild(node);
// 在組件卸載的時候移除dom節點
onUnmounted(() => {
document.body.removeChild(node);
});
return node;
};
export default useDOMCreate;
複製代碼
🌝使用transition
添加了一個淡入淡出的過渡,而且有20px的位移,視覺效果更好
🌝組件自己分爲三個部分,header
就是一個標題,body
部分是一個插糟,能夠經過父組件添加一些內容進來,
footer
部分是兩個button
,執行父組件的確認和取消回調.
<template>
<!-- 遮罩層 -->
<overlay :show='dialogVisible' @close='onCancle' :close-on-click-modal='true'></overlay>
<!-- 傳送門 此處Dom節點會被插在.Dialog裏 -->
<teleport to='.Dialog'> <div class="dialog-box" v-bind='$attrs'> <!-- 過渡動畫 添加了0.3s的淡入淡出 而且有20px的移動 顯得更加平滑 --> <transition name="dialog"> <div class="dialog-content" v-if='dialogVisible' :style="`width:${width};margin-top:${top}`"> <!-- Dialog組件header部分 --> <div class="dialog-header"> <h5 class="dialog-title">{{title}}</h5> </div> <!-- Dialog組件內容插槽 --> <slot> </slot> <!-- Dialog組件footer部分 --> <div class="dialog-footer"> <wh-button @click="onConfirm" class="footer-btn" title='確認' :use-animation="true" :background-color="`rgba(160, 21, 114, 0.3)`"></wh-button> <wh-button @click="onCancle" class="footer-btn" title='取消' :use-animation="true"></wh-button> </div> </div> </transition> </div> </teleport>
</template>
<script lang='ts'> //此處省略文件引用,詳情請查看源碼 export default defineComponent({ components: { Overlay, WhButton, }, name: 'Dialog', props: { dialogVisible: { type: Boolean, default: false, }, title: { type: String, default: '提示', }, width: { type: String, default: '400px', }, top: { type: String, default: '15vh', }, }, emits: ['cancle', 'confirm'], setup(props, { emit }) { // 建立dom節點 .Dialog useDOMCreate('Dialog'); // 關閉Dialog const onCancle = () => { emit('cancle'); }; const onConfirm = () => { emit('confirm'); }; return { onCancle, onConfirm, }; }, }); </script>
<style lang='scss'> //此處省略部分CSS代碼,詳情請查看源碼 .dialog-enter-active, .dialog-leave-active { transition: all 0.3s ease; } .dialog-enter-to, .dialog-leave-from { opacity: 1; } .dialog-enter-from, .dialog-leave-to { transform: translateY(-20px); opacity: 0; } </style>
複製代碼
正常傳入屬性使用便可,form
組件做爲內容插入,有關form組件能夠點擊這裏查看個人另外一篇文章
<!-- 添加單個書籤的彈窗 -->
<Dialog :dialog-visible='isShowAddMarkDialog' @cancle='onCancle' @confirm='onConfirm' title='添加書籤'> <wh-from ref='form'> </wh-from> </Dialog>
複製代碼
先看效果,這裏只展現了默認的狀況,其實還有其餘顏色的
設計思路/亮點 🌝使用transition
添加了過渡效果,視覺效果拉滿,淡入淡出,而且移動一個身位,經過傳入的類型屬性來綁定class
達成不一樣顏色的效果
🌝使用的時候能夠傳入一個配置對象,或者一個字符串,若是是字符串最終仍是會轉換爲對象
🌝使用fixed定位
,距離頂部的偏移量經過message
的數量來計算,全部的message
存放在一個數組中,新增和移除一條message
的時候就相應的操做數組,再根據 數組的長度 * 一個固定的高度 ,就能夠算出每一條message
的偏移量
🌝給實例添加一個銷燬的方法,在transition
的after-leave
週期中觸發這個方法,在這個方法中操做數組刪除數據,並修改props
中的offsetNumber
來形成視圖的刷新.具體的查看下方代碼以及源碼.
//Message.vue
<template>
<!-- 過渡效果 淡入淡出 而且移動一個身位 -->
<transition name='message' @after-leave="$emit('destroy')"> <div :class="classes" v-show='visiable' :style='styles' class="message"> {{message}} </div> </transition>
</template>
<script lang='ts'> //此處省略文件引用,詳情請查看源碼 export default defineComponent({ name: 'Message', props: { id: { type: String, default: '', }, type: { type: String as PropType<MessageType>, default: 'default', }, message: { type: String, default: '這是一條提示', }, duration: { type: Number, default: 2000, }, offsetNumber: { type: Number, default: 0, }, }, setup(props) { const classes = computed(() => ['message-' + props.type]); const visiable = ref(false); let timer: any = null; const startTimer = () => { timer = setTimeout(() => { visiable.value = false; }, props.duration); }; const styles = computed(() => { return { top: `${(props.offsetNumber - 1) * 55 + 20}px` }; }); // 組件渲染完了顯示 onMounted(() => { visiable.value = true; // 開啓定時器 startTimer(); }); onUnmounted(() => { clearTimeout(timer); }); return { classes, visiable, styles, }; }, }); </script> <style lang='scss'> //此處省略部分CSS代碼,詳情請查看源碼 .message-enter-active, .message-leave-active { transition: all 0.3s ease; } .message-enter-to, .message-leave-from { opacity: 1; } .message-enter-from, .message-leave-to { transform: translate(-50%,-100%); opacity: 0; } </style> 複製代碼
//index.ts
import Message from './Message.vue';
import { createVNode, render, VNode, reactive, computed } from 'vue';
// 類型
export type MessageType = 'success' | 'error' | 'default';
//一條消息的數據
export interface MessageOptions {
id?: string;
type?: MessageType;
message?: string;
duration?: number;
offsetNumber?: number;
}
export type MessageParams = MessageOptions | string;
export type MessageList = MessageOptions[];
// 存放全部的message實例 用來計算偏移量
const instances = reactive<VNode[]>([]);
const offsetNumber = computed(() => instances.length + 1);
const createMessage = (options: MessageParams) => {
// 若是參數是string類型,就把它轉換爲options對象
if (typeof options === 'string') {
options = {
message: options,
};
}
// 把組件建立爲虛擬節點 也就是一個組件實例
const vnode = createVNode(Message, {
...options,
offsetNumber: offsetNumber.value,
});
// 建立一個容器
const container = document.createElement('div');
// 把實例渲染到容器裏
render(vnode, container);
// 將渲染後的結果 放到body上
// 由於會多一個div 因此插入第一個孩子
// 爲何不直接放到body裏,要建立一個容器再取裏面的內容呢 爲了銷燬組件 若是直接放在body裏,就清空全部節點了
document.body.appendChild(container.firstElementChild!);
instances.push(vnode);
// 給實例添加一個銷燬方法
vnode.props!.onDestroy = () => {
instances.shift();
instances.forEach((item: any) => {
item.component.props.offsetNumber -= 1;
});
// 移除dom
render(null, container);
};
};
export default createMessage;
複製代碼
函數式調用,傳入配置對象,或者傳入一個字符串便可
//此處省略文件引用
createMessage({ type: 'success', message: '添加標籤成功!' });
複製代碼
請你們不吝賜教,在下方評論或者私信我,十分感謝🙏🙏🙏.
✅認爲我某個部分的設計過於繁瑣,有更加簡單或者更高逼格的封裝方式
✅認爲我部分代碼過於老舊,能夠提供新的API或最新語法
✅對於文章中部份內容不理解
✅解答我文章中一些疑問
✅認爲某些交互,功能須要優化,發現BUG
✅想要添加新功能,對於總體的設計,外觀有更好的建議
最後感謝各位的耐心觀看,既然都到這了,點個 👍贊再走吧
🔊項目預覽地址(GitHub Pages):👉👉alanhzw.github.io
🔊項目預覽備用地址(本身的服務器):👉👉warbler.duwanyu.com
🔊源碼地址(gitee):👉👉gitee.com/hzw_0174/Wa…
🔊源碼地址(github):👉👉github.com/alanhzw/War…
🔊項目總體介紹:👉👉juejin.cn/post/696394…
🔊流鶯書籤-從零搭建一個Vite+Vue3+Ts項目:👉👉juejin.cn/post/695130…
🔊流鶯書籤-基礎組件介紹(Form,Input):👉👉juejin.cn/post/696393…
🔊流鶯書籤-基礎組件(Button,Overlay,Dialog,Message):👉👉juejin.cn/post/696394…
🔊流鶯書籤-業務組件介紹:👉👉暫無
🔊個人博客:👉👉www.duwanyu.com