05 - Vue3 UI Framework - Button 組件
- 2021 年 12 月 13 日
- 筆記
- [18] Vue, [00] UI Framework
官網基本做好了,接下來開始做核心組件
返回閱讀列表點擊 這裡
目錄準備
在項目 src 目錄下創建 lib 文件夾,用來存放所有的核心組件吧。然後再在 lib 文件夾下創建 Button.vue 文件。
您也可以進行結構化設計,比如,這裡就不進行了。
|-lib
|-Button
|- Button.vue
|- Button.ts
|_ Button.scss
需求分析
慣例先行需求分析
- 多種類基礎
Button,包含警告、成功、危險等 - 允許設置
Button為禁用狀態 - 不止有傳統
Button,還可以有文字或鏈接形式 - 當處於載入中,
Button應當顯示 - 有不同的尺寸可供選擇
- 應當允許更換顏色
- 當滑鼠放置於
Button上、滑鼠按下未鬆開、處於載入中等狀態時,應當變更背景色 - 允許用戶自定義
Button上顯示的文本
那麼可以整理出以下參數表格
| 參數 | 含義 | 類型 | 可選值 | 默認值 |
|---|---|---|---|---|
| level | 默認類型 | string | default / plain / primary / success / info / warning / danger | default |
| disabled | 是否禁用 | boolean | false / true | false |
| theme | 式樣 | string | button / link / text | button |
| loding | 是否載入中 | boolean | false / true | false |
| size | 尺寸 | string | middle / small / large | middle |
| color | 顏色 | string | 任意合法顏色值 | #f3678e |
第 7 條,可以通過設置一個遮罩層來實現,只要遮罩層變色,背景色也等效變色
第 8 條,可以通過插槽實現,注意 vue3 不建議使用具名插槽
骨架
容易得到如下骨架
<template>
<button
class="jeremy-button"
:theme="theme"
:level="level"
:size="size"
:style="{ '--color': color }"
:disabled="disabled"
:loading="loading"
>
<div class="jeremy-button-mask"></div>
<span class="jeremy-button-loadingIndicator" v-if="loading"></span>
<slot></slot>
</button>
</template>
首先,本質應當是一個 button 元素,在此基礎上,將參數列表中整理出來的每個參數,都使用 v-bind 綁定到 button 上
注意,此處綁定 color,必須是如上例一樣,綁定到 --color 屬性上,才可以在 css 中使用 css3 語法 var() 讀取,在 css 小節會再解釋,此處略
之後,在 button 內
- 放置一個遮罩層,用於變色
- 放置一個」載入中」的動畫,用於在載入中狀態下顯示
- 放置一個默認插槽,用於傳遞用戶自定義的文本
然後為上述元素配置各自的 class 名稱,骨架就完成了。
功能
顯然,參數列表中整理出來的內容,一定來自引用該組件的地方的傳入,先根據參數列表,寫好 ts 聲明:
declare const props: {
theme?: "button" | "link" | "text";
level?:
| "default"
| "plain"
| "primary"
| "success"
| "info"
| "warning"
| "danger";
size?: "middle" | "small" | "large";
color: string;
disabled: boolean;
loading: boolean;
};
然後在 export default 中,寫入我們的參數
export default {
install: function (Vue) {
Vue.component(this.name, this);
},
name: "JeremyButton",
props: {
theme: {
type: String,
default: "button",
},
level: {
type: String,
default: "default",
},
size: {
type: String,
default: "middle",
},
color: {
type: String,
default: "#8c6fef",
},
disabled: {
type: Boolean,
default: false,
},
loading: {
type: Boolean,
default: false,
},
},
};
對於事件綁定,因為我們設計的組件只有一個唯一的根元素,所以對於外部傳遞過來的事件,會自動綁定到組件的根元素上面。
樣式表
注意 :UI 庫的樣式表一般不要加 scoped 修飾符,為了儘可能減少對用戶樣式表的影響,方便用戶 DIY
特別注意 : button 元素會有默認黑色外邊框,不屬於 border,必須通過 outline: none; 才能消除
然後,我們使用 css3 的 var() 語法,取得我們通過 ts 綁定到 style 上的 --color 屬性
為什麼是 --color 而不是 color ?因為 var() 語法要求這個參數必須是 -- 開頭,才可以正常訪問到
對於遮罩層,採用淡出到白色即可實現,原理此處不解釋了
最後,對於多種不同的 button,可以使用 scss 提供的 mixin / include 語法來實現,完整程式碼如下:
$theme-color: var(--color);
$base-mask: fade-out(#fff, 0.7);
$active-mask: fade-out(#fff, 0.5);
$h: 32px;
$radius: 4px;
@keyframes jeremy-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.jeremy-button {
position: relative;
display: inline-block;
padding: 10px 16px;
color: white;
border-radius: $radius;
border: none;
font-size: 16px;
cursor: pointer;
white-space: nowrap;
transition: background-color 250ms;
outline: none;
:focus {
outline: none;
}
> .jeremy-button-mask {
position: absolute;
display: inline-block;
height: 100%;
width: 100%;
left: 0;
top: 0;
border-radius: $radius;
&:hover {
background: $base-mask;
}
}
&[loading="true"],
&[disabled] {
cursor: not-allowed;
> .jeremy-button-mask {
pointer-events: none;
}
}
> .jeremy-button-loadingIndicator {
width: 14px;
height: 14px;
display: inline-block;
margin-right: 4px;
border-radius: 8px;
border-style: solid;
border-width: 2px;
animation: jeremy-spin 1s infinite linear;
}
}
@mixin layout($color) {
$loading-color: fade-out(black, 0.7);
background: $color;
&:active {
> .jeremy-button-mask {
background: $active-mask;
}
}
> .jeremy-button-loadingIndicator {
border-color: $loading-color $loading-color $loading-color transparent;
}
&[loading="true"],
&[disabled] {
> .jeremy-button-mask {
background: $base-mask;
}
}
}
.jeremy-button[theme="button"] {
$color: $theme-color;
@include layout($color);
}
.jeremy-button:not([theme="button"]) {
padding: 0;
background: white;
color: black;
&:hover {
color: $theme-color;
}
}
.jeremy-button[theme="link"] {
text-decoration: underline;
}
.jeremy-button[level="plain"] {
$base-color: $theme-color;
@include layout(white);
color: black;
> .jeremy-button-mask {
border: 1px solid rgb(187, 187, 187);
}
&:not([loading="true"]):not([disabled]) {
&:hover {
> .jeremy-button-mask {
border: 1px solid $base-color;
}
color: $base-color;
}
}
}
.jeremy-button[level="primary"] {
$color: #29adfa;
@include layout($color);
}
.jeremy-button[level="success"] {
$color: rgb(103, 194, 58);
@include layout($color);
}
.jeremy-button[level="info"] {
$color: #808080;
@include layout($color);
}
.jeremy-button[level="warning"] {
$color: rgb(230, 162, 60);
@include layout($color);
}
.jeremy-button[level="danger"] {
$color: rgb(245, 108, 108);
@include layout($color);
}
.jeremy-button[size="large"] {
padding: 14px 24px;
}
.jeremy-button[size="small"] {
padding: 6px 10px;
}
以上,button 組件就完成了! :happy:
測試一下

感謝閱讀 ☕

