4-4 使用 emotion 完成「臺灣好天氣」 UI

本單元對應的專案分支為:create-ui

單元核心#

這個單元的主要目標包含:

  • 使用 emotion 完成「臺灣好天氣」的完整 UI

切版拆解#

在了解 emotion 的基本使用後,現在就可以實際使用 emotion 來完成臺灣好天氣的 UI !

這個單元最後會完成的畫面如下:

Imgur

note

「臺灣好天氣」的設計畫面主要是參考 imgur 上的圖片 (https://imgur.com/ZLgiOyj),另外則會使用 IconFinder 上 The Weather is Nice Today 所提供的天氣圖示來完成(https://www.iconfinder.com/iconsets/the-weather-is-nice-today)。

這裡我們將根據下圖拆分成不同的 HTML 區塊:

Imgur

Location 元件#

以 Location 這個區塊為例,我們預期它會是個 div 元素,因此要建立帶有樣式的元件,只需要:

// 定義帶有樣式的 `<Location />` 元件
// 在兩個反引號中放入該 Component 的 CSS 樣式
const Location = styled.div`
font-size: 28px;
color: #212121;
margin-bottom: 20px;
`;

定義好之後,它就是一個 React 元件,可以直接把 <Location /> 放入 JSX 中:

function App() {
return (
<Container>
<WeatherCard>
<Location>台北市</Location>
</WeatherCard>
</Container>
);
}

而它最後在 HTML 中呈現出來就會是帶有一個特殊 class name 的 <div>,這個 class name(即下圖中的 css-a7vwns)則會對應到剛剛針對 <Location> 所撰寫的 CSS 樣式:

imgur

完成其他區塊的 Styled Components#

接下來可以繼續根據本篇最上方的架構,完成其他的 styled components,因為 CSS 的內容並不是本書的重點,為了減少不必要的篇幅,大家可以直接到下方的連結將 styled components 的部分複製到 ./src/App.js 中即可:

Imgur

https://github.com/pjchender/learn-react-from-hook-realtime-weather-app/blob/create-ui/public/create-ui-styled-components.js

建立好帶有樣式的元件後,就可以根據上一個段落中架構好的區塊,依序把這些元件放入 App 元件的 JSX 中:

// ./src/App.js
// ...
const App = () => {
return (
<Container>
<WeatherCard>
<Location>台北市</Location>
<Description>多雲時晴</Description>
<CurrentWeather>
<Temperature>
23 <Celsius>°C</Celsius>
</Temperature>
</CurrentWeather>
<AirFlow> 23 m/h </AirFlow>
<Rain> 48% </Rain>
<Refresh> 最後觀測時間:上午 12:03 </Refresh>
</WeatherCard>
</Container>
);
};

此時應該可以看到如下的畫面:

Imgur

到目前還未載入任何和天氣有關的圖示。

在 React 中載入 SVG 圖示的方法#

在前面的單元中,我們已經把天氣圖示放到 ./src/images 的資料夾中,由於這裡我們是使用 create-react-app 這個工具建立的 React 開發環境,多數的設定 create-react-app 都已經幫開發者設定好,所以要把 SVG 載入 React 中的方式很簡單,只需要使用 create-react-app 提供的 ReactComponent 這個元件即可。

實際來看應用的方式,這裡先以白天多雲的圖示(day-cloudy.svg)為例:

  • STEP 1-1:透過 import {...} from ..../images/day-cloudy.svg 匯入
  • STEP 1-2:在 {} 中使用 create-react-app 提供的元件 ReactComponent,透過 as 可以將這個元件名稱進行修改,這裡改成 DayCloudy
  • STEP 2:最後就可以把載入的 SVG 圖示當成 React 元件( <DayCloudy />)在 JSX 中使用
// STEP 1:使用 import { ReactComponent as xxx } from xxx 載入 SVG
import { ReactComponent as DayCloudy } from './images/day-cloudy.svg';
import { ReactComponent as RainIcon } from './images/rain.svg';
const App = () => (
<div>
{/* STEP 2:直接使用該 Component */}
<DayCloudy />
<RainIcon />
</div>
);
提醒

上述這種載入 SVG 圖檔的方式需要使用 create-react-app 來建立專案才可以使用,否則需要自行在 WebPack 中建立對應的設定才行。另外,SVG 圖檔除了可以透過這裡所說的方式,做為 React 元件載入使用外,也可以直接匯入 SVG 當成圖片使用,這個部分的說明可以參考本單元放置於 Github 上的專案說明頁(分支 create-ui)。

將 SVG 圖示套用到「臺灣好天氣」#

現在就可以透過這種方式實際將天氣圖示放入 App 元件中。

天氣圖示說明#

./src/images 中已經放了許多天氣圖示,這些圖示共可會分成三類,第一類的圖示會在白天使用,檔名以 day 開頭,會以太陽作為基底;第二類的圖示則在晚上使用,檔名以 night 開頭,會以月亮作為基底;其他的圖示則沒有特別區分白天和晚上。

將需要使用的圖示載入#

現在就讓我們來把相關的圖片載入進來,這些圖示分別是「白天多雲」、「風速」、「降雨」、「重新整理」:

// ./src/WeatherApp.js
import React from 'react';
// 載入圖示
import { ReactComponent as DayCloudyIcon } from './images/day-cloudy.svg';
import { ReactComponent as AirFlowIcon } from './images/airFlow.svg';
import { ReactComponent as RainIcon } from './images/rain.svg';
import { ReactComponent as RefreshIcon } from './images/refresh.svg';
// ...

接下來就可以把這些圖示直接當成 Component 來使用,放入 JSX 中:

Imgur

現在的畫面會像這樣子:

Imgur

圖片帶進來後,因為沒有設定寬高,看起來會破版的有點嚴重。

調整 SVG 圖示的樣式#

直接使用 CSS 選擇器來調整樣式#

對於這些 SVG 的元件來說,最後轉譯到網頁的時候其實就是把 SVG 的程式碼放入 HTML 內,因此一樣可以透過 CSS 選擇器去選到對應的 SVG 後進行樣式的調整。這裡我們先調整一下 <AirFlow /><Rain /> 的部分。只須在當初定義 styled components 的地方去添加 CSS 修改 SVG 的樣式即可。

以 AirFlow 的元件來說,可以在裡面選到 svg 元素後進行樣式調整:

const AirFlow = styled.div`
/* ... */
svg {
width: 25px;
height: auto;
margin-right: 30px;
}
`;

同樣的,以 Rain 元件來說:

const Rain = styled.div`
/* ... */
svg {
width: 25px;
height: auto;
margin-right: 30px;
}
`;

重新整理的 Refresh 元件一樣可以透過這樣的方式加以調整:

const Refresh = styled.div`
/* ... */
svg {
margin-left: 10px;
width: 15px;
height: 15px;
cursor: pointer;
}
`;

現在可以看到在風速、雨量和重新整理的部分大小都已經調整好了:

Imgur

看起來已經大致上完成了,最後還希望能調整一下天氣圖示(白天多雲)的部分,因為不同的天氣圖示可能寬高會不一樣,這裡希望能夠限制一下天氣圖示的寬度,以因應不同的氣候狀況。

根據某一元件進行樣式調整#

上面我們使用 Emotion 來「建立」帶有樣式的 styled components,並在裡面透過 CSS 選擇器選到 svg 標籤後進行調整。但 Emotion 不僅可以用來建立帶有樣式的元件,還可以將「原本就存在」的元件添加樣式

什麼意思呢?

舉例來說,剛剛我們透過 import 載入的 SVG 是一個 React 元件,例如,<DayCloudyIcon /> ,現在如果我們想要為這個原本就存在的元件添加樣式時,可以使用 const 新元件 = styled(<原有元件>)這樣的寫法:

// 透過 styled(元件) 來把樣式帶入已存在的元件中
const DayCloudy = styled(DayCloudyIcon)`
/* 在這裡寫入 CSS 樣式 */
`;

也就是說,除了原本是在 styled.<html-tag> 後面加上一個 HTML 標籤,現在則是放入一個 React 元件,然後就可以在裡面撰寫 CSS 樣式,並修改該元件的樣式。

以實際的樣式來說,這裡 DayCloudy 這個元件是根據 DayCloudyIcon 這個元件而來,並且可以對它添加 CSS 樣式:

const DayCloudy = styled(DayCloudyIcon)`
flex-basis: 30%;
`;

最後在 JSX 的地方,就要把原本使用 DayCloudyIcon 改成用新的帶有樣式的元件 DayCloudy

Imgur

現在透過 npm start 後完成的畫面會像這樣:

Imgur

換你了!完成「臺灣好天氣」 UI#

現在換你來完成「臺灣好天氣」的 UI,可以參考下面的順序:

  • 了解「臺灣好天氣」切版時的區塊結構
  • 使用 emotion 建立帶有樣式的 styled components(可於網址下載撰寫好的樣式)
  • 載入 SVG 圖示到 React 元件中(包含 day-cloudy.svg, airFlow.svg, rain.svgrefresh.svg
  • 使用 CSS 選擇器的方式修改 SVG 圖示的樣式
  • 使用 emotion 修改原有 SVG 元件的樣式

提醒讀者們,關於本單元的程式碼都可以在專案的 create-ui 分支中檢視完整的程式碼,並可以點擊「時鐘」圖示即可檢視本單元程式碼有變更的部分。另外,若有補充的資訊同樣會放在該分支的專案說明頁的。


本單元使用到的連結、完整程式碼與變更部分(時鐘圖示)可於 create-ui 分支檢視:https://github.com/pjchender/learn-react-from-hook-realtime-weather-app/tree/create-ui

Imgur