這一篇會單獨把 Weather Icon 這個子組件拉出來說,因為裡面的判定有一點小複雜。

Weather Icon

首先,這裡會有兩個因素影響 Icon 的變化:

  1. 天氣現象
  2. 白天或早上

上一篇有說到,天氣現象是由父組件傳入 currentWeather.weatherCode 的氣候代碼所決定的。而白天與早上的判定,也在上一篇由 fetchSunRiseSunSet() 這個方式所獲取並根據現在時間所判斷出來的。

子組件使用 defineProps 來獲得父組件傳進來的參數:

defineProps({
  currentWeatherCode: {
    type: Number,
    default: 1,
  },
  dayStatus: {
    type: String,
    // 預設值設為晚上
    default: 'night',
  },
});

根據一般天氣預報 說明檔裡,有些氣候狀態的樣式其實都是差不多的,所以把它們整理並對照我們所擁有的 Icon:

const weatherTypes = {
  isThunderstorm: [15, 16, 17, 18, 21, 22, 33, 34, 35, 36, 41],
  isClear: [1],
  isCloudyFog: [25, 26, 27, 28],
  isCloudy: [2, 3, 4, 5, 6, 7],
  isFog: [24],
  isPartiallyClearWithRain: [8, 9, 10, 11, 12, 13, 14, 19, 20, 29, 30, 31, 32, 38, 39],
  isSnowing: [23, 37, 42],
};

接著要判定目前是白天還是晚上:

const weatherIcons = reactive({
  day: {
    isThunderstorm: DayThunderstorm,
    isClear: DayClear,
    isCloudyFog: DayCloudyFog,
    isCloudy: DayCloudy,
    isFog: DayFog,
    isPartiallyClearWithRain: DayPartiallyClearWithRain,
    isSnowing: DaySnowing,
  },
  night: {
    isThunderstorm: NightThunderstorm,
    isClear: NightClear,
    isCloudyFog: NightCloudyFog,
    isCloudy: NightCloudy,
    isFog: NightFog,
    isPartiallyClearWithRain: NightPartiallyClearWithRain,
    isSnowing: NightSnowing,
  },
});

寫一個函數來獲得 weatherCode 並把它轉變成對應的天候氣象:

const weatherCode2Type = (weatherCode: number) => {
  const [weatherType] =
    Object.entries(weatherTypes).find(([weatherType, weatherCodes]) => weatherCodes.includes(Number(weatherCode))) ||
    [];

  return weatherType;
};

最後就是顯示在畫面上:

<template>
  <div>
    <img :src="weatherIcons[dayStatus][weatherCode2Type(currentWeatherCode)]" alt="" />
  </div>
</template>

成果就是:

vue-project-weather-icon-change.gif

更新按鈕動畫

按鈕按下確實是會更新狀態,但是,如果今天有網速比較慢的使用者,他就必須要花比較長的時間來抓取 API,那他要怎麼知道目前是更新中還是已經成功獲取 API 了?所以我讓按鈕動起來,來告訴使用者資料還在讀取中。

Weather.vue 先設定一個變數來存放更新狀態:

const isLoading = reactive({ status: false });

接著可以看到更新按鈕有三個事件是獲取 API 資料的:

@click="
  () => {
    fetchCurrentWeather();
    fetchWeatherForecast();
    fetchSunRiseSunSet();
    ...
}

到第一個獲取 API 函數裡的最上面加入:

const fetchCurrentWeather = async () => {
  // 更新按鈕動畫
  isLoading.status = true;
  ...
}

在最後一個獲取 API 函數的最下面加入:

const fetchSunRiseSunSet = async () => {
  ...
  isLoading.status = false;
}

這樣就完成按鈕的狀態。

按鈕動畫

狀態有了那動畫部分呢?我這邊使用的是 font-awesome,所以可以到 Font-awesome search 這裡來找尋自己要的 Icon。

將找到的 Icon 程式碼與狀態結合就會變成:

<i class="fa-solid fa-refresh fa-spin" v-if="isLoading.status === true"></i>
<i class="fa-solid fa-rotate" v-else></i>

這樣在更新時就會有動畫了。

可是我網速很快沒有辦法看到,那很簡單,瀏覽器都有提供限速的選項:

vue-project-weather-update-button.gif

⚠️ 如果有關掉 Windows 效能選項:在視窗內部以動畫方式顯示控制項和元素 這個設定,那請務必把他打開,不然更新按鈕在更新時是不會旋轉的。

程式碼部分同步在 et860525/Morning-things


結語

根據時間與天氣氣候變換天氣 Icon 真的很酷,這裡我是參考 [Day 21 - 即時天氣] 處理天氣圖示以及 useMemo 的使用 這篇文章的,在那個時候應該是還沒有日出日落的 API 可以使用,不過到了現在我上去看的時候已經有了,所以我就沒有再下載 JSON 檔案再處理😀。