05 - Vue3 UI Framework - Button 組件

官網基本做好了,接下來開始做核心組件

返回閱讀列表點擊 這裏

目錄準備

在項目 src 目錄下創建 lib 文件夾,用來存放所有的核心組件吧。然後再在 lib 文件夾下創建 Button.vue 文件。

您也可以進行結構化設計,比如,這裏就不進行了。

|-lib
  |-Button
  	|- Button.vue
  	|- Button.ts
  	|_ Button.scss

需求分析

慣例先行需求分析

  1. 多種類基礎 Button,包含警告、成功、危險等
  2. 允許設置 Button 爲禁用狀態
  3. 不止有傳統 Button,還可以有文字或鏈接形式
  4. 當處於加載中,Button 應當顯示
  5. 有不同的尺寸可供選擇
  6. 應當允許更換顏色
  7. 當鼠標放置於 Button 上、鼠標按下未鬆開、處於加載中等狀態時,應當變更背景色
  8. 允許用戶自定義 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

  1. 放置一個遮罩層,用於變色
  2. 放置一個”加載中”的動畫,用於在加載中狀態下顯示
  3. 放置一個默認插槽,用於傳遞用戶自定義的文本

然後爲上述元素配置各自的 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; 才能消除

然後,我們使用 css3var() 語法,取得我們通過 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:

測試一下

image-20211213131430347

感謝閱讀 ☕

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章