【vue移动端】实现左滑删除功能

280 阅读2分钟

一、应用场景

移动端我们经常会遇到需要左滑删除列表中某一项的场景,效果如下图:

QQ录屏20241217103323.gif

二、代码实现

具体实现代码如下,可直接运行查看效果

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>移动端-左滑删除案例</title>
  <script src="https://cdn.staticfile.net/vue/2.7.0/vue.min.js"></script>
  <style>
    body,
    p {
      margin: 0;
      padding: 0;
    }

    .flex-between {
      display: flex;
      justify-content: space-between;
      align-items: center;
    }

    .flex-colum-between {
      display: flex;
      flex-direction: column;
      justify-content: space-between;
    }

    .flex-1 {
      flex: 1;
    }

    .center {
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .header {
      font-size: 18px;
      font-weight: 600;
      height: 44px;
      line-height: 44px;
      text-align: center;
      color: #fff;
      background: #3C8CFF;
    }

    .goods-box {
      overflow: hidden;
    }

    .goods-item {
      transition: all 0.2s;
    }

    /* =0隐藏 */
    .goods-item[data-type="0"] {
      transform: translate3d(0, 0, 0);
    }

    /* =1显示 */
    .goods-item[data-type="1"] {
      transform: translate3d(-62px, 0, 0);
    }

    .goods-info {
      position: relative;
      display: flex;
      padding: 8px 16px;
      border-bottom: 1px solid #ccc;
      box-sizing: border-box;
    }

    .goods-info img {
      width: 88px;
      height: 88px;
      margin-right: 8px;
      border-radius: 8px;
      background: #ccc;
    }

    .goods-info .name {
      font-size: 12px;
      font-weight: 600;
      line-height: 16px;
      color: #323233;
    }

    .goods-info .des,
    .goods-info .total {
      font-size: 12px;
      line-height: 16px;
      color: #969799;
    }

    .goods-info .price {
      font-size: 16px;
      font-weight: 600;
      line-height: 18px;
      color: #323233;
    }

    .goods-info .removeBtn {
      position: absolute;
      right: -62px;
      top: 0;
      height: 100%;
      width: 60px;
      color: #fff;
      background: #ee0a24;
    }
  </style>
</head>

<body>
  <div id="app">
    <div class="header">左滑删除案例</div>
    <div class="goods-box">
      <div class="goods-item" v-for="(goods,idx) in goodsList" data-type="0">
        <div class="goods-info" @touchstart.capture="touchStart" @touchend.capture="touchEnd" @click="oneself">
          <img :src="goods.img" alt="加载失败" />
          <div class="flex-1 flex-colum-between">
            <div>
              <p class="name">{{goods.name}}</p>
              <p class="des">{{goods.des}}</p>
            </div>
            <div class="flex-between">
              <span class="price">¥{{goods.price}}</span>
              <span class="total">x{{goods.total}}</span>
            </div>
          </div>
          <div class="removeBtn center" @click="remove(idx)">删除</div>
        </div>
      </div>
    </div>

  </div>

  <script>
    new Vue({
      el: '#app',
      data: {
        startX: 0, //滑动开始
        endX: 0, //滑动结束
        goodsList: [{
            name: '商品标题-1',
            price: '1.00',
            des: '描述信息-1',
            total: 1,
            img: 'https://fastly.jsdelivr.net/npm/@vant/assets/ipad.jpeg'
          },
          {
            name: '商品标题-2',
            price: '2.00',
            des: '描述信息-2',
            total: 2,
            img: 'https://fastly.jsdelivr.net/npm/@vant/assets/ipad.jpeg'
          },
          {
            name: '商品标题-3',
            price: '3.00',
            des: '描述信息-3',
            total: 3,
            img: 'https://fastly.jsdelivr.net/npm/@vant/assets/ipad.jpeg'
          },
          {
            name: '商品标题-4',
            price: '4.00',
            des: '描述信息-4',
            total: 4,
            img: 'https://fastly.jsdelivr.net/npm/@vant/assets/ipad.jpeg'
          }
        ]
      },
      methods: {
        //滑动开始
        touchStart(e) {
          this.startX = e.touches[0].clientX; // 记录初始位置
        },
        //滑动结束
        touchEnd(e) {
          // 当前滑动的父级元素
          let parentElement = e.currentTarget.parentElement;
          // 记录结束位置
          this.endX = e.changedTouches[0].clientX;
          // 左滑大于30距离删除出现
          if (parentElement.dataset.type == 0 && this.startX - this.endX > 30) {
            this.restSlide();
            parentElement.dataset.type = 1;
          }
          // 右滑
          if (parentElement.dataset.type == 1 && this.startX - this.endX < -30) {
            this.restSlide();
            parentElement.dataset.type = 0;
          }
          this.startX = 0;
          this.endX = 0;
        },
        //判断当前是否有滑块处于滑动状态
        checkSlide() {
          let listItems = document.querySelectorAll(".goods-item");
          for (let i = 0; i < listItems.length; i++) {
            if (listItems[i].dataset.type == 1) {
              return true;
            }
          }
          return false;
        },
        // 向左滑动出现删除按钮时,点击商品信息区域取消删除
        oneself() {
          if (this.checkSlide()) {
            this.restSlide();
          } else {
            // 点击商品信息弹出弹框
            console.log("点击当前商品触发事件...");
          }
        },
        //复位滑动状态
        restSlide() {
          let listItems = document.querySelectorAll(".goods-item");
          // 复位
          for (let i = 0; i < listItems.length; i++) {
            listItems[i].dataset.type = 0;
          }
        },
        //删除数据信息
        remove(idx) {
          this.restSlide(); // 复位
          this.goodsList.splice(idx, 1); // 删除数组lists中一个数据
        },
        //
      },
    })
  </script>
</body>

</html>