JS30 day27 Click and Drag to Scroll
JS30 day27 - Click and Drag to Scroll
作業內容
這次的作業是做出可以用滑鼠左右拉讓螢幕滑動的效果
一樣 code 本身不複雜,但要想出怎麼做出來是需要創意的
可以參考這份 CodePen的效果

學到什麼
- JS
- 細節:在抓點下去時候的 X 座標,我們抓 mouseDown 事件的 pageX 屬性 但如果把這個 pageX console.log 出來看會發現問題,如果 margin 變大,可以發現就算點在 slider 邊邊,他的起始座標就是 100 多而不是接近0,這是為什麼? 因為這 pageX 是在這頁面中的絕對座標,我們要的是相對於這個捲動頁面框框的座標,所以要把整個框框的 left 座標減掉
slider.addEventListener('mousedown', (e)=> { isDown = true slider.classList.add('active') startX = e.pageX - slider.offsetLeft scollLeft = slider.scrollLeft // 要記錄一開始 slider 已經捲了多少 console.log(e.pageX) })- 細節: slider 觸發的 mousemove 事件這邊加上 preventDeault 是因為怕觸發意料之外的效果,像是選到文字等等,這個很容易漏掉
slider.addEventListener('mousemove', (e)=> { if(!isDown) return; // 如果沒有按滑鼠不觸發 e.preventDefault() // 避免捲動的時候去選框框內文字 const x = e.pageX - slider.offsetLeft walk = x - startX slider.scrollLeft = scollLeft - walk })- 有個很細節的地方在於,如果導覽列上方有東西,我們必須把座標值扣掉,否則白框位置會跑掉
const coords = { height: dropdownCoords.height, width: dropdownCoords.width, top: dropdownCoords.top - navCoords.top, left: dropdownCoords.left - navCoords.left }dropdownCoords.top - navCoords.top,像這行就是下拉選單的座標扣掉導覽列的上方座標 2. 除此之外還有更細節的地方,一開始程式碼長的是下面這樣
我再加上 class 的時候為了有動畫的效果,有個 setTimeout 設定 150 微秒之後才加上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') } //下略trigger-enter-active這 class,但移除的時候是同時移除 這樣會有個問題,當我在導覽列中快速移動,可能已經觸發到移除了,但 150 微秒還沒到,所以後面下拉選單的內容才跑出來 解決方法如下:再設上一個條件,150微秒的時候檢查是不是有一開始加上的那個 classtrigger-enter,沒有的話就不用再加上這個 classfunction 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:
<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:
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:
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 // 要減掉 slider 本身的位移減掉
scollLeft = slider.scrollLeft // 要記錄一開始 slider 已經捲了多少
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
})