├── .gitignore
├── 01 JavaScript Drum Kit
├── README.MD
├── index.css
├── index.html
└── original
│ ├── index-FINISHED.html
│ ├── index-START.html
│ ├── sounds
│ ├── boom.wav
│ ├── clap.wav
│ ├── hihat.wav
│ ├── kick.wav
│ ├── openhat.wav
│ ├── ride.wav
│ ├── snare.wav
│ ├── tink.wav
│ └── tom.wav
│ └── style.css
├── 02 JS and CSS Clock
├── README.MD
├── index.html
└── original
│ ├── index-FINISHED.html
│ └── index-START.html
├── 03 CSS Variables
├── README.MD
├── index.html
└── original
│ ├── index-FINISHED.html
│ └── index-START.html
├── 04 Array Cardio Day 1
├── README.MD
├── index.html
└── original
│ ├── index-FINISHED.html
│ └── index-START.html
├── 05 Flex Panel Gallery
├── README.MD
├── index.html
└── original
│ ├── index-FINISHED.html
│ └── index-START.html
├── 06 Type Ahead
├── README.MD
├── index.html
└── original
│ ├── index-FINISHED.html
│ ├── index-START.html
│ └── style.css
├── 07 Array Cardio Day 2
├── README.MD
├── index.html
└── original
│ ├── index-FINISHED.html
│ └── index-START.html
├── 08 Fun with HTML5 Canvas
├── README.MD
├── index.html
└── original
│ ├── index-FINISHED.html
│ └── index-START.html
├── 09 Dev Tools Domination
├── README.MD
├── index.html
└── original
│ ├── index-FINISHED.html
│ └── index-START.html
├── 10 Hold Shift and Check Checkboxes
├── README.MD
├── index.html
└── original
│ ├── index-FINISHED.html
│ └── index-START.html
├── 11 Custom Video Player
├── README.MD
├── index.html
├── original
│ ├── index.html
│ ├── scripts-FINISHED.js
│ ├── scripts.js
│ └── style.css
├── scripts.js
└── style.css
├── 12 Key Sequence Detection
├── README.MD
├── index.html
└── original
│ ├── index-FINISHED.html
│ └── index-START.html
├── 13 Slide in on Scroll
├── README.MD
├── index.html
└── original
│ ├── index-FINISHED.html
│ └── index-START.html
├── 14 JavaScript References VS Copying
├── README.MD
├── index.html
└── original
│ ├── index-FINISHED.html
│ └── index-START.html
├── 15 LocalStorage
├── README.MD
├── index.html
├── original
│ ├── index-FINISHED.html
│ ├── index-START.html
│ └── style.css
└── style.css
├── 16 Mouse Move Shadow
├── README.MD
├── index.html
└── original
│ ├── index-finished.html
│ └── index-start.html
├── 17 Sort Without Articles
├── README.MD
├── index.html
└── original
│ ├── index-FINISHED.html
│ └── index-START.html
├── 18 Adding Up Times with Reduce
├── README.MD
├── index.html
└── original
│ ├── index-FINISHED.html
│ └── index-START.html
├── 19 Webcam Fun
├── README.MD
├── index.html
├── original
│ ├── index.html
│ ├── package.json
│ ├── scripts-FINISHED.js
│ ├── scripts.js
│ └── style.css
├── package.json
├── scripts.js
└── style.css
├── 20 Speech Detection
├── README.MD
├── index.html
├── original
│ ├── index-FINISHED.html
│ ├── index-START.html
│ └── package.json
└── package.json
├── 21 Geolocation
├── README.MD
├── index.html
├── original
│ ├── index-FINISHED.html
│ ├── index-START.html
│ └── package.json
└── package.json
├── 22 Follow Along Link Highlighter
├── README.MD
├── index.html
├── original
│ ├── index-FINISHED.html
│ ├── index-START.html
│ └── style.css
└── style.css
├── 23 Speech Synthesis
├── README.MD
├── index.html
├── npm-debug.log
├── original
│ ├── index-FINISHED.html
│ ├── index-START.html
│ └── style.css
└── style.css
├── 24 Sticky Nav
├── README.MD
├── index.html
├── original
│ ├── index-FINISHED.html
│ ├── index-START.html
│ ├── style-FINISHED.css
│ └── style-START.css
└── style.css
├── 25 Event Capture, Propagation, Bubbling and Once
├── README.MD
├── index.html
└── original
│ ├── index-FINISHED.html
│ └── index-START.html
├── 26 Stripe Follow Along Nav
├── README.MD
├── index.html
└── original
│ ├── index-FINISHED.html
│ └── index-START.html
├── 27 Click and Drag
├── README.MD
├── index.html
├── original
│ ├── index-FINISHED.html
│ ├── index-START.html
│ └── style.css
└── style.css
├── 28 Video Speed Controller
├── README.MD
├── index.html
├── original
│ ├── index-FINISHED.html
│ ├── index-START.html
│ └── style.css
└── style.css
├── 29 Countdown Timer
├── README.MD
├── index.html
├── original
│ ├── index.html
│ ├── scripts-FINISHED.js
│ ├── scripts-START.js
│ └── style.css
├── scripts.js
└── style.css
├── 30 Whack A Mole
├── README.MD
├── dirt.svg
├── index.html
├── mole.svg
├── original
│ ├── dirt.svg
│ ├── index-FINISHED.html
│ ├── index-START.html
│ ├── mole.svg
│ └── style.css
└── style.css
├── README.MD
├── Readme.MD
└── index.html
/.gitignore:
--------------------------------------------------------------------------------
1 | */node_modules
2 |
--------------------------------------------------------------------------------
/01 JavaScript Drum Kit/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 一個鍵盤爵士鼓
4 | - 功能
5 | - 按下鍵盤,出現相對應的聲音
6 | - 畫面
7 | - 按下鍵盤,畫面上的字母方塊,做點小動畫
8 | - 加上黃色陰影
9 | - 按鈕彈一下(微小放大)的動畫
10 |
11 | # Javascript
12 |
13 | ## Arrow Function
14 | - [lambda](http://www.jollen.org/blog/2013/10/javascript-lambda.html) 在Javascript裡叫 `arrow function`
15 |
16 | 一般js的匿名函數這樣寫
17 | ```javascript=
18 | funtion (key) {
19 | key.addEventListener('transitionend', removeTransition);
20 | }
21 | ```
22 | 用`arrow function`的寫法就是
23 | ```javascript=
24 | key => key.addEventListener('transitionend', removeTransition)
25 | ```
26 | > 不同之處在於`this`,一般的寫法有`this`=`function`本身,但是「誰」呼叫`arrow function`,「誰」=`this`。
27 |
28 | ## Window方法
29 |
30 | jsref: [The Window Object](https://www.w3schools.com/jsref/obj_window.asp)
31 | - [`setTimeout(function, ms)`](https://www.w3schools.com/jsref/met_win_settimeout.asp) 設定「執行一次」的function與延遲時間
32 |
33 | ## DOM Document 方法
34 |
35 | jsref: [The HTML DOM Document Object](https://www.w3schools.com/jsref/dom_obj_document.asp)
36 |
37 | - `document.querySelector()`找到第一個符合指定CSS selector(s)的HTML節點
38 | - `document.querySelectorAll()`找到符合指定CSS selector(s)的HTML節點們
39 |
40 | ## DOM Element 方法
41 |
42 | jsref: [The HTML DOM Element Object](https://www.w3schools.com/jsref/dom_obj_all.asp)
43 |
44 | - `element.addEventListener()` 加一個事件到Element上
45 | - `element.classList` 回傳NodeList,element上的class屬性的內容
46 |
47 | ## NodeList 方法
48 | jsref: [JavaScript HTML DOM Node List](https://www.w3schools.com/js/js_htmldom_nodelist.asp)
49 |
50 | - `NodeList.forEach()`
51 |
52 | `querySelectorAll()`出來的物件都是`NodeList`,它不是`Array`,能使用的方法不同,在此使用`forEach()`走訪每一個元素。
53 |
54 | 參考: [`Array.forEach`](https://www.w3schools.com/jsref/jsref_forEach.asp)
55 |
56 | ## Event Object 方法
57 |
58 | jsref: [HTML DOM Events](https://www.w3schools.com/jsref/dom_obj_event.asp)
59 |
60 |
61 | - [`Transition Events`](https://www.w3schools.com/jsref/event_transitionend.asp) 過場結束(transition end)執行 myFunction
62 |
63 | 「過場」唯一事件
64 |
65 | ```javascript=
66 | element.addEventListener("transitionend", myFunction)
67 | ```
68 |
--------------------------------------------------------------------------------
/01 JavaScript Drum Kit/index.css:
--------------------------------------------------------------------------------
1 | .key {
2 | width: 32px;
3 | height: 32px;
4 |
5 | border: solid 1px black;
6 |
7 | display: flex;
8 | justify-content: center;
9 | align-items: center;
10 |
11 | margin-top: 5px;
12 |
13 | transition: all 0.07s;
14 | }
15 |
16 | .playing {
17 | width: 100px;
18 | }
19 |
--------------------------------------------------------------------------------
/01 JavaScript Drum Kit/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JavaScript Drum Kit
6 |
7 |
8 |
9 |
10 | q
11 |
12 |
13 | w
14 |
15 |
16 | e
17 |
18 |
19 | r
20 |
21 |
22 | t
23 |
24 |
25 | y
26 |
27 |
28 | u
29 |
30 |
31 | i
32 |
33 |
34 | o
35 |
36 |
37 | p
38 |
39 |
40 | a
41 |
42 |
43 | s
44 |
45 |
46 | d
47 |
48 |
49 | f
50 |
51 |
52 | g
53 |
54 |
55 | h
56 |
57 |
58 | j
59 |
60 |
61 | k
62 |
63 |
64 | l
65 |
66 |
67 | ;
68 |
69 |
70 | z
71 |
72 |
73 | x
74 |
75 |
76 | c
77 |
78 |
79 | v
80 |
81 |
82 | b
83 |
84 |
85 | n
86 |
87 |
88 | m
89 |
90 |
91 | ,
92 |
93 |
94 | .
95 |
96 |
97 | /
98 |
99 |
100 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/01 JavaScript Drum Kit/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JS Drum Kit
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | A
14 | clap
15 |
16 |
17 | S
18 | hihat
19 |
20 |
21 | D
22 | kick
23 |
24 |
25 | F
26 | openhat
27 |
28 |
29 | G
30 | boom
31 |
32 |
33 | H
34 | ride
35 |
36 |
37 | J
38 | snare
39 |
40 |
41 | K
42 | tom
43 |
44 |
45 | L
46 | tink
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/01 JavaScript Drum Kit/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JS Drum Kit
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | A
14 | clap
15 |
16 |
17 | S
18 | hihat
19 |
20 |
21 | D
22 | kick
23 |
24 |
25 | F
26 | openhat
27 |
28 |
29 | G
30 | boom
31 |
32 |
33 | H
34 | ride
35 |
36 |
37 | J
38 | snare
39 |
40 |
41 | K
42 | tom
43 |
44 |
45 | L
46 | tink
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/01 JavaScript Drum Kit/original/sounds/boom.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/boom.wav
--------------------------------------------------------------------------------
/01 JavaScript Drum Kit/original/sounds/clap.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/clap.wav
--------------------------------------------------------------------------------
/01 JavaScript Drum Kit/original/sounds/hihat.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/hihat.wav
--------------------------------------------------------------------------------
/01 JavaScript Drum Kit/original/sounds/kick.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/kick.wav
--------------------------------------------------------------------------------
/01 JavaScript Drum Kit/original/sounds/openhat.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/openhat.wav
--------------------------------------------------------------------------------
/01 JavaScript Drum Kit/original/sounds/ride.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/ride.wav
--------------------------------------------------------------------------------
/01 JavaScript Drum Kit/original/sounds/snare.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/snare.wav
--------------------------------------------------------------------------------
/01 JavaScript Drum Kit/original/sounds/tink.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/tink.wav
--------------------------------------------------------------------------------
/01 JavaScript Drum Kit/original/sounds/tom.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/tom.wav
--------------------------------------------------------------------------------
/01 JavaScript Drum Kit/original/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-size: 10px;
3 | background: url(http://i.imgur.com/b9r5sEL.jpg) bottom center;
4 | background-size: cover;
5 | }
6 | body,html {
7 | margin: 0;
8 | padding: 0;
9 | font-family: sans-serif;
10 | }
11 |
12 | .keys {
13 | display: flex;
14 | flex: 1;
15 | min-height: 100vh;
16 | align-items: center;
17 | justify-content: center;
18 | }
19 |
20 | .key {
21 | border: .4rem solid black;
22 | border-radius: .5rem;
23 | margin: 1rem;
24 | font-size: 1.5rem;
25 | padding: 1rem .5rem;
26 | transition: all .07s ease;
27 | width: 10rem;
28 | text-align: center;
29 | color: white;
30 | background: rgba(0,0,0,0.4);
31 | text-shadow: 0 0 .5rem black;
32 | }
33 |
34 | .playing {
35 | transform: scale(1.1);
36 | border-color: #ffc600;
37 | box-shadow: 0 0 1rem #ffc600;
38 | }
39 |
40 | kbd {
41 | display: block;
42 | font-size: 4rem;
43 | }
44 |
45 | .sound {
46 | font-size: 1.2rem;
47 | text-transform: uppercase;
48 | letter-spacing: .1rem;
49 | color: #ffc600;
50 | }
51 |
--------------------------------------------------------------------------------
/02 JS and CSS Clock/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 做一個漂亮甜美的時鐘,一開始就提供了一個鐘面,也寫好了三支指針
4 |
5 | - 功能
6 | - 顯示目前的時間
7 | - 更新時針、分針、秒針
8 | - 畫面
9 | - 讀得到目前的時間
10 | - 秒針要有「真實的滴答」動作
11 | - 追加規格
12 | - 指針要有長短(或粗細)可以分辨彼此
13 | - 解決最後的歸零問題
14 |
15 | # CSS
16 |
17 | cssref: [CSS3 transform Property](https://www.w3schools.com/cssref/css3_pr_transform.asp)
18 |
19 | ### 變化(`transform`)
20 |
21 | 可以進行2D和3D的變化,屬性後面可以接幾種能使用的函數
22 |
23 | 1D變化(其實有些是2D適用, 有些是3D適用)
24 | - `translateX(x)` 平移
25 | - `translateY(y)` 平移
26 | - `scaleX(x)` 放大
27 | - `scaleY(y)` 放大
28 | - `scaleZ(z)` 放大
29 | - `rotateX(angle)`, 轉
30 | - `rotateY(angle)`, 轉
31 | - `rotateZ(angle)`, 轉
32 | - `skewX(angle)`, 歪斜
33 | - `skewY(angle)`, 歪斜
34 |
35 | 2D變化
36 | - `matrix(n1,n2,n3,n4,x,y)`
37 | - **n1~n4**:歪斜(看起來像翻轉), 矩形變平行四邊形
38 | - **x,y**: 平移
39 | - `translate(x,y)`, 平移
40 | - `scale(x,y)`, 放大
41 | - `rotate(angle)`, 轉
42 | - `skew(x-angle,y-angle)`, 歪斜
43 |
44 | 3D變化
45 | - `matrix3d
46 | (n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n)`, 歪斜+平移
47 | - `ranslate3d(x,y,z)`, 平移
48 | - `scale3d(x,y,z)`, 放大
49 | - `rotate3d(x,y,z,angle)`, 轉
50 | - `perspective(n)` 透視
51 |
52 | 在此用得到的
53 | ```css=
54 | /*變化: 旋轉(7°);*/
55 | transform: rotate(7deg);
56 | ```
57 |
58 | ### 「過場時」的變化函數(`transition-timing-function`)
59 |
60 | cssref: [CSS3 transition-timing-function Property](https://www.w3schools.com/cssref/css3_pr_transition-timing-function.asp)
61 |
62 | 變化的函數,可以設成有回彈效果
63 |
64 | 可用函數(看參考資料了解比較快)
65 | - `ease`
66 | - `linear`
67 | - `ease-in` 類似`cubic-bezier(0.42,0,1,1))`
68 | - `ease-out` 類似`cubic-bezier(0,0,0.58,1))`
69 | - `ease-in-out` 類似`cubic-bezier(0.42,0,0.58,1))`
70 | - `step-star`
71 | - `step-en`
72 | - `steps(int,start|end)`
73 | - `cubic-bezier(n,n,n,n)`, [貝玆函數](http://cubic-bezier.com/#.17,.67,.83,.67) 算是自訂變化曲線函數
74 |
75 | ```css=
76 | transition-timing-function: 函數
77 | ```
78 |
79 | ### 參考資料
80 | - [linear, ease-in, ease-out, ease-in-out](https://developers.google.com/web/fundamentals/design-and-ui/animations/the-basics-of-easing?hl=zh-tw)
81 | - [transition-timing-function的各種Demo - MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-timing-function)
82 |
83 | # Javascript
84 |
85 | ## Window object 方法
86 |
87 | - [`setInterval(function, ms)`](https://www.w3schools.com/jsref/met_win_setinterval.asp) 設定「持續執行」的function與間隔時間
88 |
89 | ## 秒針角度歸零時,不會抖一下
90 |
91 | 秒針抖一下是因為角度歸零。
92 |
93 | 讓角度**不歸零**就可以避免。
94 |
--------------------------------------------------------------------------------
/02 JS and CSS Clock/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JS + CSS Clock
6 |
7 |
8 |
9 |
10 |
17 |
18 |
19 |
71 |
72 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/02 JS and CSS Clock/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JS + CSS Clock
6 |
7 |
8 |
9 |
10 |
17 |
18 |
19 |
68 |
69 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/03 CSS Variables/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 做一個"brand spanking new"的CSS, 這裡的「CSS變數」不是Sass的變數功能。畫面有三個變數: `Space`, `Blur`, `Base Color`,當我們在畫面設定值(拉動range或改變顏色),它會立即的更新畫面
4 |
5 | - 功能
6 | - 用CSS變數
7 | - 追加
8 | - 練習「設定區域變數」
9 | - 畫面
10 | - 改變控制項、立即改變畫面
11 |
12 |
13 | CSS變數可以被Javascript更新, 也就是當你可以在頁面上任何地方更新CSS變數, 變數就可以自動更新。
14 |
15 | > 我知道你會說「wes, we've had variables in SASS forever and this not new」
16 |
17 | 在SASS, 你可以定義、編譯,在變譯之後將無法再改變變數(們)
18 |
19 | ### 參考資料
20 | - [brand spanking new](http://www.phrases.org.uk/meanings/brand-spanking-new.html) 意思是 "Entirely new"(全新的)
21 | - [smart Alec](http://ce.linedict.com/dict.html#/cnen/entry/764905?source=en) 意思是 「自以為是,自作聰明的人」
22 |
23 | # HTML
24 |
25 | ## 嵌入自訂資料進HTML
26 |
27 | - [HTML data-\* Attributes](https://www.w3schools.com/tags/att_global_data.asp)
28 |
29 | 在HTML裡嵌入自定義的`color`資料
30 | ```htmlembedded=
31 |
32 | ```
33 | 在Javascript取出來的方法,在`element`取`dataset`就可以用相同的「自定義名稱」讀/寫 自定義資料了,資料型態是字串。
34 | ```javascript=
35 | tagNameElement.dataset.color = "";
36 | ```
37 |
38 | # CSS
39 |
40 | ## CSS Style Declaration Object 方法
41 |
42 | jsref: [The CSSStyleDeclaration Object](https://www.w3schools.com/jsref/obj_cssstyledeclaration.asp)
43 | - `setProperty(property, value, priority)` 設定CSS的屬性給element(直接加在`style=""`)
44 |
45 |
46 | ### 宣告變數
47 | 使用兩個`-`符號,代表「變數」
48 |
49 | 自定義變數名稱`--main-bg-color`, 值`brown`
50 | ```
51 | element {
52 | --main-bg-color: brown;
53 | }
54 | ```
55 | ### JS修改CSS的宣告
56 |
57 | ```Javascript=
58 | element.style.setProperty('--main-bg-color', 'brown');
59 | ```
60 |
61 | ### CSS使用變數
62 | 使用`var()`代表「使用變數」,並且將「宣告名稱」丟進來
63 |
64 | 將變數`--main-bg-color`給定進`background-color`
65 | ```
66 | element {
67 | background-color: var(--main-bg-color);
68 | }
69 | ```
70 |
71 |
72 |
73 | ## 取HTML element
74 |
75 | 用CSS的`:root`選擇器,可以選到JS的`document.documentElement`
76 |
77 | 在HTML中,root element就是``
78 |
79 | - [CSS3 :root Selector](https://www.w3schools.com/cssref/sel_root.asp)
80 |
81 | CSS這樣宣告
82 | ```=
83 | :root {
84 | --base: #ff6c00;
85 | --spacing: 10px;
86 | --blur: 10px;
87 | }
88 | ```
89 | JS就可以這樣改寫
90 | ```Javascript=
91 | document.documentElement.style.setProperty(`--${this.name}`, this.value);
92 | ```
93 | ### 參考資料
94 | - [CSS變數的瀏覽器支援](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables)
95 |
96 |
97 | # Javascript
98 |
99 | ## DOM方法
100 |
101 | jsref: The HTML DOM Document Object
102 |
103 | - [`document.documentElement`](https://www.w3schools.com/jsref/prop_document_documentelement.asp) 回傳``的節點
104 |
--------------------------------------------------------------------------------
/03 CSS Variables/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Update CSS Variables with
9 | JS
10 |
11 |
12 |
13 | Spacing:
14 |
15 |
16 | Blur:
17 |
18 |
19 | Base Color
20 |
21 |
22 |
23 |
24 |
62 |
63 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/03 CSS Variables/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Scoped CSS Variables and JS
6 |
7 |
8 | Update CSS Variables with JS
9 |
10 |
11 | Spacing:
12 |
13 |
14 | Blur:
15 |
16 |
17 | Base Color
18 |
19 |
20 |
21 |
22 |
23 |
61 |
62 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/03 CSS Variables/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Scoped CSS Variables and JS
6 |
7 |
8 | Update CSS Variables with JS
9 |
10 |
11 | Spacing:
12 |
13 |
14 | Blur:
15 |
16 |
17 | Base Color
18 |
19 |
20 |
21 |
22 |
23 |
46 |
47 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/04 Array Cardio Day 1/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 希望你今天有出汗的心理準備, 我們不會做什麼漂亮的東西
4 |
5 | 沒有CSS沒有DOM element, 就是Javascript基本的Array方法運用
6 |
7 | 包含`filter`, `map`, `sort`, `reduce`這些方法
8 |
9 | 在此提供了`inventors`, `people`陣列, 有一些題目, 每一題會指定一個Array Method解題
10 |
11 | > 第六題: 需要進入[Category:Boulevards in Paris](
12 | https://en.wikipedia.org/wiki/Category:Boulevards_in_Paris) 頁面,打開`console`進行解題
13 |
14 | # JavaScript
15 |
16 | ## Array 方法
17 |
18 | jsref: [JavaScript Array Reference](https://www.w3schools.com/jsref/jsref_obj_array.asp)
19 | - [`Array.filter()`](https://www.w3schools.com/jsref/jsref_filter.asp) 過濾, 函數回傳
20 | - `true`: 表示「符合條件」
21 | - `false`: 表示「不符合條件」
22 | - [`Array.reduce()`](https://www.w3schools.com/jsref/jsref_reduce.asp) 累積, 函數通常是「累加」
23 | - `reduce(function(total, everyone, currIndex, currArray), initValue)`
24 | - total, 跨元素的暫存變數
25 | - everyone, 每一個元素
26 | - currIndex, 目前的索引值
27 | - 有給initValue就0起始, 初始值自訂
28 | - 沒有initValue就1起始, 初始值為第一個元素
29 | - currArray, 整個陣列
30 | - initValue, 初始值\
31 | - `Array.from(NodeList)` 將NoteList轉成Array
32 | - 也可以用`[...NodeList]`
33 | - [`Array.sort()`](https://www.w3schools.com/jsref/jsref_sort.asp) 排序, 可用數字相減值回傳, 函數回傳值
34 | - +1: 大於
35 | - 0: 等於
36 | - -1: 小於
37 |
38 | ### 特別說一下`sort`
39 |
40 | [`Array.sort()`](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
41 |
42 | ```javascript=
43 | b.sort(function(a, b){ return b - a;});//[576, 56, 12, 9, 8, 8, 5, 3, 1, 0]
44 | b.sort(function(a, b){ return a - b;});//[0, 1, 3, 5, 8, 8, 9, 12, 56, 576]
45 | ```
46 | > 建議使用`-`而不是用`>`或`<`。
47 | > 原因:
48 | > - 用`-`回傳`>0`, `=0`, `<0`
49 | > - 用`>`或`<`回傳`true`, `false`
50 | > [參考資料](http://stackoverflow.com/questions/1063007/how-to-sort-an-array-of-integers-correctly)
51 |
52 |
53 | ## String 方法
54 | - `String.includes('keywork')` 找字串是否有包含`keywork`
55 |
--------------------------------------------------------------------------------
/04 Array Cardio Day 1/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Array Cardio 💪
6 |
7 |
8 | Psst: have a look at the JavaScript Console 💁
9 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/05 Flex Panel Gallery/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 今天要做一個超級迷人,我喜歡稱之為flex gallery。
4 |
5 | 沒什麼Javascript、很多的CSS用flexbox、用transitions
6 |
7 | - 功能
8 | - 點擊指定的區塊要以它為主, 畫面變化
9 | - 再點擊一次,恢復原狀
10 | - 畫面
11 | - 主要區塊變大, 中間的字變大
12 | - 變大之後上下的字要進來
13 |
14 | # CSS
15 |
16 | ## flexbox
17 |
18 | ### 學習資源
19 |
20 | - [Flex.io](https://www.flex.io/)
21 | - [FLEX - CSS-TRICKS](https://css-tricks.com/almanac/properties/f/flex/)
22 |
23 | ## flex
24 |
25 | 容器要先設定 `display: flex;`
26 |
27 | 元素要設定`flex: 1 0 auto;`
28 |
29 | ### flex語法解釋
30 |
31 | 要解譯這一個之前,必須了解一個詞叫「剩餘空間」([超推薦看!!出處](http://zhoon.github.io/css3/2014/08/23/flex.html))
32 |
33 | > `justify-content` 的屬性會失效
34 |
35 | `flex`是下面三者的速寫
36 | The second and third parameters (flex-shrink and flex-basis) are optional.
37 | 1. [`flex-grow`](https://css-tricks.com/almanac/properties/f/flex-grow/) 剩餘空間分配比例
38 | 2. [`flex-shirk`](https://css-tricks.com/almanac/properties/f/flex-shrink/) 壓縮比例
39 | 3. [`flex-basis`](https://css-tricks.com/almanac/properties/f/flex-basis/) 預約剩餘空間
40 |
--------------------------------------------------------------------------------
/05 Flex Panel Gallery/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Flex Panels 💪
6 |
7 |
8 |
9 |
74 |
75 |
76 |
77 |
78 |
Hey
79 |
Let's
80 |
Dance
81 |
82 |
83 |
Give
84 |
Take
85 |
Receive
86 |
87 |
88 |
Experience
89 |
It
90 |
Today
91 |
92 |
93 |
Give
94 |
All
95 |
You can
96 |
97 |
98 |
Life
99 |
In
100 |
Motion
101 |
102 |
103 |
104 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/06 Type Ahead/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 要做一個我喜歡稱為`type-ahead feature`(提示功能), 在此我們有一個擁有歡迎程度的cities和states列表, 當我們keyin一些關鍵字,此功能可以幫我找到在cities和states裡符合關鍵字的項目。
4 |
5 | - 功能
6 | - 列出符合關鍵字的項目
7 | - 畫面
8 | - 列出符合關鍵字的項目
9 | - 將關鍵字的部份mark起來
10 | - 顯示該項目的「歡迎程度」數字
11 |
12 |
13 | # Javascript
14 |
15 | > Keyword: [blob](https://zh.wikipedia.org/wiki/%E4%BA%8C%E9%80%B2%E4%BD%8D%E5%A4%A7%E5%9E%8B%E7%89%A9%E4%BB%B6) 二進位大型物件
16 |
17 | > 二進位大型物件(英語:binary large object ,或英語:basic large object,縮寫為Blob、BLOB、BLOb),在資料庫管理系統中,將二進位資料儲存為一個單一個體的集合。Blob通常是影像、聲音或多媒體檔案。
18 | >
19 | > 它由迪吉多公司的工程師吉姆·史塔基(Jim Starkey)發明。
20 |
21 | ## API
22 | - [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) 一種非同步的..API, 向後端要資料, 回傳`promise`
23 | - [response object](https://developer.mozilla.org/en-US/docs/Web/API/Response)
24 | - [promise](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise) 非同步回傳的結果
25 | - [promise.then()](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) 處理 `promise`的方法
26 |
27 | ## JavaScript RegExp Reference
28 |
29 | jsref: [RegExp Object](https://www.w3schools.com/jsref/jsref_obj_regexp.asp)
30 |
31 | 正規表示式, 常用於文字(字串)的過濾、搜尋、取代方法
32 |
33 | ### 使用`replace`將字mark起來
34 |
35 | 下面的例子, 將`key=this.value`
36 | ```javascript=
37 | const regex = new RegExp(this.value, 'gi');
38 | str.replace(regex, `${this.value} `);
39 | ```
40 |
--------------------------------------------------------------------------------
/06 Type Ahead/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Type Ahead 👀
6 |
7 |
8 |
9 |
10 |
17 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/06 Type Ahead/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Type Ahead 👀
6 |
7 |
8 |
9 |
10 |
17 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/06 Type Ahead/original/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | background:#ffc600;
4 | font-family:'helvetica neue';
5 | font-size: 20px;
6 | font-weight: 200;
7 | }
8 | *, *:before, *:after {
9 | box-sizing: inherit;
10 | }
11 | input {
12 | width: 100%;
13 | padding:20px;
14 | }
15 |
16 | .search-form {
17 | max-width:400px;
18 | margin:50px auto;
19 | }
20 |
21 | input.search {
22 | margin: 0;
23 | text-align: center;
24 | outline:0;
25 | border: 10px solid #F7F7F7;
26 | width: 120%;
27 | left: -10%;
28 | position: relative;
29 | top: 10px;
30 | z-index: 2;
31 | border-radius: 5px;
32 | font-size: 40px;
33 | box-shadow: 0 0 5px rgba(0, 0, 0, 0.12), inset 0 0 2px rgba(0, 0, 0, 0.19);
34 | }
35 |
36 |
37 | .suggestions {
38 | margin: 0;
39 | padding: 0;
40 | position: relative;
41 | /*perspective:20px;*/
42 | }
43 | .suggestions li {
44 | background:white;
45 | list-style: none;
46 | border-bottom: 1px solid #D8D8D8;
47 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.14);
48 | margin:0;
49 | padding:20px;
50 | transition:background 0.2s;
51 | display:flex;
52 | justify-content:space-between;
53 | text-transform: capitalize;
54 | }
55 |
56 | .suggestions li:nth-child(even) {
57 | transform: perspective(100px) rotateX(3deg) translateY(2px) scale(1.001);
58 | background: linear-gradient(to bottom, #ffffff 0%,#EFEFEF 100%);
59 | }
60 | .suggestions li:nth-child(odd) {
61 | transform: perspective(100px) rotateX(-3deg) translateY(3px);
62 | background: linear-gradient(to top, #ffffff 0%,#EFEFEF 100%);
63 | }
64 |
65 | span.population {
66 | font-size: 15px;
67 | }
68 |
69 | .hl {
70 | background:#ffc600;
71 | }
72 |
73 | a {
74 | color:black;
75 | background:rgba(0,0,0,0.1);
76 | text-decoration: none;
77 | }
78 |
--------------------------------------------------------------------------------
/07 Array Cardio Day 2/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 今天是Array Cardio 2(陣列有氧運動2), 我們將學到新的一些Array methods。
4 |
5 | 有兩個陣列 `people`、`comments`, 完成題目的要求。
6 |
7 | 題目以`Array.prototype.method()`為分界。
8 |
9 | 看一下題目多長是下一題
10 |
11 | 會練習到這些方法
12 | - `Array.prototype.some()`
13 | - `Array.prototype.every()`
14 | - `Array.prototype.find()`
15 | - `Array.prototype.findIndex()`
16 |
17 | # JavaScript
18 |
19 | ## Array 方法
20 | jsref: [JavaScript Array Reference](https://www.w3schools.com/jsref/jsref_obj_array.asp)
21 | - [`Array.some()`](https://www.w3schools.com/jsref/jsref_some.asp) Checks if any of the elements in an array pass a test
22 | - [`Array.every()`](https://www.w3schools.com/jsref/jsref_every.asp) Checks if every element in an array pass a test
23 | - [`Array.find()`](https://www.w3schools.com/jsref/jsref_find.asp) Returns the value of the first element in an array that pass a test
24 | - [`Array.findIndex()`](https://www.w3schools.com/jsref/jsref_findindex.asp) Returns the index of the first element in an array that pass a test
25 | - [`Array.slice()`](https://www.w3schools.com/jsref/jsref_slice_array.asp) Selects a part of an array, and returns the new array
26 | - [`Array.splice()`](https://www.w3schools.com/jsref/jsref_splice.asp) Adds/Removes elements from an array
27 |
28 | ## 超接近的字
29 | - slice 切片
30 | - splice 拼接
31 |
32 | > delete 會造成undefine, index不連續
33 | 用splice就不會,(用新的array接舊有的元素)
34 |
--------------------------------------------------------------------------------
/07 Array Cardio Day 2/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Array Cardio 💪💪
6 |
7 |
8 | Psst: have a look at the JavaScript Console 💁
9 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/07 Array Cardio Day 2/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Array Cardio 💪💪
6 |
7 |
8 | Psst: have a look at the JavaScript Console 💁
9 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/07 Array Cardio Day 2/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Array Cardio 💪💪
6 |
7 |
8 | Psst: have a look at the JavaScript Console 💁
9 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/08 Fun with HTML5 Canvas/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 今天要做一個甜美的 `HTML5 canvas`
4 |
5 | - 功能
6 | - 可以畫圖的書布
7 | - 粗細變化
8 | - 顏色變化
9 | - 畫面
10 | - 邊畫邊換色(只有新畫出來的換色)
11 | - 邊畫邊換粗細
12 | - 停下來,再繼續畫,粗細和顏色與上次停筆相同
13 |
14 | # HSL色彩空間
15 |
16 | [小工具](http://mothereffinghsl.com/)
17 |
18 | # Javascript
19 |
20 | ## [`HTML5 Canvas`](https://www.w3schools.com/html/html5_canvas.asp)
21 |
22 | ```javascript=
23 | var htmlCanvas = document.getElementById("myCanvas");
24 | var canvasContext = htmlCanvas.getContext("2d");
25 | canvasContext.beginPath();
26 | canvasContext.arc(95,50,40,0,2*Math.PI);
27 | canvasContext.stroke();
28 | ```
29 |
30 |
31 |
32 | ## [CanvasRenderingContext2D](https://developer.mozilla.org/zh-TW/docs/Web/API/CanvasRenderingContext2D)
33 |
34 | ```javascript=
35 | var htmlCanvas = document.getElementById("myCanvas");
36 | var ctx = htmlCanvas.getContext("2d");
37 | ```
38 |
39 | - ctx.strokeStyle = '#BADA55'; 線的顏色
40 | - Color or style to use for the lines around shapes.
41 | - Default #000 (black).
42 | - ctx.lineJoin = 'round';
43 | - Defines the type of corners where two lines meet.
44 | - Possible values: round, bevel, miter (default).
45 | - ctx.lineCap = 'round';
46 | - Type of endings on the end of lines.
47 | - Possible values: butt (default), round, square.
48 | - ctx.beginPath();
49 | - Starts a new path by emptying the list of sub-paths.
50 | - Call this method when you want to create a new path.
51 | - [ctx.globalCompositeOperation](https://developer.mozilla.org/zh-TW/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation) 有光學合成效果
52 |
--------------------------------------------------------------------------------
/08 Fun with HTML5 Canvas/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | HTML5 Canvas
6 |
7 |
8 |
9 |
62 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/08 Fun with HTML5 Canvas/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | HTML5 Canvas
6 |
7 |
8 |
9 |
65 |
66 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/08 Fun with HTML5 Canvas/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | HTML5 Canvas
6 |
7 |
8 |
9 |
11 |
12 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/09 Dev Tools Domination/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 這是一個開發工具和console技巧的快速教學影片。
4 |
5 | 一段教學,一段練習的方式進行
6 |
7 | # Go!!!
8 |
9 | 可以在HTML element下中斷點
10 |
11 | 直接看code就知道了!!!
12 |
13 | ## Regular (常見的正規用法)
14 | 印出常數
15 | ```
16 | console.log('aaa');
17 | ```
18 | 印出變數
19 | ```
20 | var a = 'aaa';
21 | console.loglog(a);
22 | ```
23 |
24 | ## 格式化輸出的用法 Interpolated (以內插值替換)
25 |
26 | 像C語言的 `printf()` 這樣使用
27 |
28 | ```
29 | console.log('%s', aaa);
30 | ```
31 |
32 | ### 用ES6的 template string (ES6 back-ticks)
33 |
34 | ```
35 | var a = 'ES6';
36 | console.log(`hello ${a}`);
37 | ```
38 |
39 | ## 用inline CSS (Styled)
40 |
41 | 用Interpolated時,可以使用style定義
42 |
43 | ```
44 | console.log("%cDon't do this", 'font-size: 48pt; color: red;');
45 | ```
46 |
47 | 用HTML表示就等同於在console.log印出這樣的字
48 |
49 | ```
50 | Don't do this
51 | ```
52 |
53 | ## 強調訊息類型
54 | Warning!
55 | ```
56 | console.warn('Oh Nooo');
57 | ```
58 | Error
59 | ```
60 | console.error('Oh Nooo');
61 | ```
62 | Info
63 | ```
64 | console.info('this is info');
65 | ```
66 |
67 | ## 測試常用 (Testing)
68 |
69 | ### Assert
70 |
71 | ```
72 | console.assert(1===1, 'this is wronign');
73 | console.assert(1===2, 'this is wronign');
74 | ```
75 | ## 清掉之前印出的東西 (clearing)
76 | 清掉目前 `console` 裡的東西
77 |
78 | `console.clear();`
79 |
80 | ## 印出內容
81 | Viewing DOM Elements 印出 可展開的node
82 |
83 | ### 印出簡單變數
84 | `console.log(p);`
85 |
86 | #### 印出來要「群組化顯示」
87 | Grouping together log群組化印出
88 |
89 | 預設展開
90 | ```
91 | console.group(`${dog.name}`);
92 | //放入你要印出來的內容(簡單變數)....
93 | console.groupEnd(`${dog.name}`);
94 | ```
95 | 預設縮起來
96 | ```
97 | console.groupCollapsed(`${dog.name}`);
98 | //放入你要印出來的內容(簡單變數)....
99 | console.groupEnd(`${dog.name}`);
100 | ```
101 |
102 | ### 印出物件
103 |
104 | ```
105 | var aObject = {
106 | name: 'chris',
107 | age: 33
108 | }
109 | console.dir(aObject);
110 | ```
111 |
112 | ### 印出陣列
113 |
114 | ```
115 | var aArray = [{
116 | name: 'chris',
117 | age: 33
118 | },
119 | {
120 | name: 'chris',
121 | age: 28
122 | }]
123 | console.table(aArray);
124 | ```
125 |
126 | ## 計數 (counting)
127 |
128 | 立即印出「內容」和「出現次數」
129 |
130 | ```
131 | console.count('aaaaaa');
132 | console.count('bbbbbb');
133 | console.count('aaaaaa');
134 | console.count('bbbbbb');
135 | console.count('aaaaaa');
136 | console.count('bbbbbb');
137 | ```
138 |
139 | ## 計時 (timing)
140 |
141 | ```
142 | console.time('fetching data');
143 | fetch('https://api.github.com/users/webos')
144 | .then(data => data.json())
145 | .then(data => {
146 | console.timeEnd('fetching data');
147 | console.log(data);
148 | });
149 | ```
150 |
--------------------------------------------------------------------------------
/09 Dev Tools Domination/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Console Tricks!
8 |
9 |
10 |
11 | ×BREAK×DOWN×
12 |
13 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/09 Dev Tools Domination/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Console Tricks!
6 |
7 |
8 |
9 | ×BREAK×DOWN×
10 |
11 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/09 Dev Tools Domination/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Console Tricks!
6 |
7 |
8 |
9 | ×BREAK×DOWN×
10 |
11 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/10 Hold Shift and Check Checkboxes/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 今天的題目算是a bit of a tough one。
4 |
5 | 一開始看到一個checkbox們,你對其中一個打勾,再按住`shift`對另一個打勾,中間的選項會自動勾起來。
6 |
7 | 不管是由上選下來,還是由下選上來
8 |
9 | - 功能
10 | - 像Gmail選取郵件這樣,可以先選一個,再「按住`shift`」勾選,進行多選的效果。
11 | - 畫面
12 | - 滑鼠點覽checkbox,要打勾,再點擊則取消
13 | - 「按住`shift`」勾選,若畫面上已有打勾,則此次和上次打勾的選項中間都要自動打勾
14 | - 追加
15 | - 第三次打勾...自己要勾起來,已勾過的到自己中間的也都要勾起來,但是己勾過的不能改變。
16 |
17 | # Javascript
18 |
19 | ## 程式撰寫思維
20 |
21 | > 以前都寫C++。
22 | > 物件長怎樣 就要依序存取進去,由外而內。
23 |
24 | 不同於以往的程式撰寫思維。
25 | 在此,程式操作只要針對` `
26 | 所以,影片中就直接把` `抓出來處理。
27 |
28 | > 長得相同,實則不同
29 |
30 | 在`querySelector`完之後,列出來的`
56 |
62 |
63 |
64 |
65 |
This is an inbox layout.
66 |
67 |
68 |
69 |
Check one item
70 |
71 |
72 |
73 |
Hold down your Shift key
74 |
75 |
76 |
77 |
Check a lower item
78 |
79 |
80 |
81 |
Everything inbetween should also be set to checked
82 |
83 |
84 |
85 |
Try do it with out any libraries
86 |
87 |
88 |
89 |
Just regular JavaScript
90 |
91 |
92 |
93 |
Good Luck!
94 |
95 |
96 |
97 |
Don't forget to tweet your result!
98 |
99 |
100 |
101 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/11 Custom Video Player/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 今天要做一個`HTML5 video`播放器,要試著改變video的預設style。
4 |
5 | 每一個不同的browser看起來的預設介面都不太相同。
6 |
7 | 預設介面隱藏控制介面,還會剩下一些調整選項。
8 |
9 | - 功能
10 | - 和預設介面相同的功能
11 | - play、pause、跳到指定時間、音量調整、~~全螢幕~~、播放速度、往前10秒、往後5秒
12 | - 畫面
13 | - 依題目程式碼給的
14 | - 縮起來剩下進度條(已給)
15 | - Hover整個出現(已給)
16 |
17 |
18 | # HTML
19 |
20 | ## HTML Video
21 |
22 | html tag: (HTML Audio/Video DOM Reference)[https://www.w3schools.com/tags/ref_av_dom.asp]
23 |
24 | ### Event
25 | - click
26 | - pause
27 | - play
28 | - timeupdate
29 |
30 | ### 設計原則
31 |
32 | > 使用者操作->
33 | 觸發介面元件事件->改變真實程式物件->
34 | 觸發程式物件事件->改變介面
35 |
36 | 將介面與程式切割開來
37 |
38 | ## 更棒的寫法
39 |
40 | ```javascript
41 | let mousedown = false;
42 | progress.addEventListener('click', scrub);
43 | progress.addEventListener('mousemove', (e) => {
44 | if (mousedown)
45 | scrub(e);
46 | });
47 | progress.addEventListener('mousedown', () => mousedown = true);
48 | progress.addEventListener('mouseup', () => mousedown = false);
49 |
50 | ```
51 | ```javascript
52 | let mousedown = false;
53 | progress.addEventListener('click', scrub);
54 | progress.addEventListener('mousemove', (e) => mousedown && scrub(e));
55 | progress.addEventListener('mousedown', () => mousedown = true);
56 | progress.addEventListener('mouseup', () => mousedown = false);
57 | ```
58 |
--------------------------------------------------------------------------------
/11 Custom Video Player/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | HTML Video Player
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
►
18 |
19 |
20 |
« 10s
21 |
25s »
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/11 Custom Video Player/original/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | HTML Video Player
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
►
18 |
19 |
20 |
« 10s
21 |
25s »
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/11 Custom Video Player/original/scripts-FINISHED.js:
--------------------------------------------------------------------------------
1 | /* Get Our Elements */
2 | const player = document.querySelector('.player');
3 | const video = player.querySelector('.viewer');
4 | const progress = player.querySelector('.progress');
5 | const progressBar = player.querySelector('.progress__filled');
6 | const toggle = player.querySelector('.toggle');
7 | const skipButtons = player.querySelectorAll('[data-skip]');
8 | const ranges = player.querySelectorAll('.player__slider');
9 |
10 | /* Build out functions */
11 | function togglePlay() {
12 | const method = video.paused ? 'play' : 'pause';
13 | video[method]();
14 | }
15 |
16 | function updateButton() {
17 | const icon = this.paused ? '►' : '❚ ❚';
18 | console.log(icon);
19 | toggle.textContent = icon;
20 | }
21 |
22 | function skip() {
23 | video.currentTime += parseFloat(this.dataset.skip);
24 | }
25 |
26 | function handleRangeUpdate() {
27 | video[this.name] = this.value;
28 | }
29 |
30 | function handleProgress() {
31 | const percent = (video.currentTime / video.duration) * 100;
32 | progressBar.style.flexBasis = `${percent}%`;
33 | }
34 |
35 | function scrub(e) {
36 | const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration;
37 | video.currentTime = scrubTime;
38 | }
39 |
40 | /* Hook up the event listners */
41 | video.addEventListener('click', togglePlay);
42 | video.addEventListener('play', updateButton);
43 | video.addEventListener('pause', updateButton);
44 | video.addEventListener('timeupdate', handleProgress);
45 |
46 | toggle.addEventListener('click', togglePlay);
47 | skipButtons.forEach(button => button.addEventListener('click', skip));
48 | ranges.forEach(range => range.addEventListener('change', handleRangeUpdate));
49 | ranges.forEach(range => range.addEventListener('mousemove', handleRangeUpdate));
50 |
51 | let mousedown = false;
52 | progress.addEventListener('click', scrub);
53 | progress.addEventListener('mousemove', (e) => mousedown && scrub(e));
54 | progress.addEventListener('mousedown', () => mousedown = true);
55 | progress.addEventListener('mouseup', () => mousedown = false);
56 |
--------------------------------------------------------------------------------
/11 Custom Video Player/original/scripts.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/11 Custom Video Player/original/scripts.js
--------------------------------------------------------------------------------
/11 Custom Video Player/scripts.js:
--------------------------------------------------------------------------------
1 | //click view play and pause
2 | function switchPlayAndPause() {
3 | if(video_application.paused) {
4 | video_application.play();
5 | }
6 | else {
7 | video_application.pause();
8 | }
9 | }
10 | const video_application = document.querySelector('.player__video');
11 | const play_or_pause_ctrl = document.querySelector('.player__button[title="Toggle Play"]');
12 |
13 | //play button
14 | video_application.addEventListener('click', switchPlayAndPause);
15 | play_or_pause_ctrl.addEventListener('click', switchPlayAndPause);
16 |
17 | //video event update icon
18 | function updateIcon() {
19 | const ctrl = document.querySelector('.player__button[title="Toggle Play"]');
20 | if(video_application.paused) {
21 | ctrl.textContent = '>';
22 | }
23 | else {
24 | ctrl.textContent = '||';
25 | }
26 | }
27 | video_application.addEventListener('play', updateIcon);
28 | video_application.addEventListener('pause', updateIcon);
29 | play_or_pause_ctrl.textContent = '[]';
30 |
31 | //process
32 | // initial process
33 | const process = document.querySelector('.progress__filled');
34 | process.style.flexBasis = '0%';
35 | video_application.currentTime = 0;
36 |
37 | //show
38 | function updateProcessView(e) {
39 | const currWatched = ((video_application.currentTime / video_application.duration) * 100)
40 | .toPrecision(2) + '%';
41 | process.style.flexBasis = currWatched;
42 | }
43 |
44 | //change
45 | function changeProcess(e) {
46 | const currWatched = (e.layerX / process_all.clientWidth) * video_application.duration;
47 | video_application.currentTime = currWatched;
48 | }
49 |
50 |
51 | const process_all = document.querySelector('.progress');
52 | process_all.addEventListener('click', changeProcess);
53 | video_application.addEventListener('timeupdate', updateProcessView);
54 |
55 |
56 | //range ctrl
57 | function changeRange() {
58 | video_application[this.name] = this.value;
59 | }
60 |
61 | // initial volume
62 | video_application.volume = 0.5;
63 | // initial playbackRate
64 | video_application.playbackRate = 1;
65 | // playbackRate
66 | const range_ctrls = document.querySelectorAll('.player__slider');
67 | range_ctrls.forEach(ctrl => {
68 | ctrl.addEventListener('change', changeRange);
69 | ctrl.addEventListener('mousemove', changeRange);
70 | });
71 |
72 | //player__button add 10s
73 | function skip() {
74 | video_application.currentTime += Number(this.dataset.skip);
75 | }
76 |
77 | const jump_sec_ctrls = document.querySelectorAll('[data-skip]');
78 | jump_sec_ctrls.forEach(ctrl =>
79 | ctrl.addEventListener('click', skip)
80 | );
81 |
--------------------------------------------------------------------------------
/12 Key Sequence Detection/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 做一個打密碼出現彩蛋的功能,像是KONAME code這樣
4 |
5 | > 翻譯原文(大概的意思,應該與上一面描述相同)
6 | >
7 | > 做一個鍵盤輸入序列檢測(), 意思是在視窗輸入一定數量的「鍵」, 就會發生特定的事件。最耳熟能詳的就是KONAME code, 如果你在1980或1990年代時期長大, 你就知道輸入`↑, ↑, ↓, ↓, ←, →, ←, →, B, A, start`就可以觸發類似超級馬莉中無限生命的功能
8 | 若你在[網站](https://www.buzzfeed.com/)輸入`↑, ↑, ↓, ↓, ←, →, ←, →, B, A, start`, 它會將所有的字改為`Wilkie`,我不知道是誰,但是很有趣 不同的網站一些不同的復活節彩蛋,你可以去看看。
9 |
10 | 此例實做的密碼是`wesbos`, 不過練習的話可以自行定義!!
11 |
12 |
13 | # Javascript
14 |
15 | ## [`splice`](https://www.w3schools.com/jsref/jsref_splice.asp)
16 |
17 | ```javascript
18 | pressed.splice(-secretCode.length-1, pressed.length - secretCode.length);
19 | ```
20 |
21 | ## Syntax
22 | array.splice(index, howmany, item1, ....., itemX)
23 |
24 | - index < 0, 從後面數過來
25 | - howmany, 刪掉的個數
26 |
--------------------------------------------------------------------------------
/12 Key Sequence Detection/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/12 Key Sequence Detection/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Key Detection
6 |
7 |
8 |
9 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/12 Key Sequence Detection/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Key Detection
6 |
7 |
8 |
9 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/13 Slide in on Scroll/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 今天要做一個slide in on scroll, 捲到特定位置,載入特定圖片
4 |
5 | - 功能
6 | - 捲到特定位置, 載入特定圖片
7 | - 畫面
8 | - 進入畫面時, 靠左的圖, 從左邊滑入, 靠右邊的圖, 從右邊滑入
9 | - 滑入同時, 要從透明漸漸出現
10 | - 離開畫面時, 靠左的圖, 從左邊滑出, 靠右邊的圖, 從右邊滑出
11 | - 滑入同時, 要從漸漸消失至透明
12 |
13 | # Javascript
14 |
15 | ## Window Event Object
16 |
17 | ### Drag Events
18 |
19 | - [onscroll](https://www.w3schools.com/tags/ev_onscroll.asp), Script to be run when an element's scrollbar is being scrolled
20 |
21 | ## 閉包
22 |
23 | 滑動延遲反應
24 |
25 | ```javascript=
26 | function debounce(func, wait = 20, immediate = true) {
27 | var timeout;
28 |
29 | // console.log('debounce()');
30 | //滑一次執行一次A
31 | return function() {
32 | // console.log('A() start');
33 | var context = this, args = arguments; //this = window object
34 |
35 | //最後一次滑動之後執行的內容
36 | var later = function() {
37 | // console.log('laterRun() start');
38 | timeout = null;//清掉等待執行數字
39 | if (!immediate) func.apply(context, args); //沒有要立即執行的就上了
40 | // console.log('laterRun() end');
41 | };
42 |
43 | var callNow = immediate && !timeout;
44 | //true = 立即 && 無等待執行(滑動停止,時間間隔到)(每次間隔切換一次)
45 | //false = 立即 && 有等待執行(連續滑動中)
46 | //false = 不立即 && 無等待執行(滑動停止,時間間隔到)
47 | //false = 不立即 && 有等待執行(連續滑動中)
48 |
49 | //連續滑動時,延遲等待執行
50 | clearTimeout(timeout);//時間還沒到的時候,就等待取消
51 | timeout = setTimeout(later, wait); //設定等待執行, 紀錄等待執行數字
52 |
53 |
54 | if (callNow) func.apply(context, args);//如果立即執行,就用像原本這樣執行
55 | };
56 | }
57 |
58 | //...
59 | document.addEventListener('scroll', debounce(checkSlide));
60 |
61 | ```
62 |
--------------------------------------------------------------------------------
/14 JavaScript References VS Copying/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 今天來做做Javascript reference 與 copy,可以稍稍的了解一下Javascript是怎麼運作的
4 |
5 | 有些bug來自大家不了解reference在copy的特別之處,尤其是巢狀式物件
6 |
7 | 就依下面的題目一題一題做吧(我也看不懂如何分題目@@)
8 |
9 | # Javascript
10 |
11 | ## Array Clone
12 | ```javascript=
13 | const copy_players1 = players.slice();
14 | const copy_players2 = [].concat(players);
15 | const copy_players3 = [...players];
16 | const copy_players4 = Array.from(players);
17 | ```
18 |
19 | ## Object Clone
20 | ### 深層(一層)Clone
21 | - `Object.assign()` object to object
22 | ```javascript=
23 | const dev = Object.assign({}, wes);
24 | ```
25 |
26 | ### 深層(多層)Clone
27 | - [`JSON.parse()`](https://www.w3schools.com/js/js_json_parse.asp) string to json
28 | - [`JSON.stringify()`](https://www.w3schools.com/js/js_json_stringify.asp) json to string
29 |
30 | ```javascript=
31 | const dev2 = JSON.parse(JSON.stringfy(wes));
32 | ```
33 |
--------------------------------------------------------------------------------
/14 JavaScript References VS Copying/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JS Reference VS Copy
6 |
7 |
8 |
9 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/14 JavaScript References VS Copying/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JS Reference VS Copy
6 |
7 |
8 |
9 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/14 JavaScript References VS Copying/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JS Reference VS Copy
6 |
7 |
8 |
9 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/15 LocalStorage/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 今天主要學兩個東西。
4 | 1. localStorage
5 | 2. event delegation
6 |
7 | - 功能
8 | - 做一個可以輸入文字的框,送出之後,新增到上面打勾的選項
9 | - 重新整理時,選單不會清空
10 | - 畫面
11 | - 選單的選項前面,要有打勾的功能,並且自訂圖示。
12 | - 不打勾: 方塊
13 | - 打勾: 一塊"pork taco"
14 |
15 | # JavaScript
16 |
17 | ## DOM Object
18 | [HTML DOM appendChild() Method](https://www.w3schools.com/jsref/met_node_appendchild.asp) 將node放入node的裡面(下一層)。
19 |
20 | ## Event Object
21 | - [preventDefault() Event Method](https://www.w3schools.com/jsref/event_preventdefault.asp) 取消Event的預設行為
22 | - [Event.target](https://www.w3schools.com/jsref/event_target.asp) 回傳「觸發了誰」
23 |
24 | ## label 加入for
25 | ```javascript=
26 | label.setAttribute('for', input.id);
27 | ```
28 |
29 | ## 清空form
30 | ```javascript=
31 | form.reset();
32 | ```
33 | ## [`localStorage`](https://www.w3schools.com/html/html5_webstorage.asp) 物件
34 |
35 | `localStorage`比`cookie`更適合拿來做暫存的小空間。
36 |
37 | - localStorage.setItem('key', 'value');
38 | - localStorage.getItem('key');
39 | - localStorage.removeItem('key');
40 |
41 | 另外還有`sessionStorage`
42 |
43 | ## 更棒的寫法
44 |
45 | 原本的寫法
46 | ```javascript=
47 | let items = [];
48 | if (JSON.parse(localStorage.getItem('items'))){
49 | items = JSON.parse(localStorage.getItem('items'));
50 | }
51 | populateList(items, itemsList); //update view
52 |
53 | ```
54 | 改成
55 | ```javascript=
56 | const items = JSON.parse(localStorage.getItem('items')) || [];
57 | populateList(items, itemsList); //update view
58 | ```
59 |
60 | ## 問題
61 |
62 | 1. 為什麼不在label上面掛click的事件?
63 | 2. 最後加上去更新畫面的(`Line 70: populateList(items, itemsList);`)是否必要?
64 |
--------------------------------------------------------------------------------
/15 LocalStorage/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | LocalStorage
6 |
7 |
8 |
9 |
13 |
14 |
15 |
16 |
17 |
LOCAL TAPAS
18 |
19 |
20 | Loading Tapas...
21 |
22 |
26 |
27 |
28 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/15 LocalStorage/original/style.css:
--------------------------------------------------------------------------------
1 |
2 | html {
3 | box-sizing: border-box;
4 | background:url('http://wes.io/hx9M/oh-la-la.jpg') center no-repeat;
5 | background-size:cover;
6 | min-height:100vh;
7 | display:flex;
8 | justify-content: center;
9 | align-items: center;
10 | text-align: center;
11 | font-family: Futura,"Trebuchet MS",Arial,sans-serif
12 | }
13 | *, *:before, *:after {box-sizing: inherit; }
14 |
15 | svg {
16 | fill:white;
17 | background: rgba(0,0,0,0.1);
18 | padding: 20px;
19 | border-radius: 50%;
20 | width:200px;
21 | margin-bottom: 50px;
22 | }
23 |
24 | .wrapper {
25 | padding: 20px;
26 | max-width: 350px;
27 | background: rgba(255,255,255,0.95);
28 | box-shadow: 0 0 0 10px rgba(0,0,0,0.1);
29 | }
30 |
31 | h2 {
32 | text-align: center;
33 | margin: 0;
34 | font-weight: 200;
35 | }
36 |
37 | .plates {
38 | margin: 0;
39 | padding: 0;
40 | text-align: left;
41 | list-style: none;
42 | }
43 |
44 | .plates li {
45 | border-bottom: 1px solid rgba(0,0,0,0.2);
46 | padding: 10px 0;
47 | font-weight: 100;
48 | display: flex;
49 | }
50 |
51 | .plates label {
52 | flex:1;
53 | cursor: pointer;
54 |
55 | }
56 |
57 | .plates input {
58 | display: none;
59 | }
60 |
61 | .plates input + label:before {
62 | content: '⬜️';
63 | margin-right: 10px;
64 | }
65 |
66 | .plates input:checked + label:before {
67 | content: '🌮';
68 | }
69 |
70 | .add-items {
71 | margin-top: 20px;
72 | }
73 |
74 | .add-items input {
75 | padding:10px;
76 | outline:0;
77 | border:1px solid rgba(0,0,0,0.1);
78 | }
79 |
--------------------------------------------------------------------------------
/15 LocalStorage/style.css:
--------------------------------------------------------------------------------
1 |
2 | html {
3 | box-sizing: border-box;
4 | background:url('http://wes.io/hx9M/oh-la-la.jpg') center no-repeat;
5 | background-size:cover;
6 | min-height:100vh;
7 | display:flex;
8 | justify-content: center;
9 | align-items: center;
10 | text-align: center;
11 | font-family: Futura,"Trebuchet MS",Arial,sans-serif
12 | }
13 | *, *:before, *:after {box-sizing: inherit; }
14 |
15 | svg {
16 | fill:white;
17 | background: rgba(0,0,0,0.1);
18 | padding: 20px;
19 | border-radius: 50%;
20 | width:200px;
21 | margin-bottom: 50px;
22 | }
23 |
24 | .wrapper {
25 | padding: 20px;
26 | max-width: 350px;
27 | background: rgba(255,255,255,0.95);
28 | box-shadow: 0 0 0 10px rgba(0,0,0,0.1);
29 | }
30 |
31 | h2 {
32 | text-align: center;
33 | margin: 0;
34 | font-weight: 200;
35 | }
36 |
37 | .plates {
38 | margin: 0;
39 | padding: 0;
40 | text-align: left;
41 | list-style: none;
42 | }
43 |
44 | .plates li {
45 | border-bottom: 1px solid rgba(0,0,0,0.2);
46 | padding: 10px 0;
47 | font-weight: 100;
48 | display: flex;
49 | }
50 |
51 | .plates label {
52 | flex:1;
53 | cursor: pointer;
54 |
55 | }
56 |
57 | .plates input {
58 | display: none;
59 | }
60 |
61 | .plates input + label:before {
62 | content: '⬜️';
63 | margin-right: 10px;
64 | }
65 |
66 | .plates input:checked + label:before {
67 | content: '🌮';
68 | }
69 |
70 | .add-items {
71 | margin-top: 20px;
72 | }
73 |
74 | .add-items input {
75 | padding:10px;
76 | outline:0;
77 | border:1px solid rgba(0,0,0,0.1);
78 | }
79 |
--------------------------------------------------------------------------------
/16 Mouse Move Shadow/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 今天要學習滑鼠移動事件,移動滑鼠,影子要移動相對的距離
4 |
5 | - 功能
6 | - 移動滑鼠,文字的影子要移動
7 | - 畫面
8 | - 影子要隨著滑鼠相對的位子來修改距離
9 | - 追加: 模糊程度要隨著滑鼠相對的位子來修改
10 |
11 | # HTML
12 |
13 | ## Attribute
14 | - [`contenteditable`](https://www.w3schools.com/tags/att_global_contenteditable.asp) 可編輯段落
15 |
16 | # ES6語法
17 |
18 | [JavaScript ES6 中的物件解構賦值(object destructuring)](https://pjchender.blogspot.tw/2017/01/es6-object-destructuring.html)
19 |
20 | 下面的例子,就是右邊的物件屬性賦值到左邊的物件屬性。
21 |
22 | 對應「物件屬性名稱」。
23 | ```javascript=
24 | const {a: a1, b: b1} = {a: a2, b: b2};
25 | ```
26 | 等同於
27 | ```javascript=
28 | const a1 = a2;
29 | const b1 = b2;
30 | ```
31 |
32 | # Javascript
33 |
34 | ## [`text-shadow`](https://www.w3schools.com/jsref/prop_style_textshadow.asp)
35 |
36 | 用Javascript修改CSS,就要改element.style物件屬性
37 | ```javascript=
38 | object.style.textShadow = "none|h-shadow v-shadow blur color|initial|inherit"
39 | ```
40 |
41 | ## Mouse Event Coordinate
42 |
43 | [測試小頁面](https://dwatow.github.io/coordinate.html)
44 |
45 | - [movementX](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX), movementY
46 | - 距離上次Event觸發時滑鼠位置的差距
47 | - 原點: 上次的 screen(x, y)
48 | - 計算方式: currentEvent.movementX = currentEvent.screenX - previousEvent.screenX.
49 | - [clientX](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX), clientY
50 | - application client area的座標, 也就是client的可視區座標
51 | - 可視區左上角
52 | - [offsetX](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/offsetX), offsetY
53 | - 在HTML元素上的座標
54 | - 原點: 該HTML元素的左上角。
55 | - [pageX](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX), pageY
56 | - 在頁面上的位置
57 | - 原點: 該頁的左上角(捲軸捲動時,座標會改變)
58 | - [screenX](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenX), screenY
59 | - 在螢幕上的座標
60 | - 原點: 作業系統畫面(螢幕)的左上角
61 | - [x](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/x), y
62 | - client(x, y)的意思, 它只是別名
63 |
64 | ## 座標轉換
65 |
66 | 在此有一個盲點,就是`width=clientWidth`
67 | 花了一點時間弄懂...@@
68 |
69 | ```javascript
70 | const xWalk = Math.round((x / width * walk) - (walk / 2));
71 | const yWalk = Math.round((y / height * walk) - (walk / 2));
72 | ```
73 |
--------------------------------------------------------------------------------
/16 Mouse Move Shadow/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Mouse Shadow
6 |
7 |
8 |
9 |
10 |
🔥WOAH!
11 |
12 |
13 |
37 |
38 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/16 Mouse Move Shadow/original/index-finished.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Mouse Shadow
6 |
7 |
8 |
9 |
10 |
🔥WOAH!
11 |
12 |
13 |
37 |
38 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/16 Mouse Move Shadow/original/index-start.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Mouse Shadow
6 |
7 |
8 |
9 |
10 |
🔥WOAH!
11 |
12 |
13 |
36 |
37 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/17 Sort Without Articles/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 今天我們要使用`Array.sort()`
4 | 排序一個band names的Array,但是排序不考慮"The," "An,", "A"這些字眼
5 |
6 | - 功能
7 | - 排序不考慮"The," "An,", "A"這些字眼
8 | - 畫面
9 | - 顯示排序結果
10 |
11 | # Javascript
12 |
13 | ## String object
14 |
15 | [`string.trim()`](https://www.w3schools.com/jsref/jsref_trim_string.asp), Remove whitespace
16 |
17 | ## 比較相似函數名稱
18 |
19 | ### String
20 |
21 | - [`string.split(separator, limit)`](https://www.w3schools.com/jsref/jsref_split.asp)
22 | - 回傳新`array`, `string`用`separator`切割成`array`, 執行完`separator`次結束動作。
23 | - 直接`toString()`等同於用`','`取代`separator`
24 | - [`string.slice(start, end)`](https://www.w3schools.com/jsref/jsref_slice_string.asp)
25 | - 回傳新`string`, `string`切成子`string`
26 |
27 | ### Array
28 |
29 | - [`array.splice(index, howmany, item1, ....., itemX)`](https://www.w3schools.com/jsref/jsref_splice.asp)
30 | - 回傳新`array`, 從`index`移除`homany`個, 後面加上`item1`...`itemX`
31 | - [`array.slice(start, end)`](https://www.w3schools.com/jsref/jsref_slice_array.asp)
32 | - 回傳新`array`, `array`切成子`array`
33 |
--------------------------------------------------------------------------------
/17 Sort Without Articles/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Sort Without Articles
6 |
7 |
8 |
9 |
45 |
46 |
48 |
49 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/17 Sort Without Articles/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Sort Without Articles
6 |
7 |
8 |
9 |
43 |
44 |
45 |
46 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/17 Sort Without Articles/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Sort Without Articles
6 |
7 |
8 |
9 |
43 |
44 |
45 |
46 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/18 Adding Up Times with Reduce/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 今天我們要解決一些實際的問題,那就是把播放清單中的時間加起來。
4 |
5 | - 功能
6 | - 加總時間
7 | - 畫面
8 | - 依時間格式顯示`時:分:秒`
9 |
10 | # Javascript
11 |
12 | `map()`連擊!!!
13 | 可以一直將Array的元素轉成別的東西。
14 |
15 | ```javascript
16 | Array.map().map().map();
17 | ```
18 |
--------------------------------------------------------------------------------
/19 Webcam Fun/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 使用Webcam來進行一些動作
4 |
5 | - 功能
6 | - 拍照功能
7 | - 拍照檔案儲存.png檔
8 | - 拉動上面的range會改變濾鏡參數
9 | - 畫面
10 | - 右上角出現webcam的畫面
11 | - 中間的大圖是`canvas`輸出結果
12 | - 出現照片縮圖列表
13 | - 加上濾鏡(redEffect、rgbSplit、globalAlpha、Green screen)
14 |
15 | > 這一關,跳關了QQ
16 | > 下面有寫原因
17 |
18 | ## 蓋一個Web Service
19 |
20 | 原因是需要讓browser連「安全連線」
21 | 有兩個辦法
22 | 1. `https`連線
23 | 2. 連到`localhost`
24 |
25 | > 注意: 用`file://`是不包含在此範圍的
26 |
27 | 在此用node.js,安裝如下
28 |
29 | ### 安裝npm
30 |
31 | nodejs package manager
32 |
33 | 先裝npm
34 | ```bash
35 | $ sudo apt install npm
36 | ```
37 | 在專案裝npm
38 | ```bash
39 | $ npm install
40 | ```
41 | 專案資料夾中,就會出現node_modules
42 |
43 | ### 安裝nvm
44 |
45 | nodejs version manager
46 |
47 | [Github](https://github.com/creationix/nvm/blob/master/README.markdown)
48 |
49 | [安裝步驟](https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-16-04)
50 |
51 | - 安裝..
52 | ```
53 | $ sudo apt-get update
54 | $ sudo apt-get install build-essential libssl-dev
55 | ```
56 |
57 | - 下載檔案`install.sh`和`install_nvm.sh`
58 | ```
59 | $ curl -sL https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh -o install_nvm.sh
60 | ```
61 | - 安裝nvm
62 | ```
63 | $ bash install_nvm.sh
64 | ```
65 |
66 | 接下來就可以做這些事
67 | - `nvm ls-remote` 看「目前可以灌的」版號列表
68 | - `nvm ls-remote <版號>` 看「目前可以灌的」版號列表, 過濾版號
69 | - `nvm install <版號>` 指定安裝`node.js`版號
70 | - `nvm use <版號>`切到指定版號
71 | - `node -v`看目前使用的node.js版號
72 | - `nvm ls`列出目前已安裝的node.js版號
73 |
74 | `express`是`node.js`的framework
75 |
76 | ## Webcam
77 |
78 | 在此,我使用的webcam是用usb接上去的
79 | 用Chrome抓不到,但是用firefox可以抓到
80 | 但是firefox也運作不了。QQ
81 |
82 | ## 無法運作的原因
83 |
84 | `getUserMedia()`依然回傳primse, 但是它的`then`並不會執行,所以抓不到`localMesiaStream`這個東西。QQ
85 |
86 | ```javascript=
87 | function getVideo() {
88 | navigator.mediaDevices.getUserMedia({
89 | video: true,
90 | audio: false
91 | })
92 | .then(localMediaStream => {
93 | cnosole.log(localMediaStream); //this line can't run
94 | video.src = window.URL.cereateObjectURL(localMediaStream);
95 | video.play();
96 | });
97 | }
98 |
99 | getVideo();
100 |
101 | ```
102 |
103 |
104 | ## Javascript
105 |
106 | - ~~[Window Navigator](https://www.w3schools.com/js/js_window_navigator.asp)~~
107 | - [The Navigator Object](https://www.w3schools.com/jsref/obj_navigator.asp) 包含瀏覽器訊息(對網頁來說是系統訊息?)
108 |
109 |
110 | 執行這一行,瀏覽器會問你是否要分享攝影機。
111 |
112 | ```javascript=
113 | navigator.mediaDevices.getUserMedia({video: true, audio: false}); //較新的瀏覽器支援語法
114 | navigator.getUserMedia(); //舊語法
115 | ```
116 | 回傳`Promise`,後面要接`.then()`處理。
117 |
--------------------------------------------------------------------------------
/19 Webcam Fun/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Get User Media Code Along!
6 |
7 |
8 |
9 |
10 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/19 Webcam Fun/original/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Get User Media Code Along!
6 |
7 |
8 |
9 |
10 |
11 |
12 | Take Photo
13 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/19 Webcam Fun/original/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gum",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "scripts.js",
6 | "scripts": {
7 | "start": "browser-sync start --server --files \"*.css, *.html, *.js\""
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "browser-sync": "^2.12.5"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/19 Webcam Fun/original/scripts-FINISHED.js:
--------------------------------------------------------------------------------
1 | const video = document.querySelector('.player');
2 | const canvas = document.querySelector('.photo');
3 | const ctx = canvas.getContext('2d');
4 | const strip = document.querySelector('.strip');
5 | const snap = document.querySelector('.snap');
6 |
7 | function getVideo() {
8 | navigator.mediaDevices.getUserMedia({ video: true, audio: false })
9 | .then(localMediaStream => {
10 | console.log(localMediaStream);
11 | video.src = window.URL.createObjectURL(localMediaStream);
12 | video.play();
13 | })
14 | .catch(err => {
15 | console.error(`OH NO!!!`, err);
16 | });
17 | }
18 |
19 | function paintToCanvas() {
20 | const width = video.videoWidth;
21 | const height = video.videoHeight;
22 | canvas.width = width;
23 | canvas.height = height;
24 |
25 | return setInterval(() => {
26 | ctx.drawImage(video, 0, 0, width, height);
27 | // take the pixels out
28 | let pixels = ctx.getImageData(0, 0, width, height);
29 | // mess with them
30 | // pixels = redEffect(pixels);
31 |
32 | pixels = rgbSplit(pixels);
33 | // ctx.globalAlpha = 0.8;
34 |
35 | // pixels = greenScreen(pixels);
36 | // put them back
37 | ctx.putImageData(pixels, 0, 0);
38 | }, 16);
39 | }
40 |
41 | function takePhoto() {
42 | // played the sound
43 | snap.currentTime = 0;
44 | snap.play();
45 |
46 | // take the data out of the canvas
47 | const data = canvas.toDataURL('image/jpeg');
48 | const link = document.createElement('a');
49 | link.href = data;
50 | link.setAttribute('download', 'handsome');
51 | link.innerHTML = ` `;
52 | strip.insertBefore(link, strip.firsChild);
53 | }
54 |
55 | function redEffect(pixels) {
56 | for(let i = 0; i < pixels.data.length; i+=4) {
57 | pixels.data[i + 0] = pixels.data[i + 0] + 200; // RED
58 | pixels.data[i + 1] = pixels.data[i + 1] - 50; // GREEN
59 | pixels.data[i + 2] = pixels.data[i + 2] * 0.5; // Blue
60 | }
61 | return pixels;
62 | }
63 |
64 | function rgbSplit(pixels) {
65 | for(let i = 0; i < pixels.data.length; i+=4) {
66 | pixels.data[i - 150] = pixels.data[i + 0]; // RED
67 | pixels.data[i + 500] = pixels.data[i + 1]; // GREEN
68 | pixels.data[i - 550] = pixels.data[i + 2]; // Blue
69 | }
70 | return pixels;
71 | }
72 |
73 | function greenScreen(pixels) {
74 | const levels = {};
75 |
76 | document.querySelectorAll('.rgb input').forEach((input) => {
77 | levels[input.name] = input.value;
78 | });
79 |
80 | for (i = 0; i < pixels.data.length; i = i + 4) {
81 | red = pixels.data[i + 0];
82 | green = pixels.data[i + 1];
83 | blue = pixels.data[i + 2];
84 | alpha = pixels.data[i + 3];
85 |
86 | if (red >= levels.rmin
87 | && green >= levels.gmin
88 | && blue >= levels.bmin
89 | && red <= levels.rmax
90 | && green <= levels.gmax
91 | && blue <= levels.bmax) {
92 | // take it out!
93 | pixels.data[i + 3] = 0;
94 | }
95 | }
96 |
97 | return pixels;
98 | }
99 |
100 | getVideo();
101 |
102 | video.addEventListener('canplay', paintToCanvas);
103 |
--------------------------------------------------------------------------------
/19 Webcam Fun/original/scripts.js:
--------------------------------------------------------------------------------
1 | const video = document.querySelector('.player');
2 | const canvas = document.querySelector('.photo');
3 | const ctx = canvas.getContext('2d');
4 | const strip = document.querySelector('.strip');
5 | const snap = document.querySelector('.snap');
6 |
--------------------------------------------------------------------------------
/19 Webcam Fun/original/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | }
4 |
5 | *, *:before, *:after {
6 | box-sizing: inherit;
7 | }
8 |
9 | html {
10 | font-size: 10px;
11 | background:#ffc600;
12 | }
13 |
14 | .photobooth {
15 | background:white;
16 | max-width:150rem;
17 | margin: 2rem auto;
18 | border-radius:2px;
19 | }
20 |
21 | /*clearfix*/
22 | .photobooth:after {
23 | content: '';
24 | display: block;
25 | clear: both;
26 | }
27 |
28 | .photo {
29 | width:100%;
30 | float:left;
31 | }
32 |
33 | .player {
34 | position: absolute;
35 | top:20px;
36 | right: 20px;
37 | width:200px;
38 | }
39 |
40 | /*
41 | Strip!
42 | */
43 |
44 | .strip {
45 | padding:2rem;
46 | }
47 | .strip img {
48 | width:100px;
49 | overflow-x: scroll;
50 | padding:0.8rem 0.8rem 2.5rem 0.8rem;
51 | box-shadow:0 0 3px rgba(0,0,0,0.2);
52 | background:white;
53 | }
54 |
55 | .strip a:nth-child(5n+1) img { transform: rotate(10deg); }
56 | .strip a:nth-child(5n+2) img { transform: rotate(-2deg); }
57 | .strip a:nth-child(5n+3) img { transform: rotate(8deg); }
58 | .strip a:nth-child(5n+4) img { transform: rotate(-11deg); }
59 | .strip a:nth-child(5n+5) img { transform: rotate(12deg); }
60 |
--------------------------------------------------------------------------------
/19 Webcam Fun/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gum",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "scripts.js",
6 | "scripts": {
7 | "start": "browser-sync start --server --files '*.css, *.html, *.js'"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "browser-sync": "^2.12.5"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/19 Webcam Fun/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | }
4 |
5 | *, *:before, *:after {
6 | box-sizing: inherit;
7 | }
8 |
9 | html {
10 | font-size: 10px;
11 | background:#ffc600;
12 | }
13 |
14 | .photobooth {
15 | background:white;
16 | max-width:150rem;
17 | margin: 2rem auto;
18 | border-radius:2px;
19 | }
20 |
21 | /*clearfix*/
22 | .photobooth:after {
23 | content: '';
24 | display: block;
25 | clear: both;
26 | }
27 |
28 | .photo {
29 | width:100%;
30 | float:left;
31 | }
32 |
33 | .player {
34 | position: absolute;
35 | top:20px;
36 | right: 20px;
37 | width:200px;
38 | }
39 |
40 | /*
41 | Strip!
42 | */
43 |
44 | .strip {
45 | padding:2rem;
46 | }
47 | .strip img {
48 | width:100px;
49 | overflow-x: scroll;
50 | padding:0.8rem 0.8rem 2.5rem 0.8rem;
51 | box-shadow:0 0 3px rgba(0,0,0,0.2);
52 | background:white;
53 | }
54 |
55 | .strip a:nth-child(5n+1) img { transform: rotate(10deg); }
56 | .strip a:nth-child(5n+2) img { transform: rotate(-2deg); }
57 | .strip a:nth-child(5n+3) img { transform: rotate(8deg); }
58 | .strip a:nth-child(5n+4) img { transform: rotate(-11deg); }
59 | .strip a:nth-child(5n+5) img { transform: rotate(12deg); }
60 |
--------------------------------------------------------------------------------
/20 Speech Detection/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 今天要做語音輸入的功能
4 |
5 | - 功能
6 | - 講話要變文字在畫面上
7 | - 停頓太久要分段
8 | - 畫面
9 | - 講話要變文字在畫面上
10 | - 停頓太久要分段
11 |
12 | # Javascript
13 |
14 | ## SpeechRecognition 是一個很新的東西
15 | [window.SpeechRecognition 教學](http://javascript.ruanyifeng.com/htmlapi/webspeech.html)
16 |
17 | - [`SpeechRecognition.end`](https://developer.mozilla.org/en-US/docs/Web/Events/end_%28SpeechRecognition%29), 此例子,講完麥克風就停了要再度打開。
18 | - [`SpeechRecognitionEvent.results`](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognitionEvent/results) 講的結果
19 | - [`SpeechRecognitionResult.isFinal`](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognitionResult/isFinal) 是否講完一段
20 | - [`SpeechRecognitionAlternative.transcript`](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognitionAlternative/transcript) 語音的辨識內容
21 |
22 | ## 更好的寫法
23 |
24 | 兩個寫法各有好壞。
25 |
26 | 原本這樣寫
27 | ```javascript=
28 | const transcript = [...e.results]
29 | .map(result => result[0])
30 | .map(result => result.transcript);
31 | console.log(...transcript);
32 | ```
33 | 也可以這樣寫
34 | ```javascript=
35 | const transcript = [...e.results]
36 | .map(result => result[0])
37 | .map(result => result.transcript).join('');
38 | console.log(transcript);
39 | ```
40 |
--------------------------------------------------------------------------------
/20 Speech Detection/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Speech Detection
6 |
7 |
8 |
9 |
10 |
11 |
12 |
47 |
48 |
49 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/20 Speech Detection/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Speech Detection
6 |
7 |
8 |
9 |
10 |
11 |
12 |
43 |
44 |
45 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/20 Speech Detection/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Speech Detection
6 |
7 |
8 |
9 |
10 |
11 |
12 |
17 |
18 |
19 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/20 Speech Detection/original/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gum",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "scripts.js",
6 | "scripts": {
7 | "start": "browser-sync start --directory --server --files \"*.css, *.html, *.js\""
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "browser-sync": "^2.12.5"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/20 Speech Detection/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gum",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "scripts.js",
6 | "scripts": {
7 | "start": "browser-sync start --server --files '*.css, *.html, *.js'"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "browser-sync": "^2.12.5"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/21 Geolocation/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 今天要做地理資訊的功能
4 |
5 | - 功能
6 | - 抓到指北角度要改畫面
7 | - 抓到移動速度要更新畫面
8 | - 畫面
9 | - 指北轉要旋轉
10 | - 顯示移動速度
11 |
12 | # Javascript
13 |
14 | - `window.navigator`
15 | - `navigator.geolocation`
16 | - `geolocation.watchPosition()` 監看目前位置
17 |
18 |
19 | - `watchPosition(okfun, errfun)`
20 | - okfun 執行正常有抓到GPS的資訊
21 | - errfun 執行錯誤情況、使用者不授權時的行為
22 |
23 | ```javascript=
24 | function okfun(data) {
25 | console.log(data)
26 | }
27 | ```
28 |
29 | data是Geoposition物件。
30 | - `timestamp` 當下時間
31 | - `coords` 目前地理資訊
32 | - `accuracy` 平面精度
33 | - `altitude` 高度
34 | - `altitudeAccuracy` 高空精度
35 | - `heading` 真北角度
36 | - `latitude` 緯度
37 | - `longitude` 經度
38 | - `speed` 速度
39 |
40 | ## 問題
41 |
42 | 電腦沒有模擬器
43 | 所以使用iPad來跑看看
44 |
45 | 不過,watchPosition執行幾次就跳err給我了
46 | 不知道為什麼
47 |
48 | 另外,也沒有速度的資訊也沒有指北的角度
49 |
--------------------------------------------------------------------------------
/21 Geolocation/original/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gum",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "scripts.js",
6 | "scripts": {
7 | "start": "browser-sync start --directory --server --files \"*.css, *.html, *.js\" --https"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "browser-sync": "^2.12.5"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/21 Geolocation/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gum",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "scripts.js",
6 | "scripts": {
7 | "start": "browser-sync start --server --files '*.css, *.html, *.js' --https"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "browser-sync": "^2.12.5"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/22 Follow Along Link Highlighter/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 這幾週[stripe.com](https://stripe.com/)更新了網站,而且它的導覽列的hover效果很厲害
4 |
5 | - 功能
6 | - 超連結Hover時,白色區塊要出現在該超連結區塊上
7 | - 畫面
8 | - 白色區塊的寬高要符合該超連結區塊
9 | - 滑鼠移開時,不變
10 | - 滑鼠進入另一個超連結區塊時,白色的區塊追上去(不會消失)
11 |
12 | # Javascript
13 |
14 | [`htmlElement.getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect)
15 | 取得在可視範圍的方塊(內含的離邊都是距離可視範圍)
16 |
17 | 要用`window.scroll`的座標來修正成`page`的座標
18 |
19 | ## 更好的寫法
20 |
21 | 寫這樣
22 | 用`position: absolution`做定位效果
23 | ```javascript=
24 | highlight.style.top = `${linkCoodrs.top}px`;
25 | highlight.style.left = `${linkCoodrs.left}px`;
26 | ```
27 | 可以寫成這樣
28 | 用`transform`的位移變化`translate()`
29 | ```javascript=
30 | hightlight.style.transform = `translate(${linkCoodrs.left}px), ${linkCoodrs.top}px)`;
31 | ```
32 |
--------------------------------------------------------------------------------
/22 Follow Along Link Highlighter/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 👀👀👀Follow Along Nav
6 |
7 |
8 |
9 |
10 |
11 |
18 |
19 |
20 |
21 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Est explicabo unde natus necessitatibus esse obcaecati distinctio, aut itaque, qui vitae!
22 |
Aspernatur sapiente quae sint soluta modi, atque praesentium laborum pariatur earum quaerat cupiditate consequuntur facilis ullam dignissimos, aperiam quam veniam.
23 |
Cum ipsam quod, incidunt sit ex tempore placeat maxime corrupti possimus veritatis ipsum fugit recusandae est doloremque? Hic, quibusdam , nulla.
24 |
Esse quibusdam, ad, ducimus cupiditate nulla , quae magni odit totam ut consequatur eveniet sunt quam provident sapiente dicta neque quod.
25 |
Aliquam dicta sequi culpa fugiat consequuntur pariatur optio ad minima, maxime odio , distinctio magni impedit tempore enim repellendus repudiandae quas!
26 |
27 |
28 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/22 Follow Along Link Highlighter/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 👀👀👀Follow Along Nav
6 |
7 |
8 |
9 |
10 |
11 |
18 |
19 |
20 |
21 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Est explicabo unde natus necessitatibus esse obcaecati distinctio, aut itaque, qui vitae!
22 |
Aspernatur sapiente quae sint soluta modi, atque praesentium laborum pariatur earum quaerat cupiditate consequuntur facilis ullam dignissimos, aperiam quam veniam.
23 |
Cum ipsam quod, incidunt sit ex tempore placeat maxime corrupti possimus veritatis ipsum fugit recusandae est doloremque? Hic, quibusdam , nulla.
24 |
Esse quibusdam, ad, ducimus cupiditate nulla , quae magni odit totam ut consequatur eveniet sunt quam provident sapiente dicta neque quod.
25 |
Aliquam dicta sequi culpa fugiat consequuntur pariatur optio ad minima, maxime odio , distinctio magni impedit tempore enim repellendus repudiandae quas!
26 |
27 |
28 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/22 Follow Along Link Highlighter/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 👀👀👀Follow Along Nav
6 |
7 |
8 |
9 |
10 |
11 |
18 |
19 |
20 |
21 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Est explicabo unde natus necessitatibus esse obcaecati distinctio, aut itaque, qui vitae!
22 |
Aspernatur sapiente quae sint soluta modi, atque praesentium laborum pariatur earum quaerat cupiditate consequuntur facilis ullam dignissimos, aperiam quam veniam.
23 |
Cum ipsam quod, incidunt sit ex tempore placeat maxime corrupti possimus veritatis ipsum fugit recusandae est doloremque? Hic, quibusdam , nulla.
24 |
Esse quibusdam, ad, ducimus cupiditate nulla , quae magni odit totam ut consequatur eveniet sunt quam provident sapiente dicta neque quod.
25 |
Aliquam dicta sequi culpa fugiat consequuntur pariatur optio ad minima, maxime odio , distinctio magni impedit tempore enim repellendus repudiandae quas!
26 |
27 |
28 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/22 Follow Along Link Highlighter/original/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | }
4 | *, *:before, *:after {
5 | box-sizing: inherit;
6 | }
7 | body {
8 | min-height: 100vh;
9 | margin: 0; /* Important! */
10 | font-family: sans-serif;
11 | background:
12 | linear-gradient(45deg, hsla(340, 100%, 55%, 1) 0%, hsla(340, 100%, 55%, 0) 70%),
13 | linear-gradient(135deg, hsla(225, 95%, 50%, 1) 10%, hsla(225, 95%, 50%, 0) 80%),
14 | linear-gradient(225deg, hsla(140, 90%, 50%, 1) 10%, hsla(140, 90%, 50%, 0) 80%),
15 | linear-gradient(315deg, hsla(35, 95%, 55%, 1) 100%, hsla(35, 95%, 55%, 0) 70%);
16 | }
17 |
18 | .wrapper {
19 | margin:0 auto;
20 | max-width:500px;
21 | font-size: 20px;
22 | line-height: 2;
23 | position: relative;
24 | }
25 |
26 | a {
27 | text-decoration: none;
28 | color:black;
29 | background:rgba(0,0,0,0.05);
30 | border-radius: 20px
31 | }
32 |
33 | .highlight {
34 | transition: all 0.2s;
35 | border-bottom:2px solid white;
36 | position: absolute;
37 | top:0;
38 | background:white;
39 | left:0;
40 | z-index: -1;
41 | border-radius:20px;
42 | display: block;
43 | box-shadow: 0 0 10px rgba(0,0,0,0.2)
44 | }
45 |
46 | .menu {
47 | padding: 0;
48 | display: flex;
49 | list-style: none;
50 | justify-content: center;
51 | margin:100px 0;
52 | }
53 |
54 | .menu a {
55 | display: inline-block;
56 | padding:5px;
57 | margin:0 20px;
58 | color:black;
59 | }
60 |
--------------------------------------------------------------------------------
/22 Follow Along Link Highlighter/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | }
4 | *, *:before, *:after {
5 | box-sizing: inherit;
6 | }
7 | body {
8 | min-height: 100vh;
9 | margin: 0; /* Important! */
10 | font-family: sans-serif;
11 | background:
12 | linear-gradient(45deg, hsla(340, 100%, 55%, 1) 0%, hsla(340, 100%, 55%, 0) 70%),
13 | linear-gradient(135deg, hsla(225, 95%, 50%, 1) 10%, hsla(225, 95%, 50%, 0) 80%),
14 | linear-gradient(225deg, hsla(140, 90%, 50%, 1) 10%, hsla(140, 90%, 50%, 0) 80%),
15 | linear-gradient(315deg, hsla(35, 95%, 55%, 1) 100%, hsla(35, 95%, 55%, 0) 70%);
16 | }
17 |
18 | .wrapper {
19 | margin:0 auto;
20 | max-width:500px;
21 | font-size: 20px;
22 | line-height: 2;
23 | position: relative;
24 | }
25 |
26 | a {
27 | text-decoration: none;
28 | color:black;
29 | background:rgba(0,0,0,0.05);
30 | border-radius: 20px
31 | }
32 |
33 | .highlight {
34 | display: block;
35 |
36 | position: absolute;
37 | top:0;
38 | left:0;
39 | z-index: -1;
40 |
41 | border-bottom:2px solid white;
42 | border-radius:20px;
43 |
44 | background:white;
45 |
46 | box-shadow: 0 0 10px rgba(0,0,0,0.2);
47 | transition: all 0.1s;
48 | }
49 |
50 | .menu {
51 | padding: 0;
52 | display: flex;
53 | list-style: none;
54 | justify-content: center;
55 | margin:100px 0;
56 | }
57 |
58 | .menu a {
59 | display: inline-block;
60 | padding:5px;
61 | margin:0 20px;
62 | color:black;
63 | }
64 |
--------------------------------------------------------------------------------
/23 Speech Synthesis/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 今天要做語音輸出(speech synthesis)
4 |
5 | - 功能
6 | - 切換不同的聲音(語系)
7 | - 調整說話速度(聲調改變)
8 | - 調整咬字速度
9 | - 要能輸入要唸的內容
10 | - 觸發開始講
11 | - 觸發閉嘴
12 | - 切換聲音時,會閉嘴再開始講
13 | - 畫面
14 | - 好像沒什麼特別的
15 |
16 | # Javascript
17 |
18 | ## 語音訊息
19 | ```javascript=
20 | const msg = new SpeechSynthesisUtterance();
21 | ```
22 | `msg.text` 要唸的文字
23 | `msg.voice` 要用什麼語音服務唸
24 |
25 | ## 語音服務物件
26 |
27 | 等一下他要唸語音訊息
28 |
29 | - `speechSynthesis.getVoices();` 目前可以用的語音服務
30 | - `speechSynthesis.speak(msg);`唸
31 | - `speechSynthesis.cancel();` 停!別唸了
32 |
33 | ## 較好的寫法
34 |
35 | 有一個function要丟參數,但是又在只能丟函數參考的地方
36 | 無法連同參數一起丟進去時
37 |
38 | 寫一個新的function
39 | ```javascript=
40 | function toggle_false() {
41 | toggle(false);
42 | }
43 | stopButton.addEventListener('click', toggle_false);
44 | ```
45 |
46 | 用bind()
47 | ```javascript=
48 | stopButton.addEventListener('click', toggle.bind(null, false));
49 | ```
50 |
51 | 用arrow function
52 | ```javascript=
53 | stopButton.addEventListener('click', () => toggle(false));
54 | ```
55 |
--------------------------------------------------------------------------------
/23 Speech Synthesis/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Speech Synthesis
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
The Voiceinator 5000
14 |
15 |
16 | Select A Voice
17 |
18 |
19 | Rate:
20 |
21 |
22 | Pitch:
23 |
24 |
25 |
26 | Stop!
27 | Speak
28 |
29 |
30 |
31 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/23 Speech Synthesis/npm-debug.log:
--------------------------------------------------------------------------------
1 | 0 info it worked if it ends with ok
2 | 1 verbose cli [ '/home/chris/.nvm/versions/node/v6.10.2/bin/node',
3 | 1 verbose cli '/home/chris/.nvm/versions/node/v6.10.2/bin/npm',
4 | 1 verbose cli 'start' ]
5 | 2 info using npm@3.10.10
6 | 3 info using node@v6.10.2
7 | 4 verbose stack Error: ENOENT: no such file or directory, open '/home/chris/code/JavaScript30/23 Speech Synthesis/package.json'
8 | 4 verbose stack at Error (native)
9 | 5 verbose cwd /home/chris/code/JavaScript30/23 Speech Synthesis
10 | 6 error Linux 4.4.0-72-generic
11 | 7 error argv "/home/chris/.nvm/versions/node/v6.10.2/bin/node" "/home/chris/.nvm/versions/node/v6.10.2/bin/npm" "start"
12 | 8 error node v6.10.2
13 | 9 error npm v3.10.10
14 | 10 error path /home/chris/code/JavaScript30/23 Speech Synthesis/package.json
15 | 11 error code ENOENT
16 | 12 error errno -2
17 | 13 error syscall open
18 | 14 error enoent ENOENT: no such file or directory, open '/home/chris/code/JavaScript30/23 Speech Synthesis/package.json'
19 | 15 error enoent ENOENT: no such file or directory, open '/home/chris/code/JavaScript30/23 Speech Synthesis/package.json'
20 | 15 error enoent This is most likely not a problem with npm itself
21 | 15 error enoent and is related to npm not being able to find a file.
22 | 16 verbose exit [ -2, true ]
23 |
--------------------------------------------------------------------------------
/23 Speech Synthesis/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Speech Synthesis
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
The Voiceinator 5000
14 |
15 |
16 | Select A Voice
17 |
18 |
19 | Rate:
20 |
21 |
22 | Pitch:
23 |
24 |
25 |
26 | Stop!
27 | Speak
28 |
29 |
30 |
31 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/23 Speech Synthesis/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Speech Synthesis
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
The Voiceinator 5000
14 |
15 |
16 | Select A Voice
17 |
18 |
19 | Rate:
20 |
21 |
22 | Pitch:
23 |
24 |
25 |
26 | Stop!
27 | Speak
28 |
29 |
30 |
31 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/23 Speech Synthesis/original/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-size: 10px;
3 | box-sizing: border-box;
4 | }
5 |
6 | *, *:before, *:after {
7 | box-sizing: inherit;
8 | }
9 |
10 | body {
11 | margin: 0;
12 | padding: 0;
13 | font-family: sans-serif;
14 | background-color:#3BC1AC;
15 | display:flex;
16 | min-height: 100vh;
17 | align-items: center;
18 |
19 | background-image:
20 | radial-gradient(circle at 100% 150%, #3BC1AC 24%, #42D2BB 25%, #42D2BB 28%, #3BC1AC 29%, #3BC1AC 36%, #42D2BB 36%, #42D2BB 40%, transparent 40%, transparent),
21 | radial-gradient(circle at 0 150%, #3BC1AC 24%, #42D2BB 25%, #42D2BB 28%, #3BC1AC 29%, #3BC1AC 36%, #42D2BB 36%, #42D2BB 40%, transparent 40%, transparent),
22 | radial-gradient(circle at 50% 100%, #42D2BB 10%, #3BC1AC 11%, #3BC1AC 23%, #42D2BB 24%, #42D2BB 30%, #3BC1AC 31%, #3BC1AC 43%, #42D2BB 44%, #42D2BB 50%, #3BC1AC 51%, #3BC1AC 63%, #42D2BB 64%, #42D2BB 71%, transparent 71%, transparent),
23 | radial-gradient(circle at 100% 50%, #42D2BB 5%, #3BC1AC 6%, #3BC1AC 15%, #42D2BB 16%, #42D2BB 20%, #3BC1AC 21%, #3BC1AC 30%, #42D2BB 31%, #42D2BB 35%, #3BC1AC 36%, #3BC1AC 45%, #42D2BB 46%, #42D2BB 49%, transparent 50%, transparent),
24 | radial-gradient(circle at 0 50%, #42D2BB 5%, #3BC1AC 6%, #3BC1AC 15%, #42D2BB 16%, #42D2BB 20%, #3BC1AC 21%, #3BC1AC 30%, #42D2BB 31%, #42D2BB 35%, #3BC1AC 36%, #3BC1AC 45%, #42D2BB 46%, #42D2BB 49%, transparent 50%, transparent);
25 | background-size:100px 50px;
26 | }
27 |
28 |
29 | .voiceinator {
30 | padding:2rem;
31 | width:50rem;
32 | margin:0 auto;
33 | border-radius:1rem;
34 | position: relative;
35 | background:white;
36 | overflow: hidden;
37 | z-index: 1;
38 | box-shadow:0 0 5px 5px rgba(0,0,0,0.1);
39 | }
40 |
41 | h1 {
42 | width:calc(100% + 4rem);
43 | margin: -2rem 0 2rem -2rem;
44 | padding:.5rem;
45 | background: #ffc600;
46 | border-bottom: 5px solid #F3C010;
47 | text-align: center;
48 | font-size: 5rem;
49 | font-weight: 100;
50 | font-family: 'Pacifico', cursive;
51 | text-shadow:3px 3px 0 #F3C010;
52 |
53 | }
54 |
55 | .voiceinator input,
56 | .voiceinator button,
57 | .voiceinator select,
58 | .voiceinator textarea {
59 | width: 100%;
60 | display: block;
61 | margin:10px 0;
62 | padding:10px;
63 | border:0;
64 | font-size: 2rem;
65 | background:#F7F7F7;
66 | outline:0;
67 | }
68 |
69 | textarea {
70 | height: 20rem;
71 | }
72 |
73 | input[type="select"] {
74 |
75 | }
76 |
77 | .voiceinator button {
78 | background:#ffc600;
79 | border:0;
80 | width: 49%;
81 | float:left;
82 | font-family: 'Pacifico', cursive;
83 | margin-bottom: 0;
84 | font-size: 2rem;
85 | border-bottom: 5px solid #F3C010;
86 | cursor:pointer;
87 | position: relative;
88 | }
89 |
90 | .voiceinator button:active {
91 | top:2px;
92 | }
93 |
94 | .voiceinator button:nth-of-type(1) {
95 | margin-right: 2%;
96 | }
97 |
--------------------------------------------------------------------------------
/23 Speech Synthesis/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-size: 10px;
3 | box-sizing: border-box;
4 | }
5 |
6 | *, *:before, *:after {
7 | box-sizing: inherit;
8 | }
9 |
10 | body {
11 | margin: 0;
12 | padding: 0;
13 | font-family: sans-serif;
14 | background-color:#3BC1AC;
15 | display:flex;
16 | min-height: 100vh;
17 | align-items: center;
18 |
19 | background-image:
20 | radial-gradient(circle at 100% 150%, #3BC1AC 24%, #42D2BB 25%, #42D2BB 28%, #3BC1AC 29%, #3BC1AC 36%, #42D2BB 36%, #42D2BB 40%, transparent 40%, transparent),
21 | radial-gradient(circle at 0 150%, #3BC1AC 24%, #42D2BB 25%, #42D2BB 28%, #3BC1AC 29%, #3BC1AC 36%, #42D2BB 36%, #42D2BB 40%, transparent 40%, transparent),
22 | radial-gradient(circle at 50% 100%, #42D2BB 10%, #3BC1AC 11%, #3BC1AC 23%, #42D2BB 24%, #42D2BB 30%, #3BC1AC 31%, #3BC1AC 43%, #42D2BB 44%, #42D2BB 50%, #3BC1AC 51%, #3BC1AC 63%, #42D2BB 64%, #42D2BB 71%, transparent 71%, transparent),
23 | radial-gradient(circle at 100% 50%, #42D2BB 5%, #3BC1AC 6%, #3BC1AC 15%, #42D2BB 16%, #42D2BB 20%, #3BC1AC 21%, #3BC1AC 30%, #42D2BB 31%, #42D2BB 35%, #3BC1AC 36%, #3BC1AC 45%, #42D2BB 46%, #42D2BB 49%, transparent 50%, transparent),
24 | radial-gradient(circle at 0 50%, #42D2BB 5%, #3BC1AC 6%, #3BC1AC 15%, #42D2BB 16%, #42D2BB 20%, #3BC1AC 21%, #3BC1AC 30%, #42D2BB 31%, #42D2BB 35%, #3BC1AC 36%, #3BC1AC 45%, #42D2BB 46%, #42D2BB 49%, transparent 50%, transparent);
25 | background-size:100px 50px;
26 | }
27 |
28 |
29 | .voiceinator {
30 | padding:2rem;
31 | width:50rem;
32 | margin:0 auto;
33 | border-radius:1rem;
34 | position: relative;
35 | background:white;
36 | overflow: hidden;
37 | z-index: 1;
38 | box-shadow:0 0 5px 5px rgba(0,0,0,0.1);
39 | }
40 |
41 | h1 {
42 | width:calc(100% + 4rem);
43 | margin: -2rem 0 2rem -2rem;
44 | padding:.5rem;
45 | background: #ffc600;
46 | border-bottom: 5px solid #F3C010;
47 | text-align: center;
48 | font-size: 5rem;
49 | font-weight: 100;
50 | font-family: 'Pacifico', cursive;
51 | text-shadow:3px 3px 0 #F3C010;
52 |
53 | }
54 |
55 | .voiceinator input,
56 | .voiceinator button,
57 | .voiceinator select,
58 | .voiceinator textarea {
59 | width: 100%;
60 | display: block;
61 | margin:10px 0;
62 | padding:10px;
63 | border:0;
64 | font-size: 2rem;
65 | background:#F7F7F7;
66 | outline:0;
67 | }
68 |
69 | textarea {
70 | height: 20rem;
71 | }
72 |
73 | input[type="select"] {
74 |
75 | }
76 |
77 | .voiceinator button {
78 | background:#ffc600;
79 | border:0;
80 | width: 49%;
81 | float:left;
82 | font-family: 'Pacifico', cursive;
83 | margin-bottom: 0;
84 | font-size: 2rem;
85 | border-bottom: 5px solid #F3C010;
86 | cursor:pointer;
87 | position: relative;
88 | }
89 |
90 | .voiceinator button:active {
91 | top:2px;
92 | }
93 |
94 | .voiceinator button:nth-of-type(1) {
95 | margin-right: 2%;
96 | }
97 |
--------------------------------------------------------------------------------
/24 Sticky Nav/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 |
4 | - 功能
5 | - menu到畫面最上面,要固定
6 | - 畫面
7 | - menu固定在畫面上面時,要露出Logo
8 | - menu不固定時,不露出logo
9 | - menu固定的那一瞬間,下面的文章不可以出現跳動
10 |
11 | # Javascript
12 |
13 | ## 另一種改法
14 |
15 | 得到一個想法
16 |
17 | 「新增`.style`若太麻煩」(太多code)
18 | 不如「新增`.classList`」
19 |
20 | 由於在此不只是要加上去,還要移除,所以,用`classList`的操作會比操作`style`來得容易又好懂
21 |
--------------------------------------------------------------------------------
/24 Sticky Nav/original/style-FINISHED.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | background:#eeeeee;
4 | font-family:'helvetica neue';
5 | font-size: 20px;
6 | font-weight: 200;
7 | }
8 | body {
9 | margin: 0;
10 | }
11 | *, *:before, *:after {
12 | box-sizing: inherit;
13 | }
14 |
15 | .site-wrap {
16 | max-width: 700px;
17 | margin: 70px auto;
18 | background:white;
19 | padding:40px;
20 | text-align: justify;
21 | box-shadow: 0 0 10px 5px rgba(0, 0, 0, 0.05);
22 | transform: scale(0.98);
23 | transition: transform 0.5s;
24 | }
25 |
26 | body.fixed-nav .site-wrap {
27 | transform: scale(1);
28 | }
29 |
30 |
31 | header {
32 | text-align: center;
33 | height:50vh;
34 | background:url(http://wes.io/iEgP/wow-so-deep.jpg) bottom center no-repeat;
35 | background-size:cover;
36 | display:flex;
37 | align-items:center;
38 | justify-content: center;
39 | }
40 |
41 | h1 {
42 | color:white;
43 | font-size: 7vw;
44 | text-shadow: 3px 4px 0 rgba(0,0,0,0.2)
45 | }
46 |
47 | nav {
48 | background:black;
49 | top:0;
50 | width: 100%;
51 | transition:all 0.5s;
52 | position: relative;
53 | z-index: 1;
54 | }
55 |
56 | body.fixed-nav nav {
57 | position: fixed;
58 | box-shadow:0 5px 0 rgba(0,0,0,0.1);
59 | }
60 |
61 | nav ul {
62 | margin: 0;
63 | padding:0;
64 | list-style: none;
65 | display:flex;
66 | }
67 |
68 | nav li {
69 | flex:1;
70 | text-align: center;
71 | display: flex;
72 | justify-content: center;
73 | align-items: center;
74 | }
75 |
76 | li.logo {
77 | max-width:0;
78 | overflow: hidden;
79 | background: white;
80 | transition: all 0.5s;
81 | font-weight: 600;
82 | font-size: 30px;
83 | }
84 |
85 | li.logo a {
86 | color:black;
87 | }
88 |
89 | .fixed-nav li.logo {
90 | max-width:500px;
91 | }
92 |
93 | nav a {
94 | text-decoration: none;
95 | padding:20px;
96 | display: inline-block;
97 | color:white;
98 | transition:all 0.2s;
99 | text-transform: uppercase;
100 | }
101 |
--------------------------------------------------------------------------------
/24 Sticky Nav/original/style-START.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | background:#eeeeee;
4 | font-family:'helvetica neue';
5 | font-size: 20px;
6 | font-weight: 200;
7 | }
8 | body {
9 | margin: 0;
10 | }
11 | *, *:before, *:after {
12 | box-sizing: inherit;
13 | }
14 |
15 | .site-wrap {
16 | max-width: 700px;
17 | margin: 70px auto;
18 | background:white;
19 | padding:40px;
20 | text-align: justify;
21 | box-shadow: 0 0 10px 5px rgba(0, 0, 0, 0.05);
22 | transform: scale(0.98);
23 | transition: transform 0.5s;
24 | }
25 |
26 | header {
27 | text-align: center;
28 | height:50vh;
29 | background:url(http://wes.io/iEgP/wow-so-deep.jpg) bottom center no-repeat;
30 | background-size:cover;
31 | display:flex;
32 | align-items:center;
33 | justify-content: center;
34 | }
35 |
36 | h1 {
37 | color:white;
38 | font-size: 7vw;
39 | text-shadow: 3px 4px 0 rgba(0,0,0,0.2)
40 | }
41 |
42 | nav {
43 | background:black;
44 | top:0;
45 | width: 100%;
46 | transition:all 0.5s;
47 | position: relative;
48 | z-index: 1;
49 | }
50 |
51 | nav ul {
52 | margin: 0;
53 | padding:0;
54 | list-style: none;
55 | display:flex;
56 | }
57 |
58 | nav li {
59 | flex:1;
60 | text-align: center;
61 | display: flex;
62 | justify-content: center;
63 | align-items: center;
64 | }
65 |
66 | li.logo {
67 | max-width:0;
68 | overflow: hidden;
69 | background: white;
70 | transition: all .5s;
71 | font-weight: 600;
72 | font-size: 30px;
73 | }
74 |
75 | li.logo a {
76 | color:black;
77 | }
78 |
79 | nav a {
80 | text-decoration: none;
81 | padding:20px;
82 | display: inline-block;
83 | color:white;
84 | transition:all 0.2s;
85 | text-transform: uppercase;
86 | }
87 |
--------------------------------------------------------------------------------
/24 Sticky Nav/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | background:#eeeeee;
4 | font-family:'helvetica neue';
5 | font-size: 20px;
6 | font-weight: 200;
7 | }
8 | body {
9 | margin: 0;
10 | }
11 | *, *:before, *:after {
12 | box-sizing: inherit;
13 | }
14 |
15 | .site-wrap {
16 | max-width: 700px;
17 | margin: 70px auto;
18 | background:white;
19 | padding:40px;
20 | text-align: justify;
21 | box-shadow: 0 0 10px 5px rgba(0, 0, 0, 0.05);
22 | transform: scale(0.98);
23 | transition: transform 0.5s;
24 | }
25 |
26 | .fixed-nav .site-wrap {
27 | /*transform: scale(1.02);*/
28 | }
29 |
30 | header {
31 | text-align: center;
32 | height:50vh;
33 | background:url(http://wes.io/iEgP/wow-so-deep.jpg) bottom center no-repeat;
34 | background-size:cover;
35 | display:flex;
36 | align-items:center;
37 | justify-content: center;
38 | }
39 |
40 | h1 {
41 | color:white;
42 | font-size: 7vw;
43 | text-shadow: 3px 4px 0 rgba(0,0,0,0.2)
44 | }
45 |
46 | nav {
47 | background:black;
48 | top:0;
49 | width: 100%;
50 | transition:all 0.5s;
51 | position: relative;
52 | z-index: 1;
53 | }
54 |
55 | .fixed-nav nav {
56 | position: fixed;
57 | box-shadow: 0 5px rgba(0, 0, 0, 0.1);
58 | }
59 |
60 | nav ul {
61 | margin: 0;
62 | padding:0;
63 | list-style: none;
64 | display:flex;
65 | }
66 |
67 | nav li {
68 | flex:1;
69 | text-align: center;
70 | display: flex;
71 | justify-content: center;
72 | align-items: center;
73 | }
74 |
75 | li.logo {
76 | max-width: 0;
77 | overflow: hidden;
78 | background: white;
79 | transition: all .5s;
80 | font-weight: 600;
81 | font-size: 30px;
82 | }
83 |
84 | .fixed-nav li.logo {
85 | max-width: 500px;
86 | }
87 |
88 | li.logo a {
89 | color:black;
90 | }
91 |
92 | nav a {
93 | text-decoration: none;
94 | padding:20px;
95 | display: inline-block;
96 | color:white;
97 | transition:all 0.2s;
98 | text-transform: uppercase;
99 | }
100 |
--------------------------------------------------------------------------------
/25 Event Capture, Propagation, Bubbling and Once/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 這一週,不做漂亮的東西。來細說`addEventListener`吧。
4 |
5 | 跟著做,就知道了。
6 |
7 | # Javascript
8 |
9 | 細說`addEventListener`
10 |
11 | ## [`addEventListener`](https://www.w3schools.com/jsref/met_element_addeventlistener.asp)
12 |
13 | 語法:
14 | ```javascript=
15 | element.addEventListener(event, function, useCapture)
16 | ```
17 | `useCapture`: `true` | `false`
18 |
19 | 四個重點
20 | 1. Event Capture
21 | 1. Propagation
22 | 1. Bubbling
23 | 1. Once(new property)
24 |
25 | ## 名詞定義
26 | - `bubbling` 冒泡(像泡泡在水裡浮上來的過程)
27 | - operator system -> browser application -> tab -> html -> body到你按的element,
28 | 都會被觸發事件。
29 | - 事件預設的執行順序是從element往上到body...
30 | - Capture 捕獲
31 | - browser做的事稱為Capture
32 | - 意思是你點擊element, 以top down的順序capture所有的event
33 | - `event.stopPropagation()`
34 | - 取消預設(bubbling)行為,變成「點到誰,就是誰」
35 |
36 | ## 舉例
37 |
38 | 以這個例子的HTML結構來說。
39 |
40 | ### 預設行為: `bobble up`
41 | 如果每一個都用這種方式加入event
42 | ```javascript
43 | addEventListener('click', fun);
44 | ```
45 | capture時,執行fun的順序將會是由上而下(DOM樹的樹梢到樹根)
46 | ```
47 | tree
48 | two
49 | one
50 | ```
51 | ### `capture:true`
52 | 在`addEventListener`的第三個參數加上這樣的物件
53 | `capture: false` 會將行預設行為
54 | ```javascript
55 | addEventListener('click', fun, {capture:true});
56 | ```
57 | capture時,執行fun的順序將會是由下而上(DOM樹的根到樹梢)
58 | ```
59 | one
60 | two
61 | tree
62 | ```
63 | ### `stop propagation`
64 |
65 | 在監聽觸發function裡面加上`e.stopPropagation()`
66 | 意思是"stop bubbling this event up. I clicked the one that I actually wanted."
67 |
68 | ```javascript
69 | function fun (e){
70 | e.stopPropagation(); //停止漣漪效應
71 | //other thing...
72 | }
73 | addEventListener('click', fun);
74 | ```
75 | 要設定`capture:false`才是點擊誰,觸發誰
76 | ```
77 | three
78 | ```
79 | 若設定`capture:true`是另一個順序
80 | ```
81 | one
82 | ```
83 | ### 只監聽一次
84 | 在`addEventListener`的第三個參數加上這樣的物件
85 | `once: false` 會將行預設行為
86 | ```javascript
87 | addEventListener('click', fun, {once:true});
88 | ```
89 | capture之後,就自動執行`removeEventListener()`
90 | ```
91 | one
92 | two
93 | tree
94 | ```
95 |
96 | ## 延伸閱讀
97 | - [Bubbling and capturing](https://javascript.info/bubbling-and-capturing)
98 |
--------------------------------------------------------------------------------
/25 Event Capture, Propagation, Bubbling and Once/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Understanding JavaScript's Capture
6 |
7 |
8 |
9 |
15 |
16 |
45 |
46 |
47 |
48 |
49 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/25 Event Capture, Propagation, Bubbling and Once/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Understanding JavaScript's Capture
6 |
7 |
8 |
9 |
15 |
16 |
39 |
40 |
41 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/25 Event Capture, Propagation, Bubbling and Once/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Understanding JavaScript's Capture
6 |
7 |
8 |
9 |
15 |
16 |
39 |
40 |
41 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/26 Stripe Follow Along Nav/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 再看一次[stripe.com](https://stripe.com/)網站,而且它的導覽列的hover效果很厲害
4 |
5 | 這次來做做它的效果吧!
6 |
7 | - 功能
8 | - 超連結Hover時,白色區塊要出現在該超連結的下拉區塊
9 | - 畫面
10 | - 白色區塊的寬高要符合該超連結的下拉區塊
11 | - 滑鼠移開時,白色區塊要消失
12 | - 白色區塊出現,內容才出現
13 | - 下拉區塊尚未完全出現,滑鼠移動時,要取消顯示內容
14 |
15 | # Javascript
16 |
17 | `element.nextElementSibling()`
18 | 下一個element(等同於css selector的`+`)
19 |
20 | 做hover的效果時
21 | 「消失/出現」和「漸漸消失/漸漸出現」的css要分開
22 | ```=
23 | .trigger-enter {
24 | display: block;
25 | }
26 |
27 | .trigger-enter-active {
28 | opacity: 1;
29 | }
30 | ```
31 |
32 | 這樣下面才會抓得到東西
33 | ```javascript=
34 | element.getBoundingClientRect();
35 |
--------------------------------------------------------------------------------
/27 Click and Drag/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 今天來實現拖拉互動的功能。
4 |
5 | - 功能
6 | - 拖拉互動的功能, 要有拖拉畫面的感覺
7 | - 畫面
8 | - 拖拉的程度,要和畫面移動程度,相同的pixel
9 |
10 |
11 | [`preventDefault()`](https://www.w3schools.com/jsref/event_preventdefault.asp), 取消預設行為
12 | 在此例,好像沒什麼太大明顯的效果
13 |
14 | ## 以View寬度為滑動寬度
15 | ```javascript=
16 | items.addEventListener('mousemove', scrolling);
17 | function scrolling (e) {
18 | if (mouseDown) {
19 | const itemsRect = this.getBoundingClientRect();
20 | this.scrollLeft -= (e.movementX/itemsRect.width)*this.scrollWidth;
21 | }
22 | }
23 | ```
24 | ## 以滑鼠移動寬度(pixel數)為滑動寬度
25 | - 滑鼠點下時
26 | - 記下目前scroll的絕對位置
27 | - 記下游標的位置
28 | - 移動滑鼠時
29 | - 計算滑鼠移動的相對位置
30 | - 將相對位置加到scroll的絕對位置
31 | - 不可以在此記scroll的絕對位置,會因為scroll移動而變化!!!
32 | ```javascript=
33 | items.addEventListener('mousemove', scrolling);
34 | function scrolling (e) {
35 | if (!mouseDown) return
36 | items.scrollLeft = (startX - e.pageX) + startScrollLeft;
37 | }
38 | items.addEventListener('mousedown', (e) => {
39 | mouseDown = true;
40 | items.classList.add('active');
41 | startX = e.pageX
42 | startScrollLeft = items.scrollLeft;
43 | });
44 | ```
45 |
--------------------------------------------------------------------------------
/27 Click and Drag/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Click and Drag
6 |
7 |
8 |
9 |
10 |
01
11 |
02
12 |
03
13 |
04
14 |
05
15 |
06
16 |
07
17 |
08
18 |
09
19 |
10
20 |
11
21 |
12
22 |
13
23 |
14
24 |
15
25 |
16
26 |
17
27 |
18
28 |
19
29 |
20
30 |
21
31 |
22
32 |
23
33 |
24
34 |
25
35 |
36 |
37 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/27 Click and Drag/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Click and Drag
6 |
7 |
8 |
9 |
10 |
01
11 |
02
12 |
03
13 |
04
14 |
05
15 |
06
16 |
07
17 |
08
18 |
09
19 |
10
20 |
11
21 |
12
22 |
13
23 |
14
24 |
15
25 |
16
26 |
17
27 |
18
28 |
19
29 |
20
30 |
21
31 |
22
32 |
23
33 |
24
34 |
25
35 |
36 |
37 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/27 Click and Drag/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Click and Drag
6 |
7 |
8 |
9 |
10 |
01
11 |
02
12 |
03
13 |
04
14 |
05
15 |
06
16 |
07
17 |
08
18 |
09
19 |
10
20 |
11
21 |
12
22 |
13
23 |
14
24 |
15
25 |
16
26 |
17
27 |
18
28 |
19
29 |
20
30 |
21
31 |
22
32 |
23
33 |
24
34 |
25
35 |
36 |
37 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/27 Click and Drag/original/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | background: url('https://source.unsplash.com/NFs6dRTBgaM/2000x2000') fixed;
4 | background-size: cover;
5 | }
6 |
7 | *, *:before, *:after {
8 | box-sizing: inherit;
9 | }
10 |
11 | body {
12 | min-height: 100vh;
13 | display: flex;
14 | justify-content: center;
15 | align-items: center;
16 | font-family: sans-serif;
17 | font-size: 20px;
18 | margin: 0;
19 | }
20 |
21 | .items {
22 | height:800px;
23 | padding: 100px;
24 | width:100%;
25 | border:1px solid white;
26 | overflow-x: scroll;
27 | overflow-y: hidden;
28 | white-space: nowrap;
29 | user-select: none;
30 | cursor: pointer;
31 | transition: all 0.2s;
32 | transform: scale(0.98);
33 | will-change: transform;
34 | position: relative;
35 | background: rgba(255,255,255,0.1);
36 | font-size: 0;
37 | perspective: 500px;
38 | }
39 |
40 | .items.active {
41 | background: rgba(255,255,255,0.3);
42 | cursor: grabbing;
43 | cursor: -webkit-grabbing;
44 | transform: scale(1);
45 | }
46 |
47 | .item {
48 | width:200px;
49 | height: calc(100% - 40px);
50 | display: inline-flex;
51 | align-items: center;
52 | justify-content: center;
53 | font-size: 80px;
54 | font-weight: 100;
55 | color:rgba(0,0,0,0.15);
56 | box-shadow: inset 0 0 0 10px rgba(0,0,0,0.15);
57 | }
58 |
59 | .item:nth-child(9n+1) { background: dodgerblue;}
60 | .item:nth-child(9n+2) { background: goldenrod;}
61 | .item:nth-child(9n+3) { background: paleturquoise;}
62 | .item:nth-child(9n+4) { background: gold;}
63 | .item:nth-child(9n+5) { background: cadetblue;}
64 | .item:nth-child(9n+6) { background: tomato;}
65 | .item:nth-child(9n+7) { background: lightcoral;}
66 | .item:nth-child(9n+8) { background: darkslateblue;}
67 | .item:nth-child(9n+9) { background: rebeccapurple;}
68 |
69 | .item:nth-child(even) { transform: scaleX(1.31) rotateY(40deg); }
70 | .item:nth-child(odd) { transform: scaleX(1.31) rotateY(-40deg); }
71 |
--------------------------------------------------------------------------------
/27 Click and Drag/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | background: url('https://source.unsplash.com/NFs6dRTBgaM/2000x2000') fixed;
4 | background-size: cover;
5 | }
6 |
7 | *, *:before, *:after {
8 | box-sizing: inherit;
9 | }
10 |
11 | body {
12 | min-height: 100vh;
13 | display: flex;
14 | justify-content: center;
15 | align-items: center;
16 | font-family: sans-serif;
17 | font-size: 20px;
18 | margin: 0;
19 | }
20 |
21 | .items {
22 | height:800px;
23 | padding: 100px;
24 | width:100%;
25 | border:1px solid white;
26 | overflow-x: scroll;
27 | overflow-y: hidden;
28 | white-space: nowrap;
29 | user-select: none;
30 | cursor: pointer;
31 | transition: all 0.2s;
32 | transform: scale(0.98);
33 | will-change: transform;
34 | position: relative;
35 | background: rgba(255,255,255,0.1);
36 | font-size: 0;
37 | perspective: 500px;
38 | }
39 |
40 | .items.active {
41 | background: rgba(255,255,255,0.3);
42 | cursor: grabbing;
43 | cursor: -webkit-grabbing;
44 | transform: scale(1);
45 | }
46 |
47 | .item {
48 | width:200px;
49 | height: calc(100% - 40px);
50 | display: inline-flex;
51 | align-items: center;
52 | justify-content: center;
53 | font-size: 80px;
54 | font-weight: 100;
55 | color:rgba(0,0,0,0.15);
56 | box-shadow: inset 0 0 0 10px rgba(0,0,0,0.15);
57 | }
58 |
59 | .item:nth-child(9n+1) { background: dodgerblue;}
60 | .item:nth-child(9n+2) { background: goldenrod;}
61 | .item:nth-child(9n+3) { background: paleturquoise;}
62 | .item:nth-child(9n+4) { background: gold;}
63 | .item:nth-child(9n+5) { background: cadetblue;}
64 | .item:nth-child(9n+6) { background: tomato;}
65 | .item:nth-child(9n+7) { background: lightcoral;}
66 | .item:nth-child(9n+8) { background: darkslateblue;}
67 | .item:nth-child(9n+9) { background: rebeccapurple;}
68 |
69 | .item:nth-child(even) { transform: scaleX(1.31) rotateY(40deg); }
70 | .item:nth-child(odd) { transform: scaleX(1.31) rotateY(-40deg); }
71 |
--------------------------------------------------------------------------------
/28 Video Speed Controller/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 做一個Range改變影片的播放速度
4 |
5 | - 功能
6 | - 拖拉Range, 最小值不可以設0.4
7 | - 拖拉Range, 最大值不可以設4
8 | - 畫面
9 | - 拖拉Range, 顯示目前的速度倍率,顯示格式: nx(n是整數)
10 |
11 | # Javascript
12 |
13 | 百分比的寫法
14 |
15 | ```javascript=
16 | height = Math.round(percent * 100) + '%'
17 | playbackRate = percent * (max - min) + min;
18 | ```
19 |
20 | 不過我覺得,不如寫一個if卡住調整的幅度
21 | ```javascript=
22 | const y = (e.offsetY / speedMaxY);
23 | if (y < 0.08) return;
24 | video.playbackRate = y * 4; //0.4~4
25 | ```
26 |
--------------------------------------------------------------------------------
/28 Video Speed Controller/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Video Speed Scrubber
6 |
7 |
8 |
9 |
10 |
16 |
17 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/28 Video Speed Controller/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Video Speed Scrubber
6 |
7 |
8 |
9 |
10 |
17 |
18 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/28 Video Speed Controller/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Video Speed Scrubber
6 |
7 |
8 |
9 |
10 |
16 |
17 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/28 Video Speed Controller/original/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | display:flex;
4 | justify-content: center;
5 | align-items: center;
6 | min-height: 100vh;
7 | background:#4C4C4C url('https://unsplash.it/1500/900?image=1021');
8 | background-size:cover;
9 | font-family: sans-serif;
10 | }
11 | .wrapper {
12 | width:850px;
13 | display:flex;
14 | }
15 | video {
16 | box-shadow:0 0 1px 3px rgba(0,0,0,0.1);
17 | }
18 |
19 | .speed {
20 | background:#efefef;
21 | flex:1;
22 | display:flex;
23 | align-items:flex-start;
24 | margin:10px;
25 | border-radius:50px;
26 | box-shadow:0 0 1px 3px rgba(0,0,0,0.1);
27 | overflow: hidden;
28 | }
29 | .speed-bar {
30 | width:100%;
31 | background:linear-gradient(-170deg, #2376ae 0%, #c16ecf 100%);
32 | text-shadow:1px 1px 0 rgba(0,0,0,0.2);
33 | display: flex;
34 | align-items: center;
35 | justify-content: center;
36 | padding:2px;
37 | color:white;
38 | height:16.3%;
39 | }
40 |
--------------------------------------------------------------------------------
/28 Video Speed Controller/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | display:flex;
4 | justify-content: center;
5 | align-items: center;
6 | min-height: 100vh;
7 | background:#4C4C4C url('https://unsplash.it/1500/900?image=1021');
8 | background-size:cover;
9 | font-family: sans-serif;
10 | }
11 | .wrapper {
12 | width:850px;
13 | display:flex;
14 | }
15 | video {
16 | box-shadow:0 0 1px 3px rgba(0,0,0,0.1);
17 | }
18 |
19 | .speed {
20 | background:#efefef;
21 | flex:1;
22 | display:flex;
23 | align-items:flex-start;
24 | margin:10px;
25 | border-radius:50px;
26 | box-shadow:0 0 1px 3px rgba(0,0,0,0.1);
27 | overflow: hidden;
28 | }
29 | .speed-bar {
30 | width:100%;
31 | background:linear-gradient(-170deg, #2376ae 0%, #c16ecf 100%);
32 | text-shadow:1px 1px 0 rgba(0,0,0,0.2);
33 | display: flex;
34 | align-items: center;
35 | justify-content: center;
36 | padding:2px;
37 | color:white;
38 | height:16.3%;
39 | }
40 |
--------------------------------------------------------------------------------
/29 Countdown Timer/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 做一個倒數計時器
4 |
5 | - 功能
6 | - 各種設定好的時間,進行倒數
7 | - 自訂義分鐘倒數
8 | - 開新的倒數,直接取消上一次
9 | - 畫面
10 | - 顯示目前倒數的數字
11 | - 顯示倒數歸零時,是幾點幾分
12 |
13 | # Javascript
14 |
15 | 倒數計時
16 | 5分鐘
17 | 15分鐘
18 |
19 |
20 | 時間 和數字運算,會當成ms進行運算
21 |
22 | 停止計時
23 | ```javascript=
24 | const pid = setInterval()
25 | clearInterval(pid);
26 | ```
27 |
28 | ~~範例程式碼的`timer`視為閉包~~
29 |
30 |
31 | 忽略預設行為
32 | ```javascript=
33 | e.preventDefault();
34 | ```
35 |
--------------------------------------------------------------------------------
/29 Countdown Timer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Countdown Timer
6 |
7 |
8 |
9 |
10 |
11 |
12 | 20 Secs
13 | Work 5
14 | Quick 15
15 | Snack 20
16 | Lunch Break
17 |
20 |
21 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/29 Countdown Timer/original/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Countdown Timer
6 |
7 |
8 |
9 |
10 |
11 |
12 | 20 Secs
13 | Work 5
14 | Quick 15
15 | Snack 20
16 | Lunch Break
17 |
20 |
21 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/29 Countdown Timer/original/scripts-FINISHED.js:
--------------------------------------------------------------------------------
1 | let countdown;
2 | const timerDisplay = document.querySelector('.display__time-left');
3 | const endTime = document.querySelector('.display__end-time');
4 | const buttons = document.querySelectorAll('[data-time]');
5 |
6 | function timer(seconds) {
7 | // clear any existing timers
8 | clearInterval(countdown);
9 |
10 | const now = Date.now();
11 | const then = now + seconds * 1000;
12 | displayTimeLeft(seconds);
13 | displayEndTime(then);
14 |
15 | countdown = setInterval(() => {
16 | const secondsLeft = Math.round((then - Date.now()) / 1000);
17 | // check if we should stop it!
18 | if(secondsLeft < 0) {
19 | clearInterval(countdown);
20 | return;
21 | }
22 | // display it
23 | displayTimeLeft(secondsLeft);
24 | }, 1000);
25 | }
26 |
27 | function displayTimeLeft(seconds) {
28 | const minutes = Math.floor(seconds / 60);
29 | const remainderSeconds = seconds % 60;
30 | const display = `${minutes}:${remainderSeconds < 10 ? '0' : '' }${remainderSeconds}`;
31 | document.title = display;
32 | timerDisplay.textContent = display;
33 | }
34 |
35 | function displayEndTime(timestamp) {
36 | const end = new Date(timestamp);
37 | const hour = end.getHours();
38 | const adjustedHour = hour > 12 ? hour - 12 : hour;
39 | const minutes = end.getMinutes();
40 | endTime.textContent = `Be Back At ${adjustedHour}:${minutes < 10 ? '0' : ''}${minutes}`;
41 | }
42 |
43 | function startTimer() {
44 | const seconds = parseInt(this.dataset.time);
45 | timer(seconds);
46 | }
47 |
48 | buttons.forEach(button => button.addEventListener('click', startTimer));
49 | document.customForm.addEventListener('submit', function(e) {
50 | e.preventDefault();
51 | const mins = this.minutes.value;
52 | console.log(mins);
53 | timer(mins * 60);
54 | this.reset();
55 | });
56 |
--------------------------------------------------------------------------------
/29 Countdown Timer/original/scripts-START.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/29 Countdown Timer/original/scripts-START.js
--------------------------------------------------------------------------------
/29 Countdown Timer/original/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | font-size: 10px;
4 | background: #8E24AA;
5 | background: linear-gradient(45deg, #42a5f5 0%,#478ed1 50%,#0d47a1 100%);
6 | }
7 |
8 | *, *:before, *:after {
9 | box-sizing: inherit;
10 | }
11 |
12 | body {
13 | margin:0;
14 | text-align: center;
15 | font-family: 'Inconsolata', monospace;
16 | }
17 |
18 | .display__time-left {
19 | font-weight: 100;
20 | font-size: 20rem;
21 | margin: 0;
22 | color:white;
23 | text-shadow:4px 4px 0 rgba(0,0,0,0.05);
24 | }
25 |
26 | .timer {
27 | display:flex;
28 | min-height: 100vh;
29 | flex-direction:column;
30 | }
31 |
32 | .timer__controls {
33 | display: flex;
34 | }
35 |
36 | .timer__controls > * {
37 | flex:1;
38 | }
39 |
40 | .timer__controls form {
41 | flex:1;
42 | display:flex;
43 | }
44 |
45 | .timer__controls input {
46 | flex:1;
47 | border:0;
48 | padding:2rem;
49 | }
50 |
51 | .timer__button {
52 | background:none;
53 | border:0;
54 | cursor: pointer;
55 | color:white;
56 | font-size: 2rem;
57 | text-transform: uppercase;
58 | background:rgba(0,0,0,0.1);
59 | border-bottom:3px solid rgba(0,0,0,0.2);
60 | border-right:1px solid rgba(0,0,0,0.2);
61 | padding:1rem;
62 | font-family: 'Inconsolata', monospace;
63 | }
64 |
65 | .timer__button:hover,
66 | .timer__button:focus {
67 | background:rgba(0,0,0,0.2);
68 | outline:0;
69 | }
70 |
71 | .display {
72 | flex:1;
73 | display:flex;
74 | flex-direction: column;
75 | align-items: center;
76 | justify-content: center;
77 | }
78 |
79 | .display__end-time {
80 | font-size: 4rem;
81 | color:white;
82 | }
83 |
--------------------------------------------------------------------------------
/29 Countdown Timer/scripts.js:
--------------------------------------------------------------------------------
1 | const timers = document.querySelectorAll('.timer__button');
2 | const show_end_time = document.querySelector('.display__end-time');
3 | const show_remaining_time = document.querySelector('.display__time-left');
4 |
5 | document.customForm.addEventListener('submit', function (e) {
6 | e.preventDefault();
7 | const sec_time = this.minutes.value * 60 * 1000;
8 | const now = new Date();
9 | end_time = new Date(now.getTime() + sec_time);
10 |
11 | const diff = new Date(sec_time);
12 | show_end_time.textContent = `倒數到: ${end_time.getHours()}:${end_time.getMinutes()}:${end_time.getSeconds()}`;
13 |
14 | clearInterval(timer_pid);
15 | timer_pid = setInterval(countDownTimer, 500);
16 |
17 |
18 | this.reset();
19 | });
20 |
21 |
22 |
23 | end_time = new Date();
24 |
25 | let timer_pid;
26 | // const custumer_timer = document.querySelector('.customForm');
27 |
28 | function countDownTimer() {
29 | const now = new Date();
30 | const remaining_time = new Date(end_time - now);
31 | show_remaining_time.textContent = `${remaining_time.getMinutes()}:${remaining_time.getSeconds()}`;
32 | if(remaining_time <= 1) clearInterval(timer_pid);
33 | }
34 |
35 | function setTimer() {
36 | const sec_time = this.dataset.time * 1000;
37 | const now = new Date();
38 | end_time = new Date(now.getTime() + sec_time);
39 |
40 | const diff = new Date(sec_time);
41 | show_end_time.textContent = `倒數到: ${end_time.getHours()}:${end_time.getMinutes()}:${end_time.getSeconds()}`;
42 |
43 | clearInterval(timer_pid);
44 | timer_pid = setInterval(countDownTimer, 500);
45 | }
46 |
47 | timers.forEach(timer => {
48 | timer.addEventListener('click', setTimer);
49 | });
50 |
--------------------------------------------------------------------------------
/29 Countdown Timer/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | font-size: 10px;
4 | background: #8E24AA;
5 | background: linear-gradient(45deg, #42a5f5 0%,#478ed1 50%,#0d47a1 100%);
6 | }
7 |
8 | *, *:before, *:after {
9 | box-sizing: inherit;
10 | }
11 |
12 | body {
13 | margin:0;
14 | text-align: center;
15 | font-family: 'Inconsolata', monospace;
16 | }
17 |
18 | .display__time-left {
19 | font-weight: 100;
20 | font-size: 20rem;
21 | margin: 0;
22 | color:white;
23 | text-shadow:4px 4px 0 rgba(0,0,0,0.05);
24 | }
25 |
26 | .timer {
27 | display:flex;
28 | min-height: 100vh;
29 | flex-direction:column;
30 | }
31 |
32 | .timer__controls {
33 | display: flex;
34 | }
35 |
36 | .timer__controls > * {
37 | flex:1;
38 | }
39 |
40 | .timer__controls form {
41 | flex:1;
42 | display:flex;
43 | }
44 |
45 | .timer__controls input {
46 | flex:1;
47 | border:0;
48 | padding:2rem;
49 | }
50 |
51 | .timer__button {
52 | background:none;
53 | border:0;
54 | cursor: pointer;
55 | color:white;
56 | font-size: 2rem;
57 | text-transform: uppercase;
58 | background:rgba(0,0,0,0.1);
59 | border-bottom:3px solid rgba(0,0,0,0.2);
60 | border-right:1px solid rgba(0,0,0,0.2);
61 | padding:1rem;
62 | font-family: 'Inconsolata', monospace;
63 | }
64 |
65 | .timer__button:hover,
66 | .timer__button:focus {
67 | background:rgba(0,0,0,0.2);
68 | outline:0;
69 | }
70 |
71 | .display {
72 | flex:1;
73 | display:flex;
74 | flex-direction: column;
75 | align-items: center;
76 | justify-content: center;
77 | }
78 |
79 | .display__end-time {
80 | font-size: 4rem;
81 | color:white;
82 | }
83 |
--------------------------------------------------------------------------------
/30 Whack A Mole/README.MD:
--------------------------------------------------------------------------------
1 | # 完成目標
2 |
3 | 做一個打地鼠的遊戲
4 |
5 | - 功能
6 | - 一個關卡要幾分鐘要固定
7 | - 地鼠冒出來、自動縮下去的時間間隔要不一樣
8 | - 畫面
9 | - 畫面要顯示目前的關卡
10 | - 地鼠要自動冒出來
11 | - 地鼠要自動縮下去
12 | - 地鼠被點到要縮下去
13 |
14 | # Javascript
15 |
16 | 指定取亂數的範圍
17 |
18 | ```javascript=
19 | function randTime(min, max) {
20 | return Math.round(Math.random() * (max - min) + min);
21 | }
22 | ```
23 |
24 | 避免重複取亂數
25 | ```javascript=
26 | let lastestId;
27 | function randomHole() {
28 | const id = Math...;
29 | if (id === lastestId) {
30 | return randomHole(holes);
31 | }
32 | eles {
33 | return id;
34 | }
35 | }
36 | ```
37 |
--------------------------------------------------------------------------------
/30 Whack A Mole/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Whack A Mole!
6 |
7 |
8 |
9 |
10 |
11 | Whack-a-mole! 0
12 | Start!
13 |
14 |
15 |
18 |
21 |
24 |
27 |
30 |
33 |
34 |
35 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/30 Whack A Mole/original/index-FINISHED.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Whack A Mole!
6 |
7 |
8 |
9 |
10 |
11 | Whack-a-mole! 0
12 | Start!
13 |
14 |
15 |
18 |
21 |
24 |
27 |
30 |
33 |
34 |
35 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/30 Whack A Mole/original/index-START.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Whack A Mole!
6 |
7 |
8 |
9 |
10 |
11 | Whack-a-mole! 0
12 | Start!
13 |
14 |
15 |
18 |
21 |
24 |
27 |
30 |
33 |
34 |
35 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/30 Whack A Mole/original/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | font-size: 10px;
4 | background: #ffc600;
5 | }
6 |
7 | *, *:before, *:after {
8 | box-sizing: inherit;
9 | }
10 |
11 | body {
12 | padding: 0;
13 | margin:0;
14 | font-family: 'Amatic SC', cursive;
15 | }
16 |
17 | h1 {
18 | text-align: center;
19 | font-size: 10rem;
20 | line-height:1;
21 | margin-bottom: 0;
22 | }
23 |
24 | .score {
25 | background:rgba(255,255,255,0.2);
26 | padding:0 3rem;
27 | line-height:1;
28 | border-radius:1rem;
29 | }
30 |
31 | .game {
32 | width:600px;
33 | height:400px;
34 | display:flex;
35 | flex-wrap:wrap;
36 | margin:0 auto;
37 | }
38 |
39 | .hole {
40 | flex: 1 0 33.33%;
41 | overflow: hidden;
42 | position: relative;
43 | }
44 |
45 | .hole:after {
46 | display: block;
47 | background: url(dirt.svg) bottom center no-repeat;
48 | background-size:contain;
49 | content:'';
50 | width: 100%;
51 | height:70px;
52 | position: absolute;
53 | z-index: 2;
54 | bottom:-30px;
55 | }
56 |
57 | .mole {
58 | background:url('mole.svg') bottom center no-repeat;
59 | background-size:60%;
60 | position: absolute;
61 | top: 100%;
62 | width: 100%;
63 | height: 100%;
64 | transition:all 0.4s;
65 | }
66 |
67 | .hole.up .mole {
68 | top:0;
69 | }
70 |
--------------------------------------------------------------------------------
/30 Whack A Mole/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | font-size: 10px;
4 | background: #ffc600;
5 | }
6 |
7 | *, *:before, *:after {
8 | box-sizing: inherit;
9 | }
10 |
11 | body {
12 | padding: 0;
13 | margin:0;
14 | font-family: 'Amatic SC', cursive;
15 | }
16 |
17 | h1 {
18 | text-align: center;
19 | font-size: 10rem;
20 | line-height:1;
21 | margin-bottom: 0;
22 | }
23 |
24 | .score {
25 | background:rgba(255,255,255,0.2);
26 | padding:0 3rem;
27 | line-height:1;
28 | border-radius:1rem;
29 | }
30 |
31 | .game {
32 | width:600px;
33 | height:400px;
34 | display:flex;
35 | flex-wrap:wrap;
36 | margin:0 auto;
37 | }
38 |
39 | .hole {
40 | flex: 1 0 33.33%;
41 | overflow: hidden;
42 | position: relative;
43 | }
44 |
45 | .hole:after {
46 | display: block;
47 | background: url(dirt.svg) bottom center no-repeat;
48 | background-size:contain;
49 | content:'';
50 | width: 100%;
51 | height:70px;
52 | position: absolute;
53 | z-index: 2;
54 | bottom:-30px;
55 | }
56 |
57 | .mole {
58 | background:url('mole.svg') bottom center no-repeat;
59 | background-size:60%;
60 | position: absolute;
61 | top: 100%;
62 | width: 100%;
63 | height: 100%;
64 | transition:all 0.4s;
65 | }
66 |
67 | .hole.up .mole {
68 | top:0;
69 | }
70 |
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | # JavaScript30練習筆記
2 |
3 | [Javascript30.com](https://javascript30.com/) [[github]](https://github.com/wesbos/JavaScript30)
4 |
5 | ## 原本的檔案
6 |
7 | 放置在所有課程裡面的`original`的資料夾裡。
8 |
9 | ## 練習內容
10 |
11 | [練習記錄@Github](https://github.com/dwatow/JavaScript30/)
12 |
13 | [練習成果](https://dwatow.github.io/JavaScript30/)
14 |
15 | ## 各課程學習到的新東西
16 |
17 | 將新學到的用法,找到w3school的參考頁面放到readme.md中。
18 |
19 | 並且練習放在index.html
20 |
21 | 1. [JavaScript Drum Kit](https://dwatow.github.io/JavaScript30/01%20JavaScript%20Drum%20Kit)
22 | 2. [JS and CSS Clock](https://dwatow.github.io/JavaScript30/02%20JS%20and%20CSS%20Clock)
23 | 3. [CSS Variables](https://dwatow.github.io/JavaScript30/03%20CSS%20Variables)
24 | 4. [Array Cardio Day 1](https://dwatow.github.io/JavaScript30/04%20Array%20Cardio%20Day%201)
25 | 5. [Flex Panel Gallery](https://dwatow.github.io/JavaScript30/05%20Flex%20Panel%20Gallery)
26 | 6. [Type Ahead](https://dwatow.github.io/JavaScript30/06%20Type%20Ahead)
27 | 7. [Array Cardio Day 2](https://dwatow.github.io/JavaScript30/07%20Array%20Cardio%20Day%202)
28 | 8. [Fun with HTML5 Canvas](https://dwatow.github.io/JavaScript30/08%20Fun%20with%20HTML5%20Canvas)
29 | 9. [Dev Tools Domination](https://dwatow.github.io/JavaScript30/09%20Dev%20Tools%20Domination)
30 | 10. [Hold Shift and Check Checkboxes](https://dwatow.github.io/JavaScript30/10%20Hold%20Shift%20and%20Check%20Checkboxes)
31 | 11. [Custom Video Player](https://dwatow.github.io/JavaScript30/11%20Custom%20Video%20Player)
32 | 12. [Key Sequence Detection](https://dwatow.github.io/JavaScript30/12%20Key%20Sequence%20Detection)
33 | 13. [Slide in on Scroll](https://dwatow.github.io/JavaScript30/13%20Slide%20in%20on%20Scroll)
34 | 14. [JavaScript References VS Copying](https://dwatow.github.io/JavaScript30/14%20JavaScript%20References%20VS%20Copying)
35 | 15. [LocalStorage](https://dwatow.github.io/JavaScript30/15%20LocalStorage)
36 | 16. [Mouse Move Shadow](https://dwatow.github.io/JavaScript30/16%20Mouse%20Move%20Shadow)
37 | 17. [Sort Without Articles](https://dwatow.github.io/JavaScript30/17%20Sort%20Without%20Articles)
38 | 18. [Adding Up Times with Reduce](https://dwatow.github.io/JavaScript30/18%20Adding%20Up%20Times%20with%20Reduce)
39 | 19. [Webcam Fun](https://dwatow.github.io/JavaScript30/19%20Webcam%20Fun)
40 | 20. [Speech Detection](https://dwatow.github.io/JavaScript30/20%20Speech%20Detection)
41 | 21. [Geolocation](https://dwatow.github.io/JavaScript30/21%20Geolocation)
42 | 22. [Follow Along Link Highlighter](https://dwatow.github.io/JavaScript30/22%20Follow%20Along%20Link%20Highlighter)
43 | 23. [Speech Synthesis](https://dwatow.github.io/JavaScript30/23%20Speech%20Synthesis)
44 | 24. [Sticky Nav](https://dwatow.github.io/JavaScript30/24%20Sticky%20Nav)
45 | 25. [Event Capture, Propagation, Bubbling and Once](https://dwatow.github.io/JavaScript30/25%20Event%20Capture%2C%20Propagation%2C%20Bubbling%20and%20Once)
46 | 26. [Stripe Follow Along Nav](https://dwatow.github.io/JavaScript30/26%20Stripe%20Follow%20Along%20Nav)
47 | 27. [Click and Drag](https://dwatow.github.io/JavaScript30/27%20Click%20and%20Drag)
48 | 28. [Video Speed Controller](https://dwatow.github.io/JavaScript30/28%20Video%20Speed%20Controller)
49 | 29. [Countdown Timer](https://dwatow.github.io/JavaScript30/29%20Countdown%20Timer)
50 | 30. [Whack A Mole](https://dwatow.github.io/JavaScript30/30%20Whack%20A%20Mole)
51 |
--------------------------------------------------------------------------------
/Readme.MD:
--------------------------------------------------------------------------------
1 | # JavaScript30練習筆記
2 |
3 | [Javascript30.com](https://javascript30.com/) [[github]](https://github.com/wesbos/JavaScript30)
4 |
5 | ## 原本的檔案
6 |
7 | 放置在所有課程裡面的`original`的資料夾裡。
8 |
9 | ## 練習內容
10 |
11 | [練習記錄@Github](https://github.com/dwatow/JavaScript30/)
12 |
13 | [練習成果](https://dwatow.github.io/JavaScript30/)
14 |
15 | ## 各課程學習到的新東西
16 |
17 | 將新學到的用法,找到w3school的參考頁面放到readme.md中。
18 |
19 | 並且練習放在index.html
20 |
21 | 1. [JavaScript Drum Kit](https://dwatow.github.io/JavaScript30/01%20JavaScript%20Drum%20Kit)
22 | 2. [JS and CSS Clock](https://dwatow.github.io/JavaScript30/02%20JS%20and%20CSS%20Clock)
23 | 3. [CSS Variables](https://dwatow.github.io/JavaScript30/03%20CSS%20Variables)
24 | 4. [Array Cardio Day 1](https://dwatow.github.io/JavaScript30/04%20Array%20Cardio%20Day%201)
25 | 5. [Flex Panel Gallery](https://dwatow.github.io/JavaScript30/05%20Flex%20Panel%20Gallery)
26 | 6. [Type Ahead](https://dwatow.github.io/JavaScript30/06%20Type%20Ahead)
27 | 7. [Array Cardio Day 2](https://dwatow.github.io/JavaScript30/07%20Array%20Cardio%20Day%202)
28 | 8. [Fun with HTML5 Canvas](https://dwatow.github.io/JavaScript30/08%20Fun%20with%20HTML5%20Canvas)
29 | 9. [Dev Tools Domination](https://dwatow.github.io/JavaScript30/09%20Dev%20Tools%20Domination)
30 | 10. [Hold Shift and Check Checkboxes](https://dwatow.github.io/JavaScript30/10%20Hold%20Shift%20and%20Check%20Checkboxes)
31 | 11. [Custom Video Player](https://dwatow.github.io/JavaScript30/11%20Custom%20Video%20Player)
32 | 12. [Key Sequence Detection](https://dwatow.github.io/JavaScript30/12%20Key%20Sequence%20Detection)
33 | 13. [Slide in on Scroll](https://dwatow.github.io/JavaScript30/13%20Slide%20in%20on%20Scroll)
34 | 14. [JavaScript References VS Copying](https://dwatow.github.io/JavaScript30/14%20JavaScript%20References%20VS%20Copying)
35 | 15. [LocalStorage](https://dwatow.github.io/JavaScript30/15%20LocalStorage)
36 | 16. [Mouse Move Shadow](https://dwatow.github.io/JavaScript30/16%20Mouse%20Move%20Shadow)
37 | 17. [Sort Without Articles](https://dwatow.github.io/JavaScript30/17%20Sort%20Without%20Articles)
38 | 18. [Adding Up Times with Reduce](https://dwatow.github.io/JavaScript30/18%20Adding%20Up%20Times%20with%20Reduce)
39 | 19. [Webcam Fun](https://dwatow.github.io/JavaScript30/19%20Webcam%20Fun)
40 | 20. [Speech Detection](https://dwatow.github.io/JavaScript30/20%20Speech%20Detection)
41 | 21. [Geolocation](https://dwatow.github.io/JavaScript30/21%20Geolocation)
42 | 22. [Follow Along Link Highlighter](https://dwatow.github.io/JavaScript30/22%20Follow%20Along%20Link%20Highlighter)
43 | 23. [Speech Synthesis](https://dwatow.github.io/JavaScript30/23%20Speech%20Synthesis)
44 | 24. [Sticky Nav](https://dwatow.github.io/JavaScript30/24%20Sticky%20Nav)
45 | 25. [Event Capture, Propagation, Bubbling and Once](https://dwatow.github.io/JavaScript30/25%20Event%20Capture%2C%20Propagation%2C%20Bubbling%20and%20Once)
46 | 26. [Stripe Follow Along Nav](https://dwatow.github.io/JavaScript30/26%20Stripe%20Follow%20Along%20Nav)
47 | 27. [Click and Drag](https://dwatow.github.io/JavaScript30/27%20Click%20and%20Drag)
48 | 28. [Video Speed Controller](https://dwatow.github.io/JavaScript30/28%20Video%20Speed%20Controller)
49 | 29. [Countdown Timer](https://dwatow.github.io/JavaScript30/29%20Countdown%20Timer)
50 | 30. [Whack A Mole](https://dwatow.github.io/JavaScript30/30%20Whack%20A%20Mole)
51 |
--------------------------------------------------------------------------------