Day 27 - Click and Drag to Scroll

前言

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之间关系

范例网页请点此

完整程序码请点此


<<:  Day12 安装外接磁碟机与磁碟片 - 取得 Lua shell script 的参数列

>>:  [Day12]ISO 27001 标准:验证范围

Flask API-取得request资料(以ticks API为例)

上次我们测试Flask API已经成功了, 但里面的参数例如时间等都是直接写死在程序码里, 但正常来...

【设计+切版30天实作】|Day13 - [设计进阶挑战] 把原本Plans的背景图形改成特殊形状

设计大纲 昨天设计的「方案」区块的背景设计是单纯一个长方形+背景颜色+阴影。今天想来做点不一样的,所...

[ 卡卡 DAY 21 ] - React Native 资料手机存起来 AsyncStorage

如果怕手机关掉东西就不见了 来使用 AsyncStorage 将状态存到手机也就是 local s...

day25 : kong api gateway(下)

kong的安装我同样会透过operator的方式进行,相信经过了这几天应该很多人都感受到operat...

[Day14]Fourth Point!!

上一篇介绍了Parking,题目是说在一条很长的道路上,选择任意位子停车,要输出走去各点并回到停车处...