點選物件、拖曳滑鼠讓該物件滑動。範例連結
以下是 HTML 架構。
<div class="items">
<div class="item item1">01</div>
<div class="item item2">02</div>
<!-- 中間省略... -->
<div class="item item25">25</div>
</div>
items
元素做為容器,裡面放了由眾多 item
所組成,總寬度超過容器的東西。
看範例我們會注意到所有 item
橫著排列但角度卻不太相同,宛如手風琴,那是由於使用了 CSS 3D 配上 transform: rotateY
的效果。CSS 如下:
.items {
perspective: 500px;
}
.item:nth-child(even) { transform: scaleX(1.31) rotateY(40deg); }
.item:nth-child(odd) { transform: scaleX(1.31) rotateY(-40deg); }
rotateY
就是讓元素繞著 Y 軸旋轉的意思。然而,純粹使用 transform: rotateY
, 物件會變形,但是看起來卻像是元素的面積在變化而已,沒有立體感,那是由於我們缺乏一個觀察這個 3D 變化的「視角」。
聽起來很抽象,但想像一下,站在物件的正前方看著該物件,雖然角度都是正前方,但是距離 500 公尺 和距離 5000 公尺看到的物件是否略有不同?這就是視角 perspective
的功用,它會模擬在 3D 空間內看東西的距離感。也是 2D 和 3D 之間的差別。
perspective
可以設定我們在距離多遠的地方觀看 3D 元素。若沒有加上 perspective
,我們就像在無窮遠的地方看著元素一樣,即使有Y軸翻轉,看起來也像是平面。
接下來是這篇文章的重點,如何實現拖曳物件滑動的效果?
當內容物件寬度超過容器時,可以透過在容器上面設置 overflow: scroll
來讓容器可以用滾軸的方式查看超出寬度的物件。
.items {
overflow-x: scroll;
}
拖曳就是在 mousedown
的情況下 mousemove
,只要監聽 mousemove
事件,並在確定當下有 mousedown
時在進行動作即可。程式碼如下:
const slider = document.querySelector('.items');
let isDown = false;
let startX;
let scrollLeft;
slider.addEventListener('mousedown', (e) => {
// 按下就將 flag 設為 true
isDown = true;
slider.classList.add('active');
// 設立起始點
startX = e.pageX - slider.offsetLeft;
scrollLeft = slider.scrollLeft;
});
slider.addEventListener('mousemove', (e) => {
// 滑鼠有被按下才會繼續執行
if(!isDown) return;
// 阻止預設拖曳會選取的行為
e.preventDefault();
// 計算
const x = e.pageX - slider.offsetLeft;
const walk = (x - startX) * 3;
slider.scrollLeft = scrollLeft - walk;
});
我們的目的為,利用拖曳的距離來決定滾軸移動距離,所以拖曳滑鼠越遠,滾軸移動距離越大。為此,在滑鼠按下去時,會先記錄起始位置 startX
, 將其與拖曳的最終位置 x
相減便能得到移動距離。接著讓滾軸移動相反方向的該距離就能達到效果。
startX
由滑鼠按下去時在頁面上的位置 e.pageX
扣掉容器元素與畫面邊緣的距離 slider.offsetLeft
來得到。
最終位置 x
由一樣的方式決定,只是會隨著 mousemove
而不停更新該位置到最後一刻。
walk
是我們所算出來的移動距離乘上我們所期望的放大倍率。有時候我們會希望該滾軸移動快點。
scrollLeft
屬性決定一個有滾軸的元素,其滾軸距離起點的位置。以 walk
為變化長度, 更新該位置就行了。
最後記得讓滑鼠放開時能夠重置 isDown
開關,就大功告成。
slider.addEventListener('mouseleave', () => {
isDown = false;
slider.classList.remove('active');
});
slider.addEventListener('mouseup', () => {
isDown = false;
slider.classList.remove('active');
});
以上就是 JS30 第二十七篇!
##Reference
rotateY
perspective
scrollLeft
完整程式碼