超簡單學習 Vue.js 系列 | Vue-Toys-Converter

2022-02-12

Vue.js 簡單的示範專案,使用 Vue CLI 實作將所學習的知識以及實作需要的功能,藉此將知識實際結合,記得牢、想得到、用得出來。本次藉由 Vue Two Way Binding 實作轉換長寬,由公分 (cm) 或者英寸 (inch) 到網頁圖片經常使用的 pixel。

logo

數值轉換器 Vue Converter

GitHub Demo
技術關鍵字 說明
Horizon Input Groups
Switch Button
Two way Binding

App.Vue Padding

為了讓行動裝置的檢視畫面能夠跟邊距稍微保持距離,在 App.Vue Component 中使用全域的 CSS 對 #App 加入 Padding

#app {
  padding: 10px;
}

Form

Converter 有 DPI、Width 以及 Height 的輸入參數,因此使用 form tag 將相關的 ipunt tag 整理在一起。

每一個 input 使用 Div 包住,並且加上 mb-3 作為垂直距離留白,並在按 input 都需求加入 d-flexjustify-content-evenlyflex-wrap 來進行排版。

<div class="mb-3 d-flex justify-content-evenly flex-wrap">
  ...
</div>

Horizon Radio Buttons

水平排列的 Radio Input Tag,藉由外層的 d-flex 來排版 labelinput。此外各 Radio Input 藉由 Vue v-for 的方式渲染,寫起來清爽也方便。

在 input 使用 v-model 的方式,綁定 data 中的 dpi,只要點選就會將 value 賦予給 dpi,必須要注意各 Radio Input 必須要保持 name 不重複。

同時在 checked 使用 Vue bindind 設定預設勾選 dpi 為 96 的 Radio Input。

<div class="mb-3 d-flex justify-content-evenly flex-wrap">
  <div>
    <label for="DPIRadio" class="form-label fw-bold">DPI</label>
  </div>
  <template v-for="i in dpiRange" :key="i">
    <div class="form-check form-check-inline">
      <input
        class="form-check-input"
        type="radio"
        v-model="dpi"
        :name="'radio_dpi_' + i"
        :id="'radio_dpi_' + i"
        :value="i"
        :checked="i === 96"
      />
      <label class="form-check-label" :for="'radio_dpi_' + i">{{i}}</label>
    </div>
  </template>
</div>

Switch Button

Swithc Button 讓 Form 的互動更為活潑,且 Bootstrap 5 原生就支援 Active 的顯示非常方便,只要藉由 v-model 綁定 data 中的 boolean 即可。

搭配 js ternary operator,根據 v-model 綁定的 data 來決定 label 顯示目前使用中的 Mode。

<div class="mb-3">
  <div class="d-flex justify-content-center">
    <div class="form-check form-switch">
      <input class="form-check-input" type="checkbox" id="px2cmiSwitch" v-model="px2cmi">
      <label class="form-check-label mx-3" for="px2cmiSwitch">
        <span class="text-info fw-bold">
          {{ px2cmi === true ? 'pixel to cm / inch' : 'cm / inch to pixel'}}
        </span> Mode</label>
    </div>
  </div>
</div>

Text Input

放大與加粗 label 的文字顯示,此外在 input 使用 w-25 設定 width。

<div class="mb-3 d-flex justify-content-evenly align-items-center">
  <label for="c-width" class="col-sm-2 col-form-label fw-bold">Width</label>
  <div class="w-25">
    <input type="number" class="form-control" id="c-width" 
      style="font-size:20px" v-model="width_input"/>
  </div>

  <label for="c-height" class="col-sm-2 col-form-label fw-bold">Height</label>
  <div class="w-25">
    <input type="number" class="form-control" id="c-height" 
      style="font-size:20px" v-model="height_input" />
  </div>
</div>

Components

從 Home.vue 到 ConvertedTable.vue,藉由 js object 的方式,將資料傳入 props 入 Converted.vue,分別傳入了多個值,但也可以考慮以 vhWrapper 的方式,改用大 wrapper 或者是 plain object class object 的觀念用單一物件傳入。

Home.vue

<ConvertedTable 
  :px2cmi="px2cmi" 
  :whWrapper="{width: width_input, height: height_input}"
  :dpi="dpi"
  :inchToCm="inchToCm"
/>
import ConvertedTable from '@/components/ConvertedTable.vue'
export default {
  name: "Home",
  data() {
    return {
      dpiRange: [72, 96, 150, 300],
      dpi: 96,
      inchToCm: 2.54,
      height_input: 1080,
      width_input: 1920,
      px2cmi: true
    }
  },
  components: {
    ConvertedTable
  }
};

ConvertedTable.vue

props 接入了傳入的 data。

export default {
  props: ["px2cmi", "whWrapper", "dpi", "inchToCm"]
}

table 的使用一定要注意要使用 thead 以及 tbody Boostrap 預設的 Table Style 才會美觀。

使用 v-show 根據 props 中的資料判斷是否顯示特定資料列,沒有使用 computed,直接在 table cell 中進行運算結果的呈現 (因為沒有要重複使用)。

<table class="table" style="font-size:20px">
    <thead>
      <tr class="border-primary">
        <th>Unit</th>
        <th>Width</th>
        <th>Height</th>
      </tr>
    </thead>
    <tbody>
      <tr v-show="px2cmi">
        <td>cm</td>
        <td>{{ (whWrapper.width / dpi * inchToCm).toFixed(3) }}</td>
        <td>{{ (whWrapper.height / dpi * inchToCm).toFixed(3) }}</td>
      </tr>
      <tr v-show="px2cmi">
        <td>inch</td>
        <td>{{ (whWrapper.width / dpi).toFixed(3) }} </td>
        <td>{{ (whWrapper.height / dpi).toFixed(3) }}</td>
      </tr>
      <tr v-show="px2cmi !== true">
        <td>cm to px</td>
        <td>{{ Math.round((whWrapper.width / inchToCm) * dpi) }}</td>
        <td>{{ Math.round((whWrapper.height / inchToCm) * dpi) }}</td>
      </tr>
      <tr v-show="px2cmi !== true">
        <td>inch to px</td>
        <td>{{ Math.round((whWrapper.width) * dpi) }} </td>
        <td>{{ Math.round((whWrapper.height) * dpi) }}</td>
      </tr>
    </tbody>
  </table>

Boostrap 5 Extended

在行動裝置的時候保持 Width 100% 使用,但在寬螢幕則僅使用 Width 50% 讓網頁的內容置中顯示,算是以 Mobile First 的設計方式。因 Bootstrap 沒有支援 Width 的 RWD,所以自行進行擴充:

.w-50-lg {
    @media screen and (min-width:992px){    
      width: 50% !important;
    }
}