05 - Flex Panel Gallery
俗話說的好,一天一蘋果,醫生遠離我
一天一 JS,What the f*ck JavaScript?
small steps every day - 記錄著新手村日記
完成目標
- 功能(CSS居多)
- 點擊指定的區塊有兩段式的動畫變化
- 再點擊一次,恢復原狀
- 畫面
- 改用 Flex 排版
- 標籤 p 中間的字變大
- 變大之後上下的字要進來
index_START.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Flex Panels 💪</title> <link href='https://fonts.googleapis.com/css?family=Amatic+SC' rel='stylesheet' type='text/css'> </head> <body> <style> html { box-sizing: border-box; background: #ffc600; font-family: 'helvetica neue'; font-size: 20px; font-weight: 200; } body { margin: 0; } *, *:before, *:after { box-sizing: inherit; }
.panels { min-height: 100vh; overflow: hidden; }
.panel { background: #6B0F9C; box-shadow: inset 0 0 0 5px rgba(255,255,255,0.1); color: white; text-align: center; align-items: center; transition: font-size 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11), flex 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11), background 0.2s; font-size: 20px; background-size: cover; background-position: center; }
.panel1 { background-image:url(https://source.unsplash.com/gYl-UtwNg_I/1500x1500); } .panel2 { background-image:url(https://source.unsplash.com/rFKUFzjPYiQ/1500x1500); } .panel3 { background-image:url(https://images.unsplash.com/photo-1465188162913-8fb5709d6d57?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&w=1500&h=1500&fit=crop&s=967e8a713a4e395260793fc8c802901d); } .panel4 { background-image:url(https://source.unsplash.com/ITjiVXcwVng/1500x1500); } .panel5 { background-image:url(https://source.unsplash.com/3MNzGlQM7qs/1500x1500); }
.panel > * { margin: 0; width: 100%; transition: transform 0.5s; }
.panel p { text-transform: uppercase; font-family: 'Amatic SC', cursive; text-shadow: 0 0 4px rgba(0, 0, 0, 0.72), 0 0 14px rgba(0, 0, 0, 0.45); font-size: 2em; } .panel p:nth-child(2) { font-size: 4em; }
.panel.open { font-size: 40px; }
</style> <div class="panels"> <div class="panel panel1"> <p>Hey</p> <p>Let's</p> <p>Dance</p> </div> <div class="panel panel2"> <p>Give</p> <p>Take</p> <p>Receive</p> </div> <div class="panel panel3"> <p>Experience</p> <p>It</p> <p>Today</p> </div> <div class="panel panel4"> <p>Give</p> <p>All</p> <p>You can</p> </div> <div class="panel panel5"> <p>Life</p> <p>In</p> <p>Motion</p> </div> </div> </body> </html>
|
CSS - step by step
原本的 .panels
為直行,現在透過 flex
來改變排列方式,變為橫排並將內容 .panel
佔據整個版面一等份
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <style> .panels { min-height: 100vh; overflow: hidden; display: flex; }
.panel { flex: 1; background: #6B0F9C; box-shadow: inset 0 0 0 5px rgba(255,255,255,0.1); color: white; text-align: center; align-items: center; transition: font-size 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11), flex 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11), background 0.2s; font-size: 20px; background-size: cover; background-position: center; } </style>
|
Flex
主軸為橫向(文字會左到右),內層的 .panel
要來改變主軸方向並置中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <style> .panels { min-height: 100vh; overflow: hidden; display: flex; }
.panel { flex: 1; display: flex; flex-direction: column; justify-content: center; background: #6B0F9C; box-shadow: inset 0 0 0 5px rgba(255,255,255,0.1); color: white; text-align: center; align-items: center; transition: font-size 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11), flex 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11), background 0.2s; font-size: 20px; background-size: cover; background-position: center; } </style>
|
子層 .panel > *
也要有垂直置中效果,並讓上下的標籤 p
文字偏移出去中間的標籤 p
文字後才能加入動畫
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| <style>
.panels { min-height: 100vh; overflow: hidden; display: flex; }
.panel { flex: 1; display: flex; flex-direction: column; justify-content: center;
background: #6B0F9C; box-shadow: inset 0 0 0 5px rgba(255,255,255,0.1); color: white; text-align: center; align-items: center; transition: font-size 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11), flex 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11), background 0.2s; font-size: 20px; background-size: cover; background-position: center; }
.panel > * { margin: 0; width: 100%; transition: transform 0.5s;
flex: 1; display: flex; justify-content: center; align-items: center; } .panel > *:first-child{ transform: translateY(-100%); } .panel > *:last-child{ transform: translateY(100%); } .panel.open-active > *:first-child{ transform: translateY(0%); } .panel.open-active > *:last-child{ transform: translateY(0%); } .panel.open { font-size: 40px; flex: 3; } </style>
|
JS - step by step
首先,抓出整個外層的 NodeList 並設為變數 panels 吧!忘記有 All 跟沒有 All 的差別可以看 Day 1 JavaScript Drum Kit 的文章,或者在這個程式碼中直接 console.log 印出來看看~
1 2 3 4 5 6 7
| <script> const panels = document.querySelectorAll(".panel") console.log(panels)
</script>
|
記得 panels 不是一個陣列所以透過 foreach
讀出每個 List,方法中監聽假設使用者有 click
的話,就會呼叫方法 clickHandler
1 2 3 4 5 6
| <script> const panels = document.querySelectorAll(".panel") panels.forEach(function(panel){ panel.addEventListener('click',clickHandler) }) </script>
|
clickHandler
方法來對 .panel
增加 Class .panel.open
(字體放大、Flex 等份變大),因為要能放大後再點又能縮小,所以用 toggle
來新增、移除,與Jquery的概念相同,對應如下:
JS:classList.add / classList.remove / classList.toogle
Jquery:addClass / removeClass / googleClass
1 2 3 4 5 6 7 8 9
| <script> const panels = document.querySelectorAll(".panel") panels.forEach(function(panel){ panel.addEventListener('click',clickHandler) }) function clickHandler(){ this.classList.toggle('open') } </script>
|
現在要來做第二段動畫,在這邊我們希望在第一段動畫完成之後再做第二段動畫,所以我們來 transitionend
呼叫 transitionendHandler
方法看看 click 動畫結束後回傳什麼
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script> const panels = document.querySelectorAll(".panel") panels.forEach(function(panel){ panel.addEventListener('click',clickHandler) panel.addEventListener('transitionend',transitionendHandler) }) function clickHandler(){ this.classList.toggle('open') } function transitionendHandler(e){ console.log(e) } </script>
|
看到了觸發兩個事件有什麼關聯呢?其實會有個Bug,因為假設直接寫了 toogle 在 transitionendHandler
中的話,事件有兩個所以被觸發了兩次,導致上下的文字進來了又出去,但如果今天回傳的事件是奇數又會可以觸發。因此,我們要寫個判斷式來觸發 Class open-active
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <script> const panels = document.querySelectorAll(".panel") panels.forEach(function(panel){ panel.addEventListener('click',clickHandler) panel.addEventListener('transitionend',transitionendHandler) }) function clickHandler(){ this.classList.toggle('open') } function transitionendHandler(e){ console.log(e) if(e.propertyName.indexOf('flex') !== -1){ this.classList.toggle('open-active') } } </script>
|
CSS - Final
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| <style> html { box-sizing: border-box; background: #ffc600; font-family: 'helvetica neue'; font-size: 20px; font-weight: 200; } body { margin: 0; } *, *:before, *:after { box-sizing: inherit; }
.panels { min-height: 100vh; overflow: hidden; display: flex; }
.panel { flex: 1; display: flex; flex-direction: column; justify-content: center;
background: #6B0F9C; box-shadow: inset 0 0 0 5px rgba(255,255,255,0.1); color: white; text-align: center; align-items: center; transition: font-size 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11), flex 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11), background 0.2s; font-size: 20px; background-size: cover; background-position: center; }
.panel1 { background-image:url(https://source.unsplash.com/gYl-UtwNg_I/1500x1500); } .panel2 { background-image:url(https://source.unsplash.com/rFKUFzjPYiQ/1500x1500); } .panel3 { background-image:url(https://images.unsplash.com/photo-1465188162913-8fb5709d6d57?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&w=1500&h=1500&fit=crop&s=967e8a713a4e395260793fc8c802901d); } .panel4 { background-image:url(https://source.unsplash.com/ITjiVXcwVng/1500x1500); } .panel5 { background-image:url(https://source.unsplash.com/3MNzGlQM7qs/1500x1500); }
.panel > * { margin: 0; width: 100%; transition: transform 0.5s; flex: 1; display: flex; justify-content: center; align-items: center; }
.panel p { text-transform: uppercase; font-family: 'Amatic SC', cursive; text-shadow: 0 0 4px rgba(0, 0, 0, 0.72), 0 0 14px rgba(0, 0, 0, 0.45); font-size: 2em; } .panel > *:first-child{ transform: translateY(-100%); } .panel > *:last-child{ transform: translateY(100%); } .panel.open-active > *:first-child{ transform: translateY(0%); } .panel.open-active > *:last-child{ transform: translateY(0%); } .panel p:nth-child(2) { font-size: 4em; }
.panel.open { font-size: 40px; flex: 3; } </style>
|
JS - Final
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <script> const panels = document.querySelectorAll(".panel") panels.forEach(function(panel){ panel.addEventListener('click',clickHandler) panel.addEventListener('transitionend',transitionendHandler) }) function clickHandler(){ this.classList.toggle('open') } function transitionendHandler(e){ console.log(e) if(e.propertyName.indexOf('flex') !== -1){ this.classList.toggle('open-active') } } </script>
|
本次範例程式碼原作者來源:https://reurl.cc/M7Z0xW