# JS Practice --- tags: Javascript relate --- ###### tags: `Javascript` # 製作一個紅綠燈 ## 成品: ![](https://i.imgur.com/OlFszR9.gif) [成品網址](https://chiehliu.github.io/git-projects/JSpractice/traffic-light-simulator/) ## 成品功能: 1. 預設起始點是亮紅燈 2. 每秒換一個燈色 3. 循環順序是紅 => 黃 => 綠 # HTML HTML很單純的顯示三個圓圈跟紅綠燈的身體 比較特殊的是color屬性 ## html程式碼: ```htmlembedded= <body> <div class="container"> <div class="circle red" color="red"></div> <div class="circle" color="yellow"></div> <div class="circle" color="green"></div> </div> </body> ``` # CSS: .circle:after 處理立體感燈泡 * 使用after創造出來的block * 把after的background-color:red 先設定出來比較好處理 * 右側用border-right做出粉紅色的邊邊來當作陰影(紅色箭頭處) * 再使用borderj-radius後就會呈現半月形狀 * 最後去除掉background-color:red就可以得到立體感的燈泡! ![](https://i.imgur.com/qmNsp0u.png) => ![](https://i.imgur.com/qulDZo1.png) => ![](https://i.imgur.com/eECGhCM.png) ## CSS完整程式碼 ```css= * { box-sizing: border-box; } body { background-color: #1abc9c; display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; } .container { height: 200px; width: 70px; background-color: #2c3e50; border-radius: 50px; display: flex; justify-content: space-between; flex-direction: column; align-items: center; padding: 15px; } .circle { background-color: rgba(0, 0, 0, 0.3); border-radius: 50%; position: relative; height: 40px; width: 40px; } .circle:after { content: ""; width: 30px; height: 30px; position: absolute; border-right: 4px solid rgba(255, 255, 255, 0.6); border-radius: 50%; top: 5px; } .circle.red { background-color: #c0392b; box-shadow: 0 0 20px 5px #c0392b; } .circle.yellow { background-color: #f1c40f; box-shadow: 0 0 20px 5px #f1c40f; } .circle.green { background-color: #2ecc71; box-shadow: 0 0 20px 5px #2ecc71; } ``` # JS: ## 變數設置 ```javascript= const circles = document.querySelectorAll('.circle'); ``` ![](https://i.imgur.com/r9DR7KE.png) ```javascript= let activeLight = 0; ``` ```javascript= const currenLight = circles[activeLight]; ``` * activeLight代表circle的index * circles[activeLight]就是顯示當前亮甚麼燈號 ![](https://i.imgur.com/SgbZy1k.png) ## functions: * `setInterval()` - 每秒執行一次更換燈號 * `changeLight()` - 因為基礎燈號一開設置在red燈,所以程式開頭先把當前的class轉回circle也就是代表熄燈,隨後增加circle的index號碼因為要讓燈號跑到下一個,接下來做if判斷式讓index跑到底時,可以繞回 0也就是回到紅燈,最後把當前的燈號currenLight的class加上 對應的div 屬性 也就是加上color內的內容,就可以切換到下一個燈號了 步驟: 1. 把當前燈號關閉(轉換class回到circle) 2. 把index號碼往前進`activeLight++;` 3. if判斷式處理當index號碼超過2時要回到0 4. 把上面index已經往前的號碼填入circle[]抓取currentLight當前亮的燈號 5. 抓好後處理他的class抓取當前燈號div的 color屬性也就是對應的顏色(red, yellow, green) ## JS完整程式碼: ```javascript= const circles = document.querySelectorAll('.circle'); let activeLight = 0; setInterval(changeLight, 1000); function changeLight() { // 預設值是亮紅燈這行的用處在於取消red的class變回circle // circles[0] = red circles[activeLight].className = 'circle'; activeLight++; if (activeLight > 2) { activeLight = 0; } const currenLight = circles[activeLight]; currenLight.classList.add(currenLight.getAttribute('color')); } ``` # 製作一個老爸笑話產生器 ## 成品: ![](https://i.imgur.com/3L2rtUc.png) [成品網址](https://chiehliu.github.io/git-projects/JSpractice/Dad-joke-generator/) ## 成品功能: 1. 點擊Get Another joke就會取得一則新的笑話 # HTML * 容器包住全部 * h3 * div 裡面處理js產生笑話 * 按鈕 ## html程式碼: ```htmlembedded= <div class="container"> <h3>Don't laugh challenge</h3> <div id="joke" class="joke"> //here goes the joke </div> <button id="get_joke" class="btn"> Get Another Joke </button> </div> ``` # CSS: * 簡單修飾容器不多作介紹 ## CSS完整程式碼 ```css= @import url('https://fonts.googleapis.com/css?family=Muli&display=swap'); * { box-sizing: border-box; } body { background-color: #686de0; font-family: 'Muli', sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; } .container { background-color: #fff; border-radius: 10px; padding: 50px 20px; text-align: center; max-width: 100%; width: 800px; box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1), 0 6px 6px rgba(0, 0, 0, 0.1); } h3 { margin: 0; letter-spacing: 2px; opacity: 0.5; } .joke { font-size: 30px; line-height: 40px; margin: 50px auto; max-width: 600px; } .btn { background-color: #9f68e0; border: 0; border-radius: 10px; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1), 0 6px 6px rgba(0, 0, 0, 0.1); color: #fff; padding: 14px 40px; font-size: 16px; cursor: pointer; } .btn:active { transform: scale(0.98); } .btn:focus { outline: 0; } ``` # JS: ## API [icanhazdadjoke API位置以及使用說明](https://icanhazdadjoke.com/api) 因為我們要使用JSON格式 ![](https://i.imgur.com/HdHkok2.png) ## 變數設置 ```javascript= const jokeEl = document.getElementById('joke'); ``` ![](https://i.imgur.com/OIMbsYD.png) ```javascript= const get_joke = document.getElementById('get_joke'); ``` ![](https://i.imgur.com/oEboUWY.png) ```javascript= const joke = await jokeRes.json(); ``` fetch回來的資料裡面的joke就是要取用的部分 ![](https://i.imgur.com/k2II9Tb.png) ## functions: * `generateJoke()` 1. jokeRes 變數等待fetch回傳的資料 2. joke 變數等待jokeRes從json格式轉回來得到下圖內容並且使用joke.joke取用後 ![](https://i.imgur.com/e0Crd1A.png) 3. 最後貼上jokeEl的HTML上面 ## 事件監聽 ```javascript= get_joke.addEventListener('click', generateJoke); ``` 點擊觸發`generateJoke()`產生笑話 ## JS完整程式碼: ```javascript= const jokeEl = document.getElementById('joke'); const get_joke = document.getElementById('get_joke'); get_joke.addEventListener('click', generateJoke); async function generateJoke() { // call the icanhaz API const jokeRes = await fetch('https://icanhazdadjoke.com/', { headers: { 'Accept': 'application/json' } }); const joke = await jokeRes.json(); console.log(joke); //set the new joke to jokeEl.innerHTML = joke.joke; } ``` # 製作一個動畫旋轉圈圈 ## 成品: ![](https://i.imgur.com/zpADG4D.gif) [成品網址](https://chiehliu.github.io/git-projects/JSpractice/Rotating%20Circles%20Animation/) ## 成品功能: # HTML 要幾個圈圈可以自訂 ## html程式碼: ```htmlembedded= <body> <div class="container"> <div class="circle"></div> <div class="circle"></div> <div class="circle"></div> </div> </body> ``` # CSS: * body部分做全置中 * container做一開始的定位(把半圓轉到上面) * 使用border做出直線(dashed),之後使用`border-radius:50%`成圓形 * 這兩個屬性作出半圓來 (border-top-color: #fff; border-right-color: #fff;) * box-shadow讓每個圓有邊框 (沒有做box-shadow的話會長這樣全部黏在一起) ![](https://i.imgur.com/yzS5ec7.png) (做出box-shado之後的效果) ![](https://i.imgur.com/d9zoVO0.png) * `transform: translate(-50%, -50%)`為了讓圓形置中到圓心 ## 讓半圓轉起來 下方是示範使用CSS來寫如何讓讓半圓轉起來 針對每一圈的border做處理 * 修改大小 * 修改旋轉角度,這邊很有趣因為每一圈都是以360deg的倍數所以一定會一瞬間是所有半圓都會聚集變回半圓,也算是這個特效的看點 * 很重要的部分`transform: translate(-50%, -50%)`的全置中效果一樣必須寫進來不能只寫rotate進去@keyframes裡面不然特效會跑掉 ```css= .circle:nth-of-type(1) { border: 50px dashed #111; border-top-color: #fff; border-right-color: #fff; animation: rotateA 3s ease-in-out infinite; } @keyframes rotateA { to { transform: translate(-50%, -50%) rotate(360deg); } } .circle:nth-of-type(2) { border: 40px dashed #111; border-top-color: #fff; border-right-color: #fff; animation: rotateB 3s ease-in-out infinite; } @keyframes rotateB { to { transform: translate(-50%, -50%) rotate(720deg); } } .circle:nth-of-type(3) { border: 30px dashed #111; border-top-color: #fff; border-right-color: #fff; animation: rotateC 3s ease-in-out infinite; } @keyframes rotateC { to { transform: translate(-50%, -50%) rotate(1080deg); } } ``` ## CSS完整程式碼 ```css= body { display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; } .container { position: relative; transform: rotate(135deg) } .circle { border-radius: 50%; border: 60px dashed #111; border-top-color: #fff; border-right-color: #fff; box-shadow: 0 0 0 2px #fff; position: absolute; top: 50%; left: 50%; width: 0; height: 0; transform: translate(-50%, -50%) rotate(0deg); animation: rotate 7s ease-in-out infinite; } ``` # JS: ## 變數設置 ```javascript= const circles = document.querySelectorAll('.circle'); ``` ![](https://i.imgur.com/wqDg92e.png) ```javascript= const style = document.createElement('style'); ``` 輸出在body內修飾半圓內部填入`@keyframes` ```javascript= const deg = (idx + 1) * 360; ``` deg代表每圈的旋轉角度 ## forEach: ### forEach的輸出結果: 利用forEach輸出circle的index的方式處理下面三個屬性: * border-widht * z-index * animation-name ![](https://i.imgur.com/7DJvsSj.png) ### keyframes的輸出結果: * translate的部分就固定 * 主要使用idx更改旋轉角度`const deg = (idx + 1) * 360;` ![](https://i.imgur.com/4rq2d5Q.png) ## JS完整程式碼: ```javascript= const circles = document.querySelectorAll('.circle'); const style = document.createElement('style'); let styleInner = ''; circles.forEach((circle, idx) => { circle.style.borderWidth = (idx + 1) * 10 + 'px'; circle.style.zIndex = -idx; circle.style.animationName = `rotate-${idx}`; const deg = (idx + 1) * 360; const style = document.createElement('style'); styleInner += ` @keyframes rotate-${idx} { to { transform: translate(-50%, -50%) rotate(${deg}deg); } } `; }); style.innerHTML = styleInner; document.body.appendChild(style); ``` # 製作一個進度條 ## 成品: ![](https://i.imgur.com/bBORPz2.gif) [成品網址](https://chiehliu.github.io/git-projects/JSpractice/Custom-Progress-Bar/) ## 成品功能: * 當頁面刷新時會更新進度條 # HTML * 這邊的data-done是給JS做選取使用 ## html程式碼: ```htmlembedded= <div class="progress"> <div class="progress-done" data-done="70"> 70% </div> </div> ``` # CSS: * 漸層色的處 左邊的顏色會顯示在右邊,反之亦然 * 在JS處理 opacity顯示0 width顯示0 ```css= background: linear-gradient(to left, #f2709c(顯示在右側), #ff9472); ``` ## CSS完整程式碼 ```css= @import url('https://fonts.googleapis.com/css?family=Montserrat&display=swap'); * { box-sizing: border-box; } body { font-family: 'Montserrat', sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; } .progress { height: 30px; width: 300px; background-color: #d8d8d8; border-radius: 20px; } .progress-done { height: 100%; width: 0; color: #fff; box-shadow: 0 3px 3px -5px #f2709c, 0 2px 5px #f2709c; background: linear-gradient(to left, #f2709c, #ff9472); border-radius: 20px; display: flex; align-items: center; justify-content: center; opacity: 0; transition: 1s ease; } ``` # JS: * 藉由html設定好的data-done由JS讀其值後動態呈現width * 並且更改opacity為1 ![](https://i.imgur.com/XNCVd6X.png) ## 變數設置 內部進度條的部分 ![](https://i.imgur.com/DP46VDz.png) ## JS完整程式碼: ```javascript= const progress = document.querySelector('.progress-done'); setTimeout(() => { progress.style.width = progress.getAttribute('data-done') + '%'; progress.style.opacity = 1; }, 500) ``` # 製作一個下雪背景 ## 成品: ![](https://i.imgur.com/srk8m46.gif) [成品網址](https://chiehliu.github.io/git-projects/JSpractice/MakeitSnow/) ## 成品功能: * 背景會下雪 * 雪花位置、大小、景深都隨機 # HTML * 需要引入fontawesome * 雪花會從JS製造 ## html程式碼: ```htmlembedded= <script src="https://kit.fontawesome.com/2ae9e2b502.js" crossorigin="anonymous"></script> <body> </body> ``` # CSS: * 使用絕對定位因為會在JS設定位置 * animation使用forwards表示動畫結會停在結束的狀態 * translateY設定105vh要展示出從螢幕上方掉下來所以設定超出視窗 ## CSS完整程式碼 ```css= body { background: #323975; } .fa-snowflake { color: #fff; position: absolute; animation: fall linear forwards; } @keyframes fall { to { transform: translateY(105vh); } } ``` # JS: ## 變數設置 抓取創造的雪花 ```javascript= const snow_flake = document.createElement('i'); ``` ## functions: `createSnowFlake()` * 創造出i tag後加上class讓它可以吃到css的修飾 * Math.random()會產出 0~1之間的小數 * 讓雪花的位置隨機 innerwidth乘上Math.random()代表雪花出現的寬度介在0~整個瀏覽器的寬度 * 讓雪花持續時間隨機 因為我們設定setTimeout為五秒所以最少持續兩秒最多五秒 * 透明度以及雪花大小 透明度就直接random很方便; 大小的部份讓他們保持在10~20之間不要大的太誇張 ## JS完整程式碼: ```javascript= setInterval(createSnowFlake, 50); function createSnowFlake() { const snow_flake = document.createElement('i'); snow_flake.classList.add('fas'); snow_flake.classList.add('fa-snowflake'); snow_flake.style.left = Math.random() * window.innerWidth + `px`; snow_flake.style.animationDuration = Math.random() * 3 + 2 + `s`; //數字會介於2~5之間 snow_flake.style.opacity = Math.random(); //給的數字會介於1~0之間 做景深相關的特效 snow_flake.style.fontSize = Math.random() * 10 + 10 + `px`; //做出不同大小 document.body.appendChild(snow_flake); setTimeout(() => { snow_flake.remove(); }, 5000) } ``` # 製作一個神奇寶貝圖鑑 ## 成品: ![](https://i.imgur.com/zJTLZQn.png) [成品網址](https://chiehliu.github.io/git-projects/JSpractice/pokeDesk/) ## 成品功能: 1. 依照圖鑑順序顯示神奇寶貝 2. 針對不同屬性呈現不同顏色 3. 有RWD效果 # HTML 基本上都是藉由JS產生所以的div ## html程式碼: ```htmlembedded= <body> <h1>PokeDex</h1> <div id="poke_container" class="poke-container"></div> </body> ``` # CSS: * pokemon-contaienr 使用flex-wrap:wrap是整個排版的重點讓圖鑑可以分行排版 並且全置中 * .pokemon .img-container img 為了使用RWD使用`max-width: 90%;`也代表父母層的90%也就是120px*0.9=108 ## CSS完整程式碼 ```css= @import url('https://fonts.googleapis.com/css?family=Muli&display=swap'); @import url('https://fonts.googleapis.com/css?family=Lato:300,400&display=swap'); * { box-sizing: border-box; } body { background: #EFEFBB; background: linear-gradient(to right, #D4D3DD, #EFEFBB); display: flex; flex-direction: column; align-items: center; justify-content: center; font-family: 'Lato'; margin: 0; } h1 { letter-spacing: 3px; } .poke-container { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; max-width: 1200px; } .pokemon { /* 這邊的背景色是因為修飾使用做好JS後可以刪除 */ background-color: #eee; border-radius: 20px; box-shadow: 0 3px 15px rgba(100, 100, 100, 0.5); margin: 10px; padding: 20px; text-align: center; } .pokemon .img-container { background-color: rgba(255, 255, 255, 0.6); border-radius: 50%; width: 120px; height: 120px; text-align: center; } .pokemon .img-container img { margin-top: 20px; max-width: 90%; } .pokemon .info { margin-top: 20px; } .pokemon .number { background-color: rgba(0, 0, 0, 0.1); border-radius: 10px; font-size: 0.8em; padding: 5px 10px; } .pokemon .name { margin: 15px 0 7px; letter-spacing: 1px; } ``` # JS: ## 變數設置 ### 全域變數 * poke_container ![](https://i.imgur.com/0EZjDSP.png) * pokemons_number = 150 主要使用在要逐個印出神奇寶貝的`fetchPokemons()`函式裡 * main_types 取出物件colors裡面的key值 * colors 設置不同屬性的神奇寶貝不同的顏色 ### getPokemon()內部變數 * url 名字跟屬性的來源 * res 回傳回來的url 資料 * pokemon 轉檔成json使用並且傳入`createPokemonCard(pokemon);` ### createPokemonCard()內部變數 * pokemonEl ![](https://i.imgur.com/tLgjn6F.png) * poke_types 使map逐個印出每個神奇寶貝的types名稱 ![](https://i.imgur.com/xusdD57.png) * name 為了讓名字第一個字可以大寫所以使用name[0]抓取第一個字並且使用toUppercase() 並且串接上name.slice(1)也就是切掉第一個字母的單字就完成瞜! * type 使用find()來回傳第一個找到的值並填入color給不同總類的神奇寶貝上不同背景色 * color 使用find()來回傳第一個找到的值並填入color給不同總類的神奇寶貝上不同背景色 * pokeInnerHTML 整個要印出在DOM上面的html架構 ## functions: * `getPokemon()` 從API擷取資下來並且傳入createPokemonCard中 * `fetchPokemons()` 逐個擷取並且印出getPokemon()得到的資料 * `createPokemonCard()` 1. 創造div並加上class 1. 取得神奇寶貝的type並且嵌入color使顏色隨屬性呈現 1. 實際HTML內的撰寫並填入相應的API內容 1. 把HTML填入pokemonEl 1. 把poke_contaienr填入最後完成pokemonEl 使用`pokemon.id.toString().padStart(3,"0")` 讓神奇寶貝編碼變成三位數 `padStart(3,"0")` 代表把內容物填充到三位數並且從0開頭(ex. 001,002,003) ## JS完整程式碼: ```javascript= const poke_container = document.getElementById('poke_container'); const pokemons_number = 150; const colors = { fire: '#FDDFDF', grass: '#DEFDE0', electric: '#FCF7DE', water: '#DEF3FD', ground: '#f4e7da', rock: '#d5d5d4', fairy: '#fceaff', poison: '#98d7a5', bug: '#f8d5a3', dragon: '#97b3e6', psychic: '#eaeda1', flying: '#F5F5F5', fighting: '#E6E0D4', normal: '#F5F5F5' }; const main_types = Object.keys(colors); // console.log(main_types); const fetchPokemons = async () => { for (let i = 1; i < pokemons_number; i++) { await getPokemon(i); } } const getPokemon = async id => { const url = `https://pokeapi.co/api/v2/pokemon/${id}`; const res = await fetch(url); const pokemon = await res.json(); createPokemonCard(pokemon); } fetchPokemons(); function createPokemonCard(pokemon) { const pokemonEl = document.createElement('div'); pokemonEl.classList.add('pokemon'); const poke_types = pokemon.types.map(el => el.type.name); const name = pokemon.name[0].toUpperCase() + pokemon.name.slice(1); const type = main_types.find(type => poke_types.indexOf(type) > -1); const color = colors[type]; pokemonEl.style.backgroundColor = color; const pokeInnerHTML = ` <div class="img-container"> <img src="https://pokeres.bastionbot.org/images/pokemon/${pokemon.id}.png" alt =${name}> </div> <div class="info"> <span class="number">#${pokemon.id.toString().padStart(3,"0")}</span> <h3 class="name">${name}</h3> <small class="type">Type: <span>${type}</span></small> </div> `; pokemonEl.innerHTML = pokeInnerHTML; poke_container.appendChild(pokemonEl); } ``` # 製作一個網頁瀏覽計數器(連結失效) ## 成品: ![](https://i.imgur.com/abnaxsW.png) [成品網址]() ## 成品功能: 1. 當頁面重整時會更新瀏覽次數 # HTML ## html程式碼: ```htmlembedded= <body> <p>This page has</p> <h1 id="count">0</h1> <p>views</p> </body> ``` # CSS: 簡單的置中以及上色 ## CSS完整程式碼 ```css= @import url('https://fonts.googleapis.com/css?family=Muli&display=swap'); * { box-sizing: border-box; } body { background-color: #192a56; color: #fff; font-family: 'Muli', sans-serif; flex-direction: column; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; } h1 { font-size: 50px; margin: 0; } p { color: rgba(255, 255, 255, 0.8); letter-spacing: 2px; margin: 0; } ``` # JS: [count API官網](https://countapi.xyz/) 參考這邊的參數設置要使用的API ![](https://i.imgur.com/UklBjL9.png) 第一步 先設置namespace的名稱 `https://api.countapi.xyz/create?namespace=mysite.com&value=42` 第二步 設置key以及value為0 `https://api.countapi.xyz/create?namespace=mysite.com&key=mykey&value=0` 第三步 修改狀態為update並輸入amount=1 `https://api.countapi.xyz/update/mysite.com/mykey/?amount=1` 就可以開始使用此API瞜! ## 變數設置 * countEl ![](https://i.imgur.com/6Zw9v81.png) ## functions: `updateVisitCount()` * 把剛剛創建的API使用fetch抓取 * 擷取api內容從JSON轉換回來並且貼上HTML ## JS完整程式碼: ```javascript= const countEl = document.getElementById('count'); function updateVisitCount() { fetch('https://api.countapi.xyz/update/github/githubBlog/?amount=1') .then(res => res.json()) .then(res => { console.log(res); countEl.innerHTML = res.value; }) } updateVisitCount(); ``` # 製作一個 ## 成品: [成品網址]() ## 成品功能: # HTML ## html程式碼: ```htmlembedded= ``` # CSS: ## CSS完整程式碼 ```css= ``` # JS: ## 變數設置 ## functions: ## JS完整程式碼: ```javascript= ``` # 製作一個 ## 成品: [成品網址]() ## 成品功能: # HTML ## html程式碼: ```htmlembedded= ``` # CSS: ## CSS完整程式碼 ```css= ``` # JS: ## 變數設置 ## functions: ## JS完整程式碼: ```javascript= ``` # 製作一個 ## 成品: [成品網址]() ## 成品功能: # HTML ## html程式碼: ```htmlembedded= ``` # CSS: ## CSS完整程式碼 ```css= ``` # JS: ## 變數設置 ## functions: ## JS完整程式碼: ```javascript= ``` # 製作一個 ## 成品: [成品網址]() ## 成品功能: # HTML ## html程式碼: ```htmlembedded= ``` # CSS: ## CSS完整程式碼 ```css= ``` # JS: ## 變數設置 ## functions: ## JS完整程式碼: ```javascript= ``` # 製作一個 ## 成品: [成品網址]() ## 成品功能: # HTML ## html程式碼: ```htmlembedded= ``` # CSS: ## CSS完整程式碼 ```css= ``` # JS: ## 變數設置 ## functions: ## JS完整程式碼: ```javascript= ```