iT邦幫忙

2021 iThome 鐵人賽

DAY 27
1
自我挑戰組

JS30 學習日記系列 第 27

Day 27 - Click and Drag to Scroll

  • 分享至 

  • xImage
  •  

前言

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

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


本日目標

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


解析程式碼

HTML 部分

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

<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。

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)則直接跳出方法不往下執行。

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 認為想要選取文字之類的。

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 的位置。

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 會是負的,所以要再加上-號修正。

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之間關係

範例網頁請點此

完整程式碼請點此


上一篇
Day 26 - Stripe Follow Along Dropdown Navigation [更新]
下一篇
Day 28 - Build a Experimental Video Speed Controller UI
系列文
JS30 學習日記31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言