# 新聞網頁製作(五)- Responsive Web Design 響應式網頁
## Slides
https://hackmd.io/HOasqwrrSSWbTUKtf4HYNw?view#/1
## 學習大綱:
- [新聞網頁製作 (一) - HTML 標記式語言](https://hackmd.io/svhFYKq4QyaarC_J0WRsig)
- [新聞網頁製作(二) - CSS 串接樣式表](https://hackmd.io/Pekdv0mvT8qD_LXLzUo9iQ)
- [新聞網頁製作(三)- CSS 選擇器與基礎網頁排版](https://hackmd.io/ccO-gvxFR5-49q3ePoNJkg)
- [新聞網頁製作(四)- CSS Position 與 Flexbox 排版](https://hackmd.io/i1uBelrpRv2Uz5emQawIiw)
- [新聞網頁製作(五)- Responsive Web Design 響應式網頁](https://hackmd.io/ojTXG2s0RQaJz85goqIz1w)
- [新聞網頁製作(六)- 多媒體元素與動態效果](https://hackmd.io/J_YYk1YUSum1x3fVo75PXA)
- [新聞網頁製作(七)- 使用 GitHub 部署網頁](https://hackmd.io/P9N34oIwS2G6Mfhag3Rqzg)
## 前言

*圖1. 響應式網頁範例*
在早期,我們只有電腦可以呈現網頁,因此對於網頁的版型並不會考慮太多,只要電腦螢幕可以呈現就好。
然而,隨著手機和平板的推出和普及,大部分的使用者,不再單單使用電腦瀏覽網頁,使用者使用手機的頻率逐年增加。對於很多網站來說,來自手機的流量已經超越桌機。
為了滿足手機和平板使用者的需求,許多網站紛紛開發出不同載具的網頁。當一個網頁,可以同時在桌機、平板和手機上呈現,即稱之為「響應式網頁」。
## 讓網頁根據視窗(viewport)大小來呈現
在手機和平板出現之前,網頁是為電腦量身訂做的,在設定上有一定的大小,並不適合在手機和平板上呈現。如下圖2.,我們會發現,即便網頁的寬設定成 375px ,在 iPhoneX 上看到的網頁,並沒有撐滿整個裝置。

*圖2.用手機呈現網頁,會發現網頁變小了,網頁並沒有撐滿 375px 的 viewport。*
為了解決網頁在手機和平板上呈現的問題,我們得在`<head>`裡面加入`<meta name="viewport" content="width=device-width, initial-scale=1.0">`。
```
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
```
透過 `<meta>`,我們告訴瀏覽器要如何根據載具的大小來呈現網頁,見圖3.。

*圖3. 透過 `<meta>`,瀏覽器採用載具的大小來呈現網頁。`*
## 響應式網頁製作
為了讓網頁能夠同時在不同的裝置(手機、平板、桌機)上呈現, CSS 提供了 Media Queries。
利用 Media Queries,我們可以在不同的螢幕大小(Viewport),選擇套用不同的 CSS 樣式。
如此一來,我們便可以在同一個網頁上,根據不同的載具,呈現不同的版型和風格。
## Media Queries 語法
若要使用 Media Queries,有以下三種寫法:
- **在 HTML 裡面使用:**
在`link` 元素中,加入 `media` 屬性。
```htmlembedded=false
<link rel="stylesheet" type="text/css" href="style.css" media="screen">
<link rel="stylesheet" type="text/css" href="style-for-print.css" media="print">
```
- **在 CSS 中使用**
```htmlembedded=false
@media screen {
/* CSS 樣式 */
}
@media print {
/* CSS 樣式 */
}
```
- **使用@import**
```
@import "style.css";
@import "style-for-print.css" print;
```
上述三種寫法,針對的是 Media 的類型。
Media 類型是大方向的分類,若我們很細部地使用 CSS 樣式,可以使用 Media 的特徵。
#### Media 類型(Media Types)
| 類型 | 說明 |
| -------- | -------- |
| all | 預設值。針對所有的裝置。 |
| print | 印刷裝置,包含預覽列印的PDF。|
|screen|螢幕裝置,包括手機、平板和桌機等。|
|speech|朗讀裝置。|
#### Media 特徵(Media Features)
| 特徵 | 說明 |
| -------- | -------- |
| height | 螢幕(viewport)高度 |
| min-height | 螢幕最小高度|
| max-heigth| 螢幕最大高度|
| width | 螢幕寬度|
| min-width|螢幕最小寬度|
| max-width|螢幕最大寬度|
|aspect-ratio|螢幕長寬比例|
|min-aspect-ratio|螢幕最小長寬比例|
|max-aspect-ratio|螢幕最大長寬比例|
|orientation|螢幕旋轉方向:**portrait**(直擺) 和 **landscape** (橫擺)|
以上 Media 特徵,我只列出常用的特徵。然而,還有許多 Media 特徵沒有提及。關於更多的 Media 特徵,推薦各位看 [CSS Media Queries 詳細介紹](https://www.oxxostudio.tw/articles/201810/css-media-queries.html),裡面有詳細的 Media 特徵的說明。
### 如何在 CSS 中使用 Media Queries?
在實務上,我們時常將 Media Queries 套用在 CSS 裡(使用 `@media`)。
接著,我們透過幾個例子來了解 `@media` 的使用方式。
##### 針對 Media 類別
```css=false
/* 針對螢幕裝置,h1 的字級大小為 60px */
@media screen {
h1 {
font-size: 60px;
}
}
/* 針對印刷裝置,h1 的字級大小為 48px */
@media print {
h1 {
font-size: 48px;
}
}
```
##### 針對 Media 特徵
```css=false
/* 針對螢幕寬度為 768px 的裝置 */
@media (width: 768px) {
h1 {
font-size: 60px;
}
}
/* 針對螢幕最小寬度為 768px 的裝置,意即螢幕寬度大於 768px 的都套用 */
@media (min-width: 768px) {
h1 {
font-size: 60px;
}
}
/* 針對螢幕最大寬度為 768px 的裝置,意即螢幕寬度小於 768px 的都套用 */
@media (max-width: 768px) {
h1 {
font-size: 60px;
}
}
/* 針對螢幕長寬比為 1:1 的裝置 */
@media (aspect-ratio: 1/1) {
h1 {
font-size: 60px;
}
}
/* 針對螢幕最大長寬比為 1024:1366 的裝置 */
@media (max-aspect-ratio: 1024/1366) {
h1 {
font-size: 60px;
}
}
/* 針對橫擺的裝置 */
@media (orientation: landscape) {
h1 {
font-size: 60px;
}
}
```
##### 在 Media 類型前使用 not 或是 only
```css=false
/* 針對非螢幕裝置時套用 */
@media not screen {
h1 {
font-size: 60px;
}
}
/* 只針對螢幕裝置,且螢幕寬度為 768px 的套用 */
/* 加上 only 的主要原因是為了避免不支援 Media 特徵的老舊瀏覽器不小心套用到樣式而寫。 */
/* 然而,現今的瀏覽器都有支援 Media 特徵的寫法,因此可以不用特別寫 only 了。 */
@media only screen and (width: 768px) {
h1 {
font-size: 60px;
}
}
```
##### 將特徵和類型使用 and 組合起來使用
```css=false
/* 針對螢幕裝置,且螢幕裝置的最小寬度為 768px 時套用 */
@media screen and (min-width: 768px) {}
/* 針對螢幕裝置,且螢幕裝置的最小寬度為 768px,且螢幕裝置橫擺時套用 */
@media screen and (min-width: 768px) and (orientation: landscape) {}
```
##### 將特徵和類型使用 or 組合起來使用
```css=false
/* 針對印刷裝置,或是螢幕裝置的最小寬度為 768px 時套用 */
@media print, (min-width: 768px) {}
/* 針對印刷裝置,或是螢幕裝置的最小寬度為 768px,或是螢幕裝置橫擺時套用 */
@media print, (min-width: 768px), (orientation: landscape) {}
```
##### 將特徵和類型使用 or、and 組合起來使用
```css=false
/*
* 針對
* 1.印刷裝置
* 2.螢幕裝置的最小寬度為 768px 且螢幕橫擺
* 時套用
*/
@media print, (min-width: 768px) and (orientation: landscape) {}
```
## break points
在設計和程式上,我們可以事先設定螢幕(viewport) break points,<br/>
當螢幕的寬超過 break points 時,就讓網站的採取更適合的 CSS style。<br/>
一般來說,break points 可以根據載具的寬來設定。
下面範例,是 Bootstrap CSS 定義的 breakpoints。
```css=false
/* Small devices (landscape phones, 576px and up) */
@media (min-width: 576px) {}
/* Medium devices (tablets, 768px and up) */
@media (min-width: 768px) {}
/* Large devices (desktops, 992px and up) */
@media (min-width: 992px) {}
/* Extra large devices (large desktops, 1200px and up) */
@media (min-width: 1200px) {}
```
當然,你也可以設定你自己覺得適合的 break points。
舉例而言,報導者的 break points 設定如下:
```css=false
/* Mobile */
@media (max-width: 767px) {}
/* Tablet */
@media (min-width: 768px) and (max-width: 1023px) {}
/* Desktop */
@media (min-width: 1024px) and (max-width: 1439px){}
/* HD */
@media (min-width: 1440px) {}
```
## 響應式圖片(Responsive Image)
針對圖片,我們使用的是 `<img>` 元素,透過 `height` 和 `width` 屬性,我們可以指定圖片的寬和高。例如:`<img src="800x600.jpg" width="800" height="600">`。
若我們不指定寬和高的話,瀏覽器會依照該圖片的原始寬高呈現。
然而,當我們指定圖片的寬和高時,圖片就不會隨著瀏覽器的視窗(viewport)動態調整。
為了讓圖片的大小能隨著螢幕大小或瀏覽器視窗動態調整,以下有幾種做法:
### 1. 使用 `width` 樣式
```css=false
img {
width: 100%;
heigth: auto;
}
```
或是
```htmlembedded=false
<img width="100%" height="auto" src="https://github.com/nickhsine/teach-at-nccu/raw/master/assets/images/image-1.jpg">
```
當圖片的寬度設定成百分比(%)時,圖片的寬度會隨著它要呈現的範圍動態調整大小,如下圖4。

*圖4:透過 `width: 100%; height: auto` 的方式使圖片動態調整大小*
### 2. 使用 `max-width` 樣式
使用 `width:100%` 可以讓圖片動態調整。
但有時候,圖片寬和長不夠大,讓圖片動態調整大小,可能會讓圖片看起來解析度不足,糊糊的。
倘若我們想限制圖片的大小,當圖片放大到某個程度後,就不再放大,我們可以使用 `max-width: 800px` 搭配 `width: 100%`(`800px` 是示意,你可以換成你想要的寬度),來限制圖片的寬度。
```css=false
/* css */
img {
max-width: 800px;
}
```
```htmlembedded=false
<!-- html -->
<img width="100%" heigth="auto" src="https://github.com/nickhsine/teach-at-nccu/raw/master/assets/images/image-1.jpg">
```
上面的程式碼會產生圖5的結果。

*圖5:透過 `max-width: 100%` 的方式使設定圖片動態調整的上限。*
### 3. 使用 Background Image
在[新聞網頁製作(二) - CSS 串接樣式表](https://hackmd.io/Pekdv0mvT8qD_LXLzUo9iQ)的內容裡,我們有教過`background-image`、`background-size`、`background-position` 等樣是,而透過這些樣式的組合,我們亦可讓圖片動態調整大小。
#### 3.1 使用 `background-size: contain`
當 `background-size` 樣式設定成 `contain` 時,背景圖會動態調整大小,試圖讓背景的圖完整呈現在內容區塊中,該圖不會被裁切,亦不會被調整長寬比。
```css=1
div.bg-size-contain {
width: 100%;
height: 400px;
background-image: url('https://github.com/nickhsine/teach-at-nccu/raw/master/assets/images/image-1.jpg');
background-repeat: no-repeat;
background-size: contain;
background-position: center;
}
```
上述程式碼可以產生圖6。

*圖6:使用 `background-size: contain`,讓圖片動態調整大小。*
#### 3.2 使用 `background-size: cover`
使用 `background-size: contain` 的好處是圖片不會被裁切,能完整呈現在背景。
然而,我們有時候會需要圖片完整填滿內容區塊。若有這個需求時,我們可以將 `background-size` 設定成 `cover`。
當 `background-size: cover` 時,瀏覽器會裁切圖片,但保留圖片的長寬比,接著將圖片等比例放大或是縮小,完整填滿內容區塊。
```css=1
div.bg-size-cover {
width: 100%;
height: 400px;
background-image: url('https://github.com/nickhsine/teach-at-nccu/raw/master/assets/images/image-1.jpg');
background-repeat: no-repeat;
background-size: cover;
background-position: center;
}
```
上述程式碼能產生圖7。

*圖7:使用 `background-size: cover`,讓圖片動態調整大小。*
#### 4. 使用`object-fit`
除了用 `background-image` 適當裁切照片,好符合瀏覽器的視窗大小之外,`object-fit` 也可達成類似效果的樣式。
透過 CSS 裡的 `object-fit`,我們可以簡易地針對照片採用幾種裁切方式。
`object-fit: none`: 不將圖片縮小,直接置入欲呈現圖片的區塊內。
`object-fit: fill`: 不保證圖片原始的比例,直接將圖片撐滿欲呈現圖片的區塊。
`object-fit: cover`: 保持圖片原始的比例,透過縮小或放大置入欲呈現圖片區塊,若欲呈現圖片的區塊的長寬比與圖片不同,圖片則不會完整呈現。
`object-fit: contain`: 保持圖片原始的比例,置入欲呈現圖片區塊,若欲呈現圖片的區塊的長寬比與圖片不同,則會留白。
`object-fit: scale-down`: 對圖片依序使用 `none` 和 `contain`,最終呈現選擇尺寸比較小的那個。
請見一下程式碼範例:
<iframe height="300" style="width: 100%;" scrolling="no" title="Untitled" src="https://codepen.io/nickhsine/embed/KKGQMXX?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/nickhsine/pen/KKGQMXX">
Untitled</a> by nickhsine (<a href="https://codepen.io/nickhsine">@nickhsine</a>)
on <a href="https://codepen.io">CodePen</a>.
</iframe>
### 不同載具用不同圖片
當我們了解圖片的呈現方式和 Media Queries 後,我們可以組合這兩個技巧,讓瀏覽器在不同的螢幕大小,呈現不同的圖片。
#### 1. 使用 `background-image`
```css=false
div.bg-size-cover {
width: 100%;
height: 400px;
background-image: url('https://github.com/nickhsine/teach-at-nccu/raw/master/assets/images/image-1.jpg');
background-repeat: no-repeat;
background-size: cover;
background-position: center;
}
@media screen and (max-width: 600px) {
div.bg-size-cover {
background-image: url('https://github.com/nickhsine/teach-at-nccu/raw/master/assets/images/image-2.jpg');
}
}
```
當內容區塊小於600px後,會呈現另外一張圖片。
見圖8。

*圖8:`background-image` 搭配 Media Queries,讓瀏覽器在不同大小時,載入不同的圖片。*
#### 2. 使用 `<picture>` 搭配 `<source>`
除了使用 Background Image 搭配 Media Queries 之外,我們也可以使用 `<picutre>` 元素搭配 `<source>` 元素,來根據不同的條件載入不同的圖片。
```htmlembedded=false
<picture>
<source media="(max-width:600px)" srcset="https://github.com/nickhsine/teach-at-nccu/raw/master/assets/images/image-2.jpg">
<img src="https://github.com/nickhsine/teach-at-nccu/raw/master/assets/images/image-1.jpg">
</picture>
```
上述程式碼預設會載入`<img>` 元素。但當瀏覽器視窗小於 600px 時,瀏覽器會載入`<source media="(max-width:600px)">` 元素的圖片。
在 `<picture>` 裡,`<source>` 元素可以不只一個,因此我們可以針對不同的 Media 類型和Media 特徵,載入不同的圖片。
### RWD 課堂練習:
<!-- https://www.figma.com/file/hDxOKN9XajmZDAPKV0mSrT/course-practice-media-queries?node-id=0%3A1 -->
https://www.figma.com/design/lCr3y28UIOLA0APFTVAJqT/course-practice-media-queries?t=c2sX69M0j9Jq702c-0
下載檔案:
https://github.com/nickhsine/teach-at-nccu/raw/master/111-02/05-09/exercise.zip
下載範例檔案:
https://github.com/nickhsine/teach-at-nccu/raw/master/110-02/05-03/exercises/2022-05-03-exercise-finish.zip
###### tags: `teach-at-nccu`, `css`, `html`