Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

纯手撸移动端网页滑动 #88

Open
lizhongzhen11 opened this issue Apr 1, 2020 · 0 comments
Open

纯手撸移动端网页滑动 #88

lizhongzhen11 opened this issue Apr 1, 2020 · 0 comments

Comments

@lizhongzhen11
Copy link
Owner

其实和轮播原理一样,只不过它是通过touch移动整体做到的,写了我4.5个小时,又变菜了。

主要是移动左右控制偏移问题,这个交互依赖于每个item的宽度以及移动视窗宽度(排除滚动条),具体代码如下:

<!-- 移动端分类菜单移到顶部 -->
    <div class="mobile-category" :class="mobileCategoryClass" v-if="mode === 'mobile'">
      <div class="mobile-swiper-container">
        <div class="mobile-swiper-wrapper relative hidden">
          <ul :style="setUlStyle" 
            @mousedown="handleStart" 
            @mousemove="handleMove"
            @mouseup="handleEnd"
            @touchstart="handleStart" 
            @touchmove="handleMove" 
            @touchend="handleEnd">
            <li class="inline-block left" :style="setLiWidth" v-for="i in categories" :key="i">
              <el-tag class="pointer relative hidden center" :class="getActive(i)" effect="dark" @click="handleChooseCategory(i)">分类{{ i }}</el-tag>
            </li>
          </ul>
        </div>
      </div>
    </div>
clientX = 0 // 移动端顶部分类菜单touchstart点到左侧距离
moveLeft = 0 // 移动端顶部分类菜单移动距离

get setUlStyle() {
  return `width: ${this.categories * 25}%; transform: translate3d(${this.moveLeft}px, 0, 0);`
}

get setLiWidth() {
  return `width: ${(document.body.clientWidth || document.documentElement.clientWidth) / 4}px;`
}

// 得到移动端分类最多可水平移动的距离
get maxMove() {
  let clientWidth = document.body.clientWidth || document.documentElement.clientWidth // 移动端视窗宽度
  let liWidth = clientWidth / 4 // 移动端分类菜单每个li宽度
  return this.categories * liWidth - clientWidth
}

// 记住touch起点
handleStart(e: any) {
  if (this.categories <= 4) {
    return
  }
  this.clientX = e?.targetTouches?.[0]?.clientX || e?.clientX
}

// 移动端 水平移动分类菜单
handleMove(e: any) {
  e.preventDefault();
  let clientX = e?.targetTouches?.[0]?.clientX || e?.clientX
  let diff = clientX - this.clientX
  if (this.categories <= 4 || Math.abs(this.moveLeft) > this.maxMove || diff === 0 || this.clientX === 0) {
    return
  }
  let clientWidth = document.body.clientWidth || document.documentElement.clientWidth
  let liWidth = clientWidth / 4
  let ulWidth = this.categories * liWidth
  if (diff < 0) { // 左移
    let sum = Math.abs(this.moveLeft) + Math.abs(diff)
    this.moveLeft = sum + clientWidth < ulWidth ? -sum : -this.maxMove
  }
  if (diff > 0) { // 右移
    let moveLeft = this.moveLeft + diff
    this.moveLeft = moveLeft <= 0 ? moveLeft : 0
  }
}

// touch 移动结束,修正水平移动值,保持4个li正好显示
handleEnd(e: any) {
  if (this.categories <= 4 || Math.abs(this.moveLeft) > this.maxMove) {
    return
  }
  let endClientX = e?.changedTouches?.[0]?.clientX || e?.clientX
  let direction = endClientX - this.clientX > 0 ? 'right' : 'left' // 判断水平移动方向
  let moveLeft = this.moveLeft // 肯定小于或等于 0
  let liWidth = (document.body.clientWidth || document.documentElement.clientWidth) / 4 // 拿到每个li宽度
  let halfLiWidth = liWidth / 2 // 每个li宽度的一半
  let ulWidth = this.categories * liWidth // 整个可移动ul的宽度
  let num = Math.floor(- moveLeft / liWidth) // 移动的距离涵盖li的个数
  // 如果余数比li宽度一半大,那么多移一个li;
  // 保持正好四个li显示
  let interDistance = num * liWidth // 肯定为非负数
  this.moveLeft = - moveLeft - interDistance > halfLiWidth ? - interDistance - liWidth : - interDistance
  this.clientX = 0
}

主要是移动时,需要保持4个li完整显示,那么就需要对移动的距离进行处理,这里耗费时间有点多,一时没想明白,其实不能保证每次移动距离是li宽度整数倍,那么需要除以li宽度得到 移动距离能包含的li整数个 以及多出来的差,利用差和li宽度一半进行比对,大于一半,那么要多移动一个li,不然只移动 整数个li。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant