iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 27
0
Modern Web

JS30 錄系列 第 27

Day 27 - Click and Drag

任務目標

點選物件、拖曳滑鼠讓該物件滑動。範例連結

作法

以下是 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
完整程式碼


上一篇
Day 26 - Follow Along Dropdown
下一篇
Day 28 - Video Speed Controller
系列文
JS30 錄30

尚未有邦友留言

立即登入留言