移动端滑动(touch)选项并实现多选效果

  • 移动端滑动选项实现多选效果,点击查看单选效果案例
  • 通过 touchstarttouchmovetouchendtouchcancel 事件实现
  • 通过父元素代理事件的方式实现子组件点击选中选项
  • 如果选项添加 disabled 属性将不会被选中
  • 移动端拖拽 .box.options 元素时,是有拖拽效果的,去除拖拽效果有两种方式:
    ① 用 css pointer-events: none; 阻止拖拽,.box 元素使用的是这种方式
    ② 用 js event.preventDefault() 阻止元素的事件, .options 元素使用的是这种方式
  • 如果需要通过鼠标滚轮来切换选项,可以使用 wheel 来实现,本案例没有实现
  • touchcancel 事件一直没有办法触发,所以没有验证效果

在这里插入图片描述

  • css

    * {
      margin: 0;
      padding: 0;
    }
    
    ul,
    li {
      list-style: none;
    }
    
    .box {
      height: 300px;
      width: 400px;
      margin: 50px auto;
      border: 1px solid #000;
      border-radius: 12px;
      position: relative;
      background-color: #f5f5f5;
      touch-action: none; /* 元素不能滑动 */
    }
    
    .box .columns {
      overflow: hidden;
      height: 100%;
    }
    
    .box .columns .options {
      transition-timing-function: cubic-bezier(0.23, 1, 0.68, 1);
      transition-duration: 0ms;
      transition-property: all;
      height: 100%;
    }
    
    .box .columns .options li {
      height: 60px;
      line-height: 60px;
      text-align: center;
      display: flex;
      justify-content: space-between;
      padding: 0 15px;
    }
    
    .box .columns .options li[disabled] {
      cursor: not-allowed;
      opacity: 0.3;
    }
    
    .box .columns .options li.selected {
      font-weight: bold;
    }
    
    .box .columns .options li.selected::after {
      content: '√';
      font-size: 16px;
      color: red;
    }
    
  • html

    <div class="box">
      <div class="columns">
        <ul class="options">
          <li data-index="0">选项一</li>
          <li disabled data-index="1">选项2</li>
          <li data-index="2">选项3</li>
          <li data-index="3">选项4</li>
          <li data-index="4">选项5</li>
          <li data-index="5">选项6</li>
          <li data-index="6">选项7</li>
          <li data-index="7">选项8</li>
          <li data-index="8">选项9</li>
        </ul>
      </div>
    </div>
    
  • javascript

    const boxEl = document.querySelector('.box')
    const optionsEl = document.querySelector('.options')
    const columnsEl = document.querySelector('.columns')
    const liFirstEl = document.querySelector('.options li:first-child')
    
    const boxHeight = boxEl.clientHeight
    const liHeight = liFirstEl.offsetHeight
    const liAllEl = optionsEl.querySelectorAll('li')
    const liCount = liAllEl.length
    const boxContainsLi = Math.floor(boxHeight / liHeight)
    
    let startY = 0
    let endY = 0
    let currentY = 0 // 当前Y轴移动的位置
    let isTouch = false // 是否是滑动事件
    const initPositionY = 0 // 进入页面时Y轴开始的位置
    let beginPositionY = initPositionY // 每次滚动后Y轴开始的位置
    const maxTranslateY = liHeight / 2 // Y轴最大移动距离(向下移动)
    const minTranslateY =
      liCount * liHeight > boxHeight
        ? -(liCount * liHeight - boxHeight + liHeight / 2)
        : -liHeight / 2 // Y轴最小移动距离(向上移动)
    
    ;(function () {
      // 初始化页面信息
      optionsEl.setAttribute(
        'style',
        `transition-duration: 0ms; transition-property: all;`
      )
      setTransform(beginPositionY, optionsEl)
    })()
    
    // 设置元素在Y轴的移动距离
    function setTransform(value, el) {
      el.style.transform = `translate3d(0px, ${value}px, 0px)`
    }
    
    // 获取元素在Y轴的移动距离
    function getTranslateY(el) {
      const translateStr = el.style.transform
      const valueStr = translateStr.match(/\(([^)]*)\)/)
      const valueArr = valueStr[1].split(',')
      return Number(valueArr[1].replace('px', ''))
    }
    
    // 给元素设置动画
    function setTransitionDuration(value, el) {
      el.style.transitionDuration = `${value}ms`
      el.style.transitionProperty = value === 0 ? 'none' : 'all'
    }
    
    // 重置动画
    function setTransitionNone(el) {
      setTimeout(() => {
        setTransitionDuration(0, el)
      }, 300)
    }
    
    // 设置 class
    function setClassName(index, elList) {
      elList[index].classList.toggle('selected')
    }
    
    // 阻止父元素的滑动
    columnsEl.addEventListener(
      'touchmove',
      function (e) {
        e.preventDefault()
      },
      false
    )
    
    // 父元素代理子元素点击事件
    columnsEl.addEventListener(
      'click',
      function (e) {
        console.log('click 事件触发')
    
        const target = e.target
        let index = 0
    
        // 方案一:遍历所有 li 得到 index
        // for (let i = 0; i < liCount; i++) {
        //   if (liAllEl[i] === target) {
        //     index = i
        //     break
        //   }
        // }
    
        // 方案二: 根据 data-index 获取当前 li 的index
        index = target.dataset.index
    
        if (liAllEl[index].hasAttribute('disabled')) {
          return false
        }
    
        setClassName(index, liAllEl)
      },
      false
    )
    
    // 开始滑动
    optionsEl.addEventListener(
      'touchstart',
      function (e) {
        console.log('touchstart 事件触发')
        startY = event.targetTouches[0].pageY
      },
      false
    )
    
    // 滑动中
    optionsEl.addEventListener(
      'touchmove',
      function (e) {
        endY = event.targetTouches[0].pageY
        let moveY = endY - startY
        if (moveY !== 0) {
          isTouch = true
        }
        console.log('touchmove 事件触发')
        currentY = beginPositionY + moveY
    
        // Y 轴位置大于最大位置
        if (currentY > maxTranslateY) {
          currentY = maxTranslateY
        } else if (currentY < minTranslateY) {
          currentY = minTranslateY
        }
        setTransform(currentY, optionsEl)
      },
      false
    )
    
    function touchend(e) {
      if (!isTouch) {
        return false
      }
      console.log('touchend 事件触发')
      setTransitionDuration(200, optionsEl)
    
      // 超过头部与底部位置
      if (currentY >= maxTranslateY) {
        currentY = initPositionY
      } else if (currentY <= minTranslateY) {
        currentY = currentY + liHeight / 2
      } else {
        let index
    
        // 根据不同的方向,回正所要选中的选项
        if (endY - startY > 0) {
          // 向下滚动
          index = Math.floor((initPositionY - currentY) / liHeight)
        } else {
          // 向上滚动
          index = Math.ceil((initPositionY - currentY) / liHeight)
        }
        index =
          index <= 0
            ? 0
            : index >= liCount - boxContainsLi
            ? liCount - boxContainsLi
            : index
    
        currentY = initPositionY - liHeight * index
        isTouch = false
      }
      setTransform(currentY, optionsEl)
      beginPositionY = currentY
      setTransitionNone(optionsEl)
    }
    // 滑动结束
    optionsEl.addEventListener('touchend', touchend, false)
    
    // 滑动取消
    optionsEl.addEventListener(
      'touchcancel',
      (evt) => {
        evt.preventDefault()
        console.log('touchcancel 事件触发')
        touchend(evt)
      },
      false
    )
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现移动touch事件的横向滑动列表效果可以使用原生的JavaScript和CSS3来实现。 首先,我们需要在HTML中创建一个容器元素,用来包含列表项。容器元素需要设置overflow-x属性为scroll,使得内容超出容器范围时可以滚动。 ```html <div class="container"> <ul class="list"> <li>项1</li> <li>项2</li> <li>项3</li> <li>项4</li> <li>项5</li> </ul> </div> ``` 然后,在CSS中,我们需要设置容器元素和列表项的样式,以及使用CSS3的transition属性来实现平滑的过渡效果。 ```css .container { width: 100%; overflow-x: scroll; -webkit-overflow-scrolling: touch; /* 添加iOS滚动效果 */ } .list { display: flex; flex-wrap: nowrap; /* 设置列表项不换行 */ transition: transform 0.3s ease; /* 添加平滑的过渡效果 */ } .list li { width: 100px; height: 100px; margin-right: 10px; background-color: #ccc; } ``` 最后,在JavaScript中,我们需要监听容器元素的touchstart、touchmove和touchend事件,计算滑动距离并通过改变列表项的transform属性来实现横向滑动效果。 ```javascript const container = document.querySelector('.container'); const list = document.querySelector('.list'); let isDragging = false; let startPosition = 0; let currentTranslate = 0; let prevTranslate = 0; let animationId = 0; container.addEventListener('touchstart', touchStart); container.addEventListener('touchmove', touchMove); container.addEventListener('touchend', touchEnd); container.addEventListener('touchcancel', touchEnd); function touchStart(event) { if (event.target.classList.contains('list')) { isDragging = true; startPosition = event.touches[0].clientX; animationId = requestAnimationFrame(updateAnimation); container.classList.add('grabbing'); } } function touchMove(event) { if (isDragging) { const currentPosition = event.touches[0].clientX; currentTranslate = prevTranslate + currentPosition - startPosition; } } function touchEnd() { isDragging = false; cancelAnimationFrame(animationId); prevTranslate = currentTranslate; container.classList.remove('grabbing'); } function updateAnimation() { list.style.transform = `translateX(${currentTranslate}px)`; animationId = requestAnimationFrame(updateAnimation); } ``` 通过以上代码,我们就成功地实现移动touch事件的横向滑动列表效果

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值