小程序-购物车商品左滑删除的最佳方案

1,610 阅读4分钟

前言

传统的小程序左滑做法是利用touchmove来实时的更新滑动的距离,这让小程序在性能上大打折扣,而且还时常出现卡顿不流畅的现象,小程序新出的movable-area + movable-view 可以完美的解决这个问题。 话不多说,直接上地址:源码地址

动图效果

利用setData的实现

wxml 文件


<view class='item'>
  <view bind:touchstart="b_touchS" bind:touchmove="b_touchM">
    <view class='carGoods' catch:touchmove="{{c_touchM}}" bind:touchend="c_touchE" data-index="{{good.id}}" style="{{good.txtStyle}}">
      <view class='left'>
        <view class='greenCheck {{good.checked?"active":""}}'><image src='/images/select.png' mode='aspectFit'></image></view>
        <view class="goodsBg">
          <image src='{{good.sku.image}}' mode='widthFix'></image>
        </view>
        <view class='product'>  
          <view class='title'>{{good.sku.name}}</view>
          <view class='format'>{{good.sku.tag}}</view>
          <view class='down'>
            <view class='price'>¥{{good.sku.price}}</view>
            <view class="num_counter">
              <image class="reduce" data-id="{{good.id}}" data-num="{{good.nums}}" src="/images/reduce.png" mode="widthFix"></image>
              <view class="num">{{good.nums}}</view>
              <image class="add" data-id="{{good.id}}" data-num="{{good.nums}}" src="/images/add.png" mode="widthFix"></image>
            </view>
          </view>
        </view>
      </view>
    </view>  
  </view>
  <view class="inner">删除</view>
</view>

js 文件

// components/good/good.js
var INITX = 30;//触发条件的X移动距离
var MAXY = 30;//触发条件
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    good: Object
  },
  created: function () {
    this.initEleWidth();
  },
  /**
   * 组件的初始数据
   */
  data: {
    delBtnWidth: 160,
    firstX: -1,
    c_touchM: ''
  },

  /**
   * 组件的方法列表
   */
  methods: {

    b_touchS: function (e) {
      if (e.touches.length == 1) {
        this.setData({
          //设置触摸起始点水平方向位置
          startX: e.touches[0].clientX,
          // 设置触摸起始点垂直位置
          startY: e.touches[0].clientY,
          //初始化x计算起点
          firstX: -1,
          c_touchM: ''
        });
      }
    },
    b_touchM: function (e) {
      if (e.touches.length == 1) {
        // 如果当商品没有滑动删除按钮时,向右滑动失效。
        const { txtStyle } = this.data.good

        //手指移动时水平方向位置
        var moveX = e.touches[0].clientX;
        // 初始化firstX已经被拉开时
        if (this.data.firstX < 0) {
          var moveY = e.touches[0].clientY
          // 计算开始和结束的两个点的X和Y的差值
          var triggerX = moveX - this.data.startX
          var triggerY = moveY - this.data.startY
          console.log(triggerX)
          if (Math.abs(triggerX) >= INITX && Math.abs(triggerY) < MAXY) {
            if (txtStyle == '' || txtStyle == 'left:0px') {
              if (triggerX > 0) {
                return false
              }
            }
            this.setData({
              firstX: moveX,
              c_touchM: 'c_touchM'
            })
          } else {
            return
          }
        }

        //手指起始点位置与移动期间的差值
      }
    },

    c_touchM: function (e) {
      if (e.touches.length == 1) {
        //手指移动时水平方向位置
        var moveX = e.touches[0].clientX;
        //手指触发点位置与移动期间的差值
        var disX = this.data.firstX - moveX
        var delBtnWidth = this.data.delBtnWidth;
        var txtStyle = "";
        if (disX > 0) {
          txtStyle = `left: ${-disX}px`;
        } else {
          txtStyle = `left: ${-delBtnWidth - disX}px`;
        }
        if (disX < 0 && disX <= -delBtnWidth) {//如果移动距离小于等于0,文本层位置不变
          txtStyle = "left:0px";
        } else if (disX > 0 && disX >= delBtnWidth) {//移动距离大于0,文本层left值等于手指移动距离
          //控制手指移动距离最大值为删除按钮的宽度
          txtStyle = "left:-" + delBtnWidth + "px";

        }

        //获取手指触摸的是哪一项
        var index = e.currentTarget.dataset.index;
        var good = this.data.good;
        if (index) {
          good.txtStyle = txtStyle;
          //更新列表的状态
          this.setData({
            good: good
          });
        }
      }
    },

    c_touchE: function (e) {
      if (e.changedTouches.length == 1) {
        //手指移动结束后水平位置
        var endX = e.changedTouches[0].clientX;
        //触摸开始与结束,手指移动的距离
        var disX = this.data.startX - endX;
        var delBtnWidth = this.data.delBtnWidth;
        // console.log(disX, delBtnWidth)
        //如果距离小于删除按钮的1/2,不显示删除按钮
        var txtStyle = disX / 2 > delBtnWidth / 2 ? "left:-" + delBtnWidth + "px" : "left:0px";
        //获取手指触摸的是哪一项
        var index = e.target.dataset.index;
        var good = this.data.good;
        if (index) {
          good.txtStyle = txtStyle;
          //更新列表的状态
          this.setData({
            good: good
          });
        }
      }
    },

    //获取元素自适应后的实际宽度
    getEleWidth: function (w) {
      var real = 0;
      try {
        var res = wx.getSystemInfoSync().windowWidth;
        //以宽度750px设计稿做宽度的自适应
        var scale = (750 / 2) / (w / 2); 
        real = Math.floor(res / scale);
        return real;
      } catch (e) {
        return false;
        // Do something when catch error
      }
    },
    initEleWidth: function () {
      var delBtnWidth = this.getEleWidth(this.data.delBtnWidth);
      this.setData({
        delBtnWidth: delBtnWidth
      });
    },
  }
})

利用movable-area + movable-view的实现

wxml 文件

<view class='item'>
  <movable-area>
    <movable-view class="carGoods" out-of-bounds="true" direction="horizontal" x="{{good.xmove}}"
        data-index="{{good.id}}"
        inertia="true"
        bindtouchstart="handleTouchStart"
        bindtouchend="handleTouchEnd"
      >
      <view class='left'>
        <view class='greenCheck {{good.checked?"active":""}}'><image src='/images/select.png' mode='aspectFit'></image></view>
        <view class="goodsBg">
          <image src='{{good.sku.image}}' mode='widthFix'></image>
        </view>
        <view class='product'>  
          <view class='title'>{{good.sku.name}}</view>
          <view class='format'>{{good.sku.tag}}</view>
          <view class='down'>
            <view class='price'>¥{{good.sku.price}}</view>
            <view class="num_counter">
              <image class="reduce" data-id="{{good.id}}" data-num="{{good.nums}}" src="/images/reduce.png" mode="widthFix"></image>
              <view class="num">{{good.nums}}</view>
              <image class="add" data-id="{{good.id}}" data-num="{{good.nums}}" src="/images/add.png" mode="widthFix"></image>
            </view>
          </view>
        </view>
      </view>
    </movable-view>
  </movable-area>
  <view class="inner">删除</view>
</view>

js 文件

// components/good2/good2.js
var INITX = 30
var MAXY = 30
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    good: Object
  },

  /**
   * 组件的初始数据
   */
  data: {
    
  },

  /**
   * 组件的方法列表
   */
  methods: {
    /**
   * 显示删除按钮
   */
    showDeleteButton: function (e) {
      console.log(e)
      let index = e.currentTarget.dataset.index
      this.setXmove(index, -80)
    },

    /**
   * 隐藏删除按钮
   */
    hideDeleteButton: function (e) {
      let index = e.currentTarget.dataset.index
      this.setXmove(index, 0)
    },

    /**
   * 设置movable-view位移
   */
    setXmove: function (index, xmove) {
      let good = this.data.good
      console.log(index)
      if (index) {
        good.xmove = xmove;
        //更新列表的状态
        this.setData({
          good: good
        });
      }
    },
    /**
     * 设置movavle-view的开始位置
     */
    handleTouchStart(e) {
      this.startX = e.touches[0].pageX
      this.startY = e.touches[0].pageY
    },
    /**
     * 设置movavle-view的结束位置
     */
    handleTouchEnd(e) {
      // console.log(e)
      // console.log(e.changedTouches[0].pageX)
      const endX = e.changedTouches[0].pageX
      const endY = e.changedTouches[0].pageY
      const triggerX = endX - this.startX
      const triggerY = endY - this.startY
      // console.log(this.startX)
      if (Math.abs(triggerX) > INITX && Math.abs(triggerY) < MAXY) {
        console.log(triggerY)
        if (endX < this.startX && endX - this.startX <= -40) {
          this.showDeleteButton(e)
        } else if (endX > this.startX && endX - this.startX < 40) {
          this.showDeleteButton(e)
        } else {
          this.hideDeleteButton(e)
        }
      }
    },
  }
})

结尾

传统的左滑删除的用户体验确实不好,小程序官方出的这movable-view确实是好用,不仅不卡,还有动画效果,如果有需要的可以试试这个方案。

参考

微信小程序movable-view实现左滑删除功能