iT邦幫忙

2021 iThome 鐵人賽

DAY 26
3
Modern Web

排版神器 Tailwind CSS~和兔兔一起快速上手漂亮的元件開發!系列 第 27

Day 26:「按鈕博物館」- 輕鬆變化各種按鈕元件

Day26-Banner

哈囉大家好~
不知道昨天的進度條做的怎麼樣?

想要交作業的人可以貼在昨天的留言區給我呦!

那我們今天的內容也很簡單,
二話不說,要開始囉?

carrotPoint 前置作業

跟昨天的環境相同,兔兔為了寫文章方便是重新建立了一個,你們可以重新建立新的專案從頭練習一次,也可以在昨天做完的專案直接多增加新的元件哦!
 

carrotPoint 建立空白元件

和昨天一樣,先建立空白元件,所以在專案裡的 ./src/components 資料夾中新增一個 RabbitButton.vue 的元件:

完成後,增添以下內容:

<template>
  
</template>

<script>
export default {
  name: "RabbitButton",
}
</script>

接著我們就可以把這個元件新增到畫面中,打開 App.vue 然後內容跟昨天差不多:

<template>
  <div :class="[
      'w-screen h-screen',
      'flex flex-col',
      'justify-center items-center',
      'gap-5',
    ]"
  >
    <RabbitButton />
  </div>
</template>

<script>
import RabbitButton from './components/RabbitButton.vue'

export default {
  data() {
    return {
      
    }
  },
  components: {
    RabbitButton,
  }
}
</script>

這樣準備工作大妥當,開始按鈕博物館導覽啦!
 

carrotPoint 按鈕

按鈕的要素其實很簡單,不外乎就是一個中間有字的矩形,滑鼠懸停時有顏色變化,最好的話連按下後也有相對應的效果,才是一個良好的互動。

那麼基於這些要素,我們就可以這麼做:

  • 保持按鈕長寬比 - px-6py-3
  • 小米定律啦 - rounded-mdfont-bold
  • 從一般、滑鼠懸停到按下按鈕後的基本灰色系變化開始 - bg-gray-400hover:bg-gray-300active:bg-gray-500
  • 進階的華麗效果:滑鼠懸停時微微放大,按下後縮小 - hover:scale-105active:scale-95
  • 不來點過渡效果,看起來就像頂叩叩的空姐一樣僵硬 - transition-all
  • 然後不知道按鈕要有什麼文字,索性就寫上 按鈕 二字讓它不會被誤認吧?

所以綜合起來,就會像是這樣:

<template>
  <div
    :class="[
      'px-6 py-3',
      'rounded-md font-bold',
      'bg-gray-400 hover:bg-gray-300 active:bg-gray-500',
      'hover:scale-105 active:scale-95',
      'transition-all'
    ]"
  >
    按鈕
  </div>
</template>

<script>
export default {
  name: "RabbitButton",
}
</script>


這麼快就有 fu 了嗎?
哎呀後面還有更有趣的啦!

畢竟我們現在按鈕還非常的單調,
讓我們來幫它點綴一下!
 

carrotPoint 動起來

Yes,這麼快就來到了我們動起來的環節了。

別以為會這麼輕易地結束,
我們現在看到的按鈕是這個樣子:

可是兔兔期待的是這個樣子:

哇,還是有點落差對吧?

「摁,是沒錯啦,但不就是差個 icon 和顏色而已嘛 ... 不過每次插入 svg 的 icon 都會覺得把結構弄得好雜亂 ...」

嘿嘿,這個立馬幫你解決!

Slot

沒錯,這就是輪到不知道甚麼時候才能派上用場但現在就派上用場的 Slot 啦!

slot 可以把原本一直要替換的東西從內部解藕出來,那麼一來我們在外面替換時就不用一直修改元件的內部功能了~

現在在文字前後幫按鈕加上兩個 slot,分別取名為 iconLefticonRight,然後因為想要 icon 和文字可以橫向排列且之間有點空隙,我們順便加上 flex gap-2 的樣式:

<!-- RabbitButton.vue -->

<div
  :class="[
    'flex gap-2',
    'px-6 py-3',
    'rounded-md font-bold',
    'text-white',
    'bg-blue-400 hover:bg-blue-300 active:bg-blue-500',
    'hover:scale-105 active:scale-95',
    'transition-all'
  ]"
>
  <slot name="iconLeft" />
  按鈕
  <slot name="iconRight" />
</div>

這麼一來就可以輕鬆的在按鈕文字的前或後加上 icon 啦!

然後啊,Tailwind 官方的專案中有個特別的網站,叫做 heroicons雖然裡面的 icon 量真的不多,但是各個都是精心設計過,適合搭配 Tailwind 直接使用的 icon,還可以直接複製 svg,算是很方便啦~

我們回到 App.vue,兔兔這邊要用的 icon 就是在 heroicons 上面找的 wifi icon (不是 wife,要認明。),然後包在指定 iconLeft 插槽 的 template 中:

<!-- App.vue -->

<div 
  :class="[
    'w-screen h-screen',
    'flex flex-col',
    'justify-center items-center',
    'gap-5',
  ]"
>
  <RabbitButton>
    <template v-slot:iconLeft>
      <svg  xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0" />
      </svg>
    </template>
  </RabbitButton>
</div>

讚啦,這樣真的很方便,把 icon 從元件中抽離之後就可以隨時替換了!

可是 ...
(你這兔又來了,果然不管做的怎麼樣都有話要說!)

就是真的需要提出來啦!
按鈕的文字和顏色的改變的確是還不方便,所以我們就必須用到 ...

Props

對,就是 Props。

我們剛剛說到要改變文字和顏色,所以就幫 props 增加兩個內容,但這兩個內容都必須要有預設值:

<script>
export default {
  name: "RabbitButton",
  props:{
    text: {
      default: "按鈕"
    },
    color: {
      default: "gray"
    },
  },
}
</script>

我們先來解決按鈕文字的部分,把按鈕的字樣取代成 {{ text }}

<div
  :class="[
    'flex gap-2',
    'px-6 py-3',
    'rounded-md font-bold',
    'text-white',
    'bg-gray-400 hover:bg-gray-300 active:bg-gray-500',
    'hover:scale-105 active:scale-95',
    'transition-all'
  ]"
>
  <slot name="iconLeft" />
  {{ text }}
  <slot name="iconRight" />
</div>

再來,就要解釋到為什麼 color 要有預設值且預設值是字串了。 因為我們要利用物件 (字典) 的特性來快速的切換按鈕的色系!

為了方便監控和節省資源,我們先在 computed 中建立一個 colorSelector 函數,並返回一個空白物件。

<script>
export default {
  name: "RabbitButton",
  props:{
    text: {
      default: "按鈕"
    },
    color: {
      default: "gray"
    },
  },
  computed: {
    colorSelector() {
      return {}
    }
  }
}
</script>

建立好之後,我們把元件中顏色的那一行 tailwind 語法加到物件中並命名為 gray,且在物件後加上 [this.color] 來讓函數找到並返回指定名稱為 gray 的內容:

<script>
export default {
  name: "RabbitButton",
  props:{
    text: {
      default: "按鈕"
    },
    color: {
      default: "gray"
    },
  },
  computed: {
    colorSelector() {
      return {
        gray: 'bg-gray-400 hover:bg-gray-300 active:bg-gray-500',
      }[this.color]
    }
  }
}
</script>

然後把 colorSelector 函數加回 class 列表,並測試一次:

<div
  :class="[
    'flex gap-2',
    'px-6 py-3',
    'rounded-md font-bold',
    'text-white',
    colorSelector,
    'hover:scale-105 active:scale-95',
    'transition-all'
  ]"
>
  <slot name="iconLeft" />
  {{ text }}
  <slot name="iconRight" />
</div>

看起來沒什麼變化是正常的,但至少這表示沒寫錯!
打鐵要趁熱,我們接著來追加其他顏色吧~

最後追加完,完整的 code 可能會是這個樣子:

<template>
  <div
    :class="[
      'flex gap-2',
      'px-6 py-3',
      'rounded-md font-bold',
      'text-white',
      colorSelector,
      'hover:scale-105 active:scale-95',
      'transition-all'
    ]"
  >
    <slot name="iconLeft" />
    {{ text }}
    <slot name="iconRight" />
  </div>
</template>

<script>
export default {
  name: "RabbitButton",
  props:{
    text: {
      default: "按鈕"
    },
    color: {
      default: "gray"
    },
  },
  computed: {
    colorSelector() {
      return {
        gray: 'bg-gray-400 hover:bg-gray-300 active:bg-gray-500',
        green: 'bg-green-400 hover:bg-green-300 active:bg-green-500',
        blue: 'bg-blue-400 hover:bg-blue-300 active:bg-blue-500',
        red: 'bg-red-400 hover:bg-red-300 active:bg-red-500',
        yellow: 'bg-yellow-400 hover:bg-yellow-300 active:bg-yellow-500',
      }[this.color]
    }
  }
}
</script>

然後這邊值得注意的是,為什麼不用變數取代 bg-XXX-400hover:bg-XXX-300 就好了呢? 為何還要這樣大費周章地去把 class 名稱都寫過一遍?

因為要讓 purge 讀取到要編譯的 class 的話,class 名稱必須保持完整,不能被拆開!

所以為了要讓樣式能正確被讀取到且被編譯,不能使用 "bg-" + color + "500" 或是 "opacity-" + number 這種組合過的形式,這對 Tailwind 來說是不合法的 class 名稱哦。

既然完成了,我們就快點來測試吧!
好興奮啊!

 

carrotPoint 最後測試

最後的測試環境,我們來做三個按鈕吧!

然後為了美化中的美化,我們可以在按鈕元件中加上 cursor-pointer 讓它看起來更像樣!

第一個按鈕,就是最普通的按鈕啦!

<RabbitButton />

再來是讓人看起來很連網路的 wifi 按鈕!

<RabbitButton text="wifi" color="blue">
  <template v-slot:iconLeft>
    <svg  xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0" />
    </svg>
  </template>
</RabbitButton>

最後,是安裝軟體時會出現的下一步按鈕~

<RabbitButton text="下一步" color="green">
  <template v-slot:iconRight>
    <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
    </svg>
  </template>
</RabbitButton>

OK,超成功的啦!
 

今天是不是也很輕鬆簡單啊?

兔兔覺得這樣做按鈕又美、彈性又高!
以後遇到再多狀況也只要小修改就能夠繼續使用了,
這有再度登場的機會正是做元件的目的!

希望你們都能夠試試看,如果有更好方法或建議 ...

做成作業留言給我看嘿!!!!
 

carrotPoint 給你們的回家作業:


關於兔兔們:


 


( # 兔兔小聲說 )

不是兔兔偷懶,兔兔只是陪富堅去取材。


上一篇
Day 25:「好慢喔,下載多少了?」- 進度條
下一篇
Day 27:「流浪到淡水!」- 手風琴選單
系列文
排版神器 Tailwind CSS~和兔兔一起快速上手漂亮的元件開發!32

尚未有邦友留言

立即登入留言