JS30 day27 - Click and Drag to Scroll
作業內容
這次的作業是做出可以用滑鼠左右拉讓螢幕滑動的效果
一樣 code 本身不複雜,但要想出怎麼做出來是需要創意的
可以參考這份 CodePen的效果
學到什麼
-
JS
- 細節:在抓點下去時候的 X 座標,我們抓 mouseDown 事件的 pageX 屬性
但如果把這個 pageX console.log 出來看會發現問題,如果 margin 變大,可以發現就算點在 slider 邊邊,他的起始座標就是 100 多而不是接近0,這是為什麼?
因為這 pageX 是在這頁面中的絕對座標,我們要的是相對於這個捲動頁面框框的座標,所以要把整個框框的 left 座標減掉
1 2 3 4 5 6 7
| slider.addEventListener('mousedown', (e)=> { isDown = true slider.classList.add('active') startX = e.pageX - slider.offsetLeft scollLeft = slider.scrollLeft console.log(e.pageX) })
|
- 細節: slider 觸發的 mousemove 事件這邊加上 preventDeault 是因為怕觸發意料之外的效果,像是選到文字等等,這個很容易漏掉
1 2 3 4 5 6 7
| slider.addEventListener('mousemove', (e)=> { if(!isDown) return; e.preventDefault() const x = e.pageX - slider.offsetLeft walk = x - startX slider.scrollLeft = scollLeft - walk })
|
- 有個很細節的地方在於,如果導覽列上方有東西,我們必須把座標值扣掉,否則白框位置會跑掉
1 2 3 4 5 6
| const coords = { height: dropdownCoords.height, width: dropdownCoords.width, top: dropdownCoords.top - navCoords.top, left: dropdownCoords.left - navCoords.left }
|
dropdownCoords.top - navCoords.top,
像這行就是下拉選單的座標扣掉導覽列的上方座標
- 除此之外還有更細節的地方,一開始程式碼長的是下面這樣
1 2 3 4 5 6 7 8 9 10 11
| function handleEnter(){ this.classList.add('trigger-enter') setTimeout(() => { this.classList.add('trigger-enter-active') },150) } function handleLeave(){ this.classList.remove('trigger-enter', 'trigger-enter-active') }
|
我再加上 class 的時候為了有動畫的效果,有個 setTimeout 設定 150 微秒之後才加上 trigger-enter-active
這 class,但移除的時候是同時移除
這樣會有個問題,當我在導覽列中快速移動,可能已經觸發到移除了,但 150 微秒還沒到,所以後面下拉選單的內容才跑出來
解決方法如下:再設上一個條件,150微秒的時候檢查是不是有一開始加上的那個 class trigger-enter
,沒有的話就不用再加上這個 class
1 2 3 4 5 6 7 8
| function handleEnter(){ this.classList.add('trigger-enter') setTimeout(() => { if(this.classList.contains('trigger-enter')){ this.classList.add('trigger-enter-active') } },150)
|
https://github.com/wesbos/JavaScript30
code 內容:
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
| <div class="items"> <div class="item item1">01</div> <div class="item item2">02</div> <div class="item item3">03</div> <div class="item item4">04</div> <div class="item item5">05</div> <div class="item item6">06</div> <div class="item item7">07</div> <div class="item item8">08</div> <div class="item item9">09</div> <div class="item item10">10</div> <div class="item item11">11</div> <div class="item item12">12</div> <div class="item item13">13</div> <div class="item item14">14</div> <div class="item item15">15</div> <div class="item item16">16</div> <div class="item item17">17</div> <div class="item item18">18</div> <div class="item item19">19</div> <div class="item item20">20</div> <div class="item item21">21</div> <div class="item item22">22</div> <div class="item item23">23</div> <div class="item item24">24</div> <div class="item item25">25</div> </div>
|
CSS:
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
| html { box-sizing: border-box; background: url('https://source.unsplash.com/NFs6dRTBgaM/2000x2000') fixed; background-size: cover; }
*, *:before, *:after { box-sizing: inherit; }
body { min-height: 100vh; display: flex; justify-content: center; align-items: center; font-family: sans-serif; font-size: 20px; margin: 0; }
.items { height: 800px; padding: 100px; width: 100%; border: 1px solid white; overflow-x: scroll; overflow-y: hidden; white-space: nowrap; user-select: none; cursor: pointer; transition: all 0.2s; transform: scale(0.98); will-change: transform; position: relative; background: rgba(255,255,255,0.1); font-size: 0; perspective: 500px; }
.items.active { background: rgba(255,255,255,0.3); cursor: grabbing; cursor: -webkit-grabbing; transform: scale(1); }
.item { width: 200px; height: calc(100% - 40px); display: inline-flex; align-items: center; justify-content: center; font-size: 80px; font-weight: 100; color: rgba(0,0,0,0.15); box-shadow: inset 0 0 0 10px rgba(0,0,0,0.15); }
.item:nth-child(9n+1) { background: dodgerblue;} .item:nth-child(9n+2) { background: goldenrod;} .item:nth-child(9n+3) { background: paleturquoise;} .item:nth-child(9n+4) { background: gold;} .item:nth-child(9n+5) { background: cadetblue;} .item:nth-child(9n+6) { background: tomato;} .item:nth-child(9n+7) { background: lightcoral;} .item:nth-child(9n+8) { background: darkslateblue;} .item:nth-child(9n+9) { background: rebeccapurple;}
.item:nth-child(even) { transform: scaleX(1.31) rotateY(40deg); } .item:nth-child(odd) { transform: scaleX(1.31) rotateY(-40deg); }
|
JS:
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
| const slider = document.querySelector('.items') let isDown = false let startX let scrollLeft
slider.addEventListener('mousedown', (e)=> { isDown = true slider.classList.add('active') startX = e.pageX - slider.offsetLeft scollLeft = slider.scrollLeft console.log(e.pageX) }) slider.addEventListener('mouseleave', ()=> { isDown = false slider.classList.remove('active') }) slider.addEventListener('mouseup', ()=> { isDown = false slider.classList.remove('active') }) slider.addEventListener('mousemove', (e)=> { if(!isDown) return; e.preventDefault() const x = e.pageX - slider.offsetLeft walk = x - startX slider.scrollLeft = scollLeft - walk })
|