Anthony Chao

JS30 day27 Click and Drag to Scroll

#Javascript

JS30 day27 - Click and Drag to Scroll

作業內容

這次的作業是做出可以用滑鼠左右拉讓螢幕滑動的效果 一樣 code 本身不複雜,但要想出怎麼做出來是需要創意的 可以參考這份 CodePen的效果

學到什麼

  • JS
    1. 細節:在抓點下去時候的 X 座標,我們抓 mouseDown 事件的 pageX 屬性 但如果把這個 pageX console.log 出來看會發現問題,如果 margin 變大,可以發現就算點在 slider 邊邊,他的起始座標就是 100 多而不是接近0,這是為什麼? 因為這 pageX 是在這頁面中的絕對座標,我們要的是相對於這個捲動頁面框框的座標,所以要把整個框框的 left 座標減掉
    slider.addEventListener('mousedown', (e)=> {
      isDown = true
      slider.classList.add('active')
      startX = e.pageX - slider.offsetLeft
      scollLeft = slider.scrollLeft // 要記錄一開始 slider 已經捲了多少
      console.log(e.pageX)
    })
    1. 細節: slider 觸發的 mousemove 事件這邊加上 preventDeault 是因為怕觸發意料之外的效果,像是選到文字等等,這個很容易漏掉
    slider.addEventListener('mousemove', (e)=> {
      if(!isDown) return; // 如果沒有按滑鼠不觸發
      e.preventDefault() // 避免捲動的時候去選框框內文字
      const x = e.pageX - slider.offsetLeft
      walk = x - startX
      slider.scrollLeft = scollLeft - walk
    })
    1. 有個很細節的地方在於,如果導覽列上方有東西,我們必須把座標值扣掉,否則白框位置會跑掉
    const coords = {
      height: dropdownCoords.height,
      width: dropdownCoords.width,
      top:  dropdownCoords.top - navCoords.top,
      left: dropdownCoords.left - navCoords.left
    }
    dropdownCoords.top - navCoords.top,像這行就是下拉選單的座標扣掉導覽列的上方座標 2. 除此之外還有更細節的地方,一開始程式碼長的是下面這樣
    function handleEnter(){
      this.classList.add('trigger-enter')
      setTimeout(() => {
        this.classList.add('trigger-enter-active')
      },150)
      //下略
    }
    function handleLeave(){
      this.classList.remove('trigger-enter', 'trigger-enter-active')
    }
      //下略
    我再加上 class 的時候為了有動畫的效果,有個 setTimeout 設定 150 微秒之後才加上 trigger-enter-active 這 class,但移除的時候是同時移除 這樣會有個問題,當我在導覽列中快速移動,可能已經觸發到移除了,但 150 微秒還沒到,所以後面下拉選單的內容才跑出來 解決方法如下:再設上一個條件,150微秒的時候檢查是不是有一開始加上的那個 class trigger-enter,沒有的話就不用再加上這個 class
    function handleEnter(){
      this.classList.add('trigger-enter')
      setTimeout(() => {
        if(this.classList.contains('trigger-enter')){
          this.classList.add('trigger-enter-active')
        }
      },150)
      //下略

https://github.com/wesbos/JavaScript30

code 內容:

HTML:

<div class="items">
    <div class="item item1">01</div>
    <div class="item item2">02</div>
    <div class="item item3">03</div>
    <div class="item item4">04</div>
    <div class="item item5">05</div>
    <div class="item item6">06</div>
    <div class="item item7">07</div>
    <div class="item item8">08</div>
    <div class="item item9">09</div>
    <div class="item item10">10</div>
    <div class="item item11">11</div>
    <div class="item item12">12</div>
    <div class="item item13">13</div>
    <div class="item item14">14</div>
    <div class="item item15">15</div>
    <div class="item item16">16</div>
    <div class="item item17">17</div>
    <div class="item item18">18</div>
    <div class="item item19">19</div>
    <div class="item item20">20</div>
    <div class="item item21">21</div>
    <div class="item item22">22</div>
    <div class="item item23">23</div>
    <div class="item item24">24</div>
    <div class="item item25">25</div>
</div>

CSS:

html {
  box-sizing: border-box;
  background: url('https://source.unsplash.com/NFs6dRTBgaM/2000x2000') fixed;
  background-size: cover;
}

*, *:before, *:after {
  box-sizing: inherit;
}

body {
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  font-family: sans-serif;
  font-size: 20px;
  margin: 0;
}

.items {
  height: 800px;
  padding: 100px;
  width: 100%;
  border: 1px solid white;
  overflow-x: scroll;
  overflow-y: hidden;
  white-space: nowrap;
  user-select: none;
  cursor: pointer;
  transition: all 0.2s;
  transform: scale(0.98);
  will-change: transform;
  position: relative;
  background: rgba(255,255,255,0.1);
  font-size: 0;
  perspective: 500px;
}

.items.active {
  background: rgba(255,255,255,0.3);
  cursor: grabbing;
  cursor: -webkit-grabbing;
  transform: scale(1);
}

.item {
  width: 200px;
  height: calc(100% - 40px);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 80px;
  font-weight: 100;
  color: rgba(0,0,0,0.15);
  box-shadow: inset 0 0 0 10px rgba(0,0,0,0.15);
}

.item:nth-child(9n+1) { background: dodgerblue;}
.item:nth-child(9n+2) { background: goldenrod;}
.item:nth-child(9n+3) { background: paleturquoise;}
.item:nth-child(9n+4) { background: gold;}
.item:nth-child(9n+5) { background: cadetblue;}
.item:nth-child(9n+6) { background: tomato;}
.item:nth-child(9n+7) { background: lightcoral;}
.item:nth-child(9n+8) { background: darkslateblue;}
.item:nth-child(9n+9) { background: rebeccapurple;}

.item:nth-child(even) { transform: scaleX(1.31) rotateY(40deg); }
.item:nth-child(odd) { transform: scaleX(1.31) rotateY(-40deg); }

JS:

const slider = document.querySelector('.items')
let isDown = false
let startX
let scrollLeft

slider.addEventListener('mousedown', (e)=> {
  isDown = true
  slider.classList.add('active')
  startX = e.pageX - slider.offsetLeft // 要減掉 slider 本身的位移減掉
  scollLeft = slider.scrollLeft // 要記錄一開始 slider 已經捲了多少
  console.log(e.pageX)
})
slider.addEventListener('mouseleave', ()=> {
  isDown = false
  slider.classList.remove('active')
})
slider.addEventListener('mouseup', ()=> {
  isDown = false
  slider.classList.remove('active')
})
slider.addEventListener('mousemove', (e)=> {
  if(!isDown) return; // 如果沒有按滑鼠不觸發
  e.preventDefault() // 避免捲動的時候去選框框內文字
  const x = e.pageX - slider.offsetLeft
  walk = x - startX
  slider.scrollLeft = scollLeft - walk
})