JS30 全攻略 第27天

前言

JS 30 是由加拿大的全端工程師 Wes Bos 免費提供的 JavaScript 簡單應用課程,課程主打 No Frameworks、No Compilers、No Libraries、No Boilerplate 在30天的30部教學影片裡,建立30個JavaScript的有趣小東西。

另外,Wes Bos 也很無私地在 Github 上公開了所有 JS 30 課程的程式碼,有興趣的話可以去 fork 或下載。


本日目標

實作出能夠按住滑鼠左右拖曳的水平捲軸。


解析程式碼

HTML 部分

捲軸(div.items)由內部的25個div.item共同組成。

1
2
3
4
5
6
7
8
9
<div class="items">
<div class="item item1">01</div>
<div class="item item2">02</div>
<div class="item item3">03</div>
<!--中間省略...-->
<div class="item item23">23</div>
<div class="item item24">24</div>
<div class="item item25">25</div>
</div>

JS 部分

宣告常數slider取得捲軸(div.items)元素。

宣告變數isDown作為判斷滑鼠是否有被按住的 flag,給定預設值是 false。

宣告變數startX用來放入之後取得的移動起始點座標。

宣告變數scrollLeft用來取得div.items下方 scrollbar 的位置,最左方是 0。

1
2
3
4
const slider = document.querySelector('.items');
let isDown = false; //flag variable
let startX;
let scrollLeft;

當滑鼠被按住(mousedown) : 把isDown設為true

當滑鼠離開 slider 的範圍(mouseleave) : 把isDown設為false,因為離開 slider 範圍不應再有拖拉的效果(不執行mousemove裡面的內容)。

當放開滑鼠(mouseup) : 把isDown設為false,因為mouseup代表已放開滑鼠。

當滑鼠在 slider 裡移動(mousemove) : 先判斷滑鼠是否被按住,若沒被按住(isDown = false)則直接跳出方法不往下執行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
slider.addEventListener('mousedown', () => {
isDown = true;
});

slider.addEventListener('mouseleave', () => {
isDown = false;
});

slider.addEventListener('mouseup', () => {
isDown = false;
});

slider.addEventListener('mousemove', () => {
if(!isDown) return; //stop the function from running
});

接下來在按住滑鼠時,在 slider 上添加.active這個 class,而在滑鼠離開 slider 範圍或放開滑鼠時,移除 slider 上的.active

.active這個 CSS class 選擇器,用來設定拖拉捲軸時產生的效果,例如處於拖拉狀態,捲軸(.items)會比原來的大小略為放大一點。

event.preventDefault()用來取消DOM的預設功能,在這裡是避免被 browser 認為想要選取文字之類的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
slider.addEventListener('mousedown', () => {
isDown = true;
slider.classList.add('active');
});

slider.addEventListener('mouseleave', () => {
isDown = false;
slider.classList.remove('active');
});

slider.addEventListener('mouseup', () => {
isDown = false;
slider.classList.remove('active');
});

slider.addEventListener('mousemove', () => {
if(!isDown) return; //stop the function from running
e.preventDefault();
});

在 slider 裡面按住滑鼠(mousedown),首先要做的是取得滑鼠在整個 HTML 文件的座標(會隨著捲軸移動改變),所以先呼叫e.pageX。那為什麼還要減去slider.offserLeft呢? 因為我們要取得的是在 slider 裡面的 x 座標,而 slider 剛好有左 margin,所以要減掉offsetLeft修正再放回startX(滑鼠在 slider 裡的 x 座標)。

宣告常數scrollLeft放入按住滑鼠時 slider 下方 scrollbar 的位置。

1
2
3
4
5
6
7
8
slider.addEventListener('mousedown', (e) => {
//省略...
//know where we click on the slider
startX = e.pageX - slider.offsetLeft;// if the slider has the margin left then we should correct it
scrollLeft = slider.scrollLeft;
console.log(startX);
console.log(scrollLeft);
});

mousemove event handler裡,宣告常數x不斷更新滑鼠在 slider 內移動的座標,跟上面一樣要把 e.pageX 減去 slider.offsetLeft 作 slider 有左 morgin 時的修正。

常數walk放入移動時的x座標減去起始點的x座標作為移動下方 scrollbar 的依據,把算出來的值乘上3是為了讓 scrollbar 移動的距離更加大、明顯。

最後,為了讓移動時更加順暢,把先前取得的 scrollLeft 減去 walk,指定捲軸移動的位置和距離大小,這裡一定要用減的,因為移動方向和算出來的值剛好差負號。

舉例來說按住滑鼠向左拉,此時的捲軸應該要往右(+)移動,但算出的 walk 會是負的,所以要再加上-號修正。

1
2
3
4
5
6
slider.addEventListener('mousemove', (e) => {
//省略...
const x = e.pageX - slider.offsetLeft;
const walk = (x - startX)*3;
slider.scrollLeft = scrollLeft - walk;
});

補充資料 :

Element.scrollLeft
HTMLElement.offsetLeft
MouseEvent.pageX
JS一秒區分clientX,offsetX,screenX,pageX之間關係

範例網頁請點此

完整程式碼請點此

分享到