# 五金行日曆

> [FIVE METAL SHOP CALENDAR 五金行日曆 2020](https://www.shoppingdesign.com.tw/post/view/4618/)
一直很喜歡這本可愛又充滿設計感的傳統日曆,但是礙於懶人個性一定放到明年還在第一頁
太可愛了也會捨不得撕,趁著這次練習 Date(),不如就自己手刻一本。
## 重點語法
撰寫語法使用 SASS,如果看不習慣的話可以轉譯成 CSS
主要會頻繁使用到以下語法:
* **flex**
* **transform: translate、skew、scale**
* **background: linear-gradient**
* **box-shadow**
* **text-shadow**
**<font color="red">*這次 CodePen 設定 CSS Base 有開啟 Reset</font>**
## 一、分析架構
開始切版之前,首先要分析架構
就像蓋房子一樣,地基要打得穩,房子才能蓋的堅固
如果是初學者,建議可以試著在紙上畫出網頁架構
**外框容器**
* calendar:日曆主體,用 `after` 製作厚度
* header:上方裝訂,用 `before` 製作厚度
* hook:掛鉤部分
* body:主要呈現內容

**資料內容**
* month:上方當月月份
* date:今天日期
* fullMonth:小月曆
* week:星期、農曆
* icons:小圖示

## 二、HTML 切版
框架分析完後,就可以開始切框架
切版時先放入靜態資料,模擬放入資料的狀況,調整樣式也較準確
<font color="red">*****</font> 可以在 css 最上方加入這段,自動把所有物件加上外框
```
* { outline: 1px solid red }
```
```htmlmixed=
<div id="app">
<div class="calendar">
<div class="header">
<div class="pin"></div>
<p>2020</p>
<div class="pin"></div>
</div>
<div class="hook"></div>
<div class="body">
<div class="month">
<p>8</p>
<p>Aug</p>
</div>
<div class="date">11</div>
<div class="footer">
<div class="fullMonth">
<div class="title">August</div>
<table>
<tr>
<th>...</th>
</tr>
<tr>
<td>...</td>
</tr>
</table>
</div>
<div class="week">
<p class="en">tuseday</p>
<p class="day">星期二</p>
<p class="lunar">農6-22</p>
</div>
<div class="icons">
<div class="material-icons">wb_sunny</div>
<div class="material-icons">cloud</div>
<div class="material-icons">brightness_1</div>
</div>
</div>
</div>
</div>
</div>
```
**小月曆** 的部分會有字體沒辦法再縮小的問題:
1. 可以用 `transform: scale` 來縮小整體
2. 再用 `transform-rigin` 調整中心位置
3. 加上 `ransform: translate` 調整位移
初步完成如下圖:

## 三、用偽元素製作細節
框架完成後,為避免框架上有太多元件
裝飾性質的東西可以用 `before`、`after` 來完成 ( 下圖綠色部分 )
**月曆厚度** 可以用 `transform: skew` 製作傾斜效果
先用 `scale` 放大會比較好對齊,邊調整位置和角度,讓兩條厚度可以接在一起

## 四、陰影運用
陰影 `box-shadow` 的運用是繪製實物時最重要的部分,可以分為幾種方式:
### 基本陰影堆疊
陰影可以無限堆疊,並且設定不同位移、強度、顏色
依照畫面的需求堆疊出陰影,增加立體感

### 利用陰影製作重複元素
陰影除了堆疊,也可以用來製作複製元素
利用 **位移變化** 和相同 **陰影強度(模糊效果)**,可以做出頁邊色票
魔鬼藏在細節中,切版最重要的就是細心和耐心
推薦文章: [CSS 模糊效果](https://www.oxxostudio.tw/articles/201407/css-blur.html)
```sass
box-shadow: 0 0 2px 1px #79746B, 1px*1 -22px*1 2px 1px #A07F2E, 1px*2 -22px*2 2px 1px #884940, 1px*3 -22px*3 2px 1px #216164, 1px*4 -22px*4 2px 1px #853555, 1px*5 -22px*5 2px 1px #628279, 1px*6 -22px*6 2px 1px #493B62, 1px*7 -22px*7 2px 1px #772C22, 1px*8 -22px*8 2px 1px #436251, 1px*9 -22px*9 2px 1px #0B6575, 1px*10 -22px*10 2px 1px #99834C, 1px*11 -22px*11 2px 1px #4D4229, 1px*12 -22px*12 2px 1px #89425A, 1px*13 -22px*13 2px 1px #5A4C6A, 1px*14 -22px*14 2px 1px #975B4B
```

## 五、抓取時間
加上顏色和相關細節設定後,就要來加入時間內容
### 日期、星期
```javascript=
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
const days = ['日', '一','二','三','四','五','六']
const en = ['sunday', 'monday','tuseday','wednesday','thursday','friday','saturday']
const today = new Date()
const yy = today.getFullYear()
const mm = today.getMonth()
const dt = today.getDate()
const dy = today.getDay()
function render (){
getElemt('.header p').innerHTML = `${yy}`
getElemt('.month').innerHTML = `
<p>${mm + 1}</p>
<p>${months[mm].substring(0, 3)}</p>` // 取出前三個字元
getElemt('.date').innerHTML = `${dt}`
getElemt('.fullMonth .title').innerHTML = `${months[mm]}`
getElemt('.week .day').innerHTML = `星期${days[dy]}`
getElemt('.week .en').innerHTML = `${en[dy]}`
}
```
### 電子鐘
```javascript=
function timer (){
let newTime = new Date()
let h = newTime.getHours()
let m = newTime.getMinutes()
let s = newTime.getSeconds()
let hour = ((h < 10) ? '0' : '') + h
let min = ((m < 10) ? '0' : '') + m
let sec = ((s < 10) ? '0' : '') + s
getElemt('.timer').innerHTML = `${hour}:${min}:${sec}`
}
setInterval(timer, 1000)
```
## 六、製作小月曆
### 取得當月份總天數
* 當月的第32天 - 當天日期 = 當月最後一天
* 舉栗:8月的第32天為9/1,32-1=31
* 得到8月的最後一天為31號,當月有31天
```javascript=
function daysInMonth(m, y) {
return 32 - new Date(y, m, 32).getDate()
}
```
### 小月曆本人
參考資料:[Challenge of building a Calendar with Pure JavaScript](https://link.medium.com/1UTiIVbCP8)
```javascript=
function calendar (year, month){
let firstDay = (new Date(year, month)).getDay()
// 取得當月第一天
let date = 1
// 當月第一天/日期,起始值
for( let r=0; r<6; r++ ) { // 最多6行
let row = createElemt('tr')
let cell, cellText
for ( let i=0; i<7; i++) {
// 一星期7天,最多7列
if( r === 0 && i < firstDay ) {
// 第0列,且當週 < 7天,填補空格
cell = createElemt('td')
row.appendChild(cell)
} else if ( date > daysInMonth(month, year) ) {
// 若當天日期 > 當月最後一天,跳出現在迴圈
break
} else {
cell = createElemt('td')
cellText = document.createTextNode(date)
// 補上空格並填入日期
if( year === yy && month === mm && date === dt ) {
cell.classList.add('now')
// 今天日期加上 now
}
cell.appendChild(cellText)
row.appendChild(cell)
date ++
}
}
getElemt('.fullMonth table').appendChild(row)
}
}
```
### 農曆計算
參考資料:[1900年至2100年公历、农历互转Js代码](https://blog.jjonline.cn/userInterFace/173.html)
## 七、關燈效果

運用 `text-shadow` 的堆疊方式就可以做出文字發光效果
開關的部分我用 `checkbox` 和 `label` 做簡單的切換
* HTML
```htmlmixed=
<div class="lightBtn">
<input id="btn" type="checkbox"/>
<label for="btn">
<span class="off">關燈</span>
<span class="on">開燈</span>
</label>
</div>
```
* SASS
```sass=
#btn
display: none
// 隱藏 checkbox
#btn:checked ~ label
// 點選 checkbox 後,變更 label 內設定
.off
// 關燈樣式 ON
.on
// 開燈樣式 OFF
```
* JavaScript
```javascript=
const btn = getElemt('#btn')
btn.addEventListener('click', () => {
if( btn.checked ) {
getElemt('body').classList.add('active')
} else {
getElemt('body').classList.remove('active')
}
})
```
---
DEMO:[Calendar 五金行日曆](https://codepen.io/ericadu/full/GRZJjjE)