微信小程序左滑删除功能完整实现

645 阅读5分钟

微信小程序左滑删除功能完整实现

功能特点

  1. 流畅的左滑删除交互

    • 向左滑动显示删除按钮
    • 向右滑动关闭删除按钮
    • 同时只能打开一个删除按钮
  2. 智能手势识别

    • 区分水平滑动和垂直滚动
    • 垂直滚动时自动关闭删除按钮
  3. 完整的功能集

    • 添加新项目
    • 删除单个项目
    • 清空整个列表
    • 空列表状态提示
  4. 美观的UI设计

    • 渐变背景和按钮
    • 圆形头像
    • 平滑的过渡动画
    • 响应式布局
  5. 用户友好交互

    • 删除确认动画
    • 清空列表确认弹窗
    • 操作成功提示

使用说明

  1. 在微信开发者工具中创建新页面 pages/swipe-delete
  2. 将上述文件放入对应目录
  3. 在 app.json 的 pages 数组中添加页面路径
  4. 准备一张空列表提示图片(可替换 /images/empty.png

文件结构

pages/
  └── swipe-delete/
        ├── swipe-delete.js     // 页面逻辑
        ├── swipe-delete.json   // 页面配置
        ├── swipe-delete.wxml   // 页面结构
        └── swipe-delete.wxss   // 页面样式

完整代码实现

1. swipe-delete.wxml

<!--pages/swipe-delete/swipe-delete.wxml-->
<view class="container">
  <!-- 头部标题 -->
  <view class="header">
    <view class="title">消息列表</view>
    <view class="subtitle">左滑项目可显示删除按钮</view>
  </view>
  
  <!-- 列表容器 -->
  <scroll-view class="list-container" scroll-y>
    <block wx:for="{{list}}" wx:key="id">
      <view class="list-item">
        <!-- 内容区域 -->
        <view 
          class="item-content {{activeIndex === index ? 'active' : ''}}"
          bindtouchstart="handleTouchStart"
          bindtouchmove="handleTouchMove"
          bindtouchend="handleTouchEnd"
          data-index="{{index}}"
        >
          <view class="avatar">{{item.name.charAt(0)}}</view>
          <view class="item-info">
            <view class="item-title">{{item.name}}</view>
            <view class="item-subtitle">{{item.message}}</view>
          </view>
          <view class="item-time">{{item.time}}</view>
        </view>
        
        <!-- 删除按钮 -->
        <view 
          class="delete-btn {{activeIndex === index ? 'active' : ''}}"
          bindtap="handleDelete"
          data-index="{{index}}"
        >
          <text class="icon">删除</text>
        </view>
      </view>
    </block>
    
    <!-- 空列表提示 -->
    <view wx:if="{{list.length === 0}}" class="empty-tip">
      <image src="/images/empty.png" class="empty-image"></image>
      <text class="empty-text">列表为空,请添加项目</text>
    </view>
  </scroll-view>
  
  <!-- 底部操作按钮 -->
  <view class="footer">
    <button class="btn add-btn" bindtap="handleAddItem">
      <text class="icon">+</text> 添加新项目
    </button>
    <button class="btn clear-btn" bindtap="handleClearAll">
      <text class="icon">×</text> 清空列表
    </button>
  </view>
</view>

2. swipe-delete.wxss

/* pages/swipe-delete/swipe-delete.wxss */
.container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background-color: #f5f7fa;
}

.header {
  background: linear-gradient(135deg, #4361ee, #3a0ca3);
  color: white;
  padding: 25rpx;
  padding-top: 60rpx;
  padding-bottom: 30rpx;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}

.title {
  font-size: 42rpx;
  font-weight: bold;
  margin-bottom: 10rpx;
}

.subtitle {
  font-size: 26rpx;
  opacity: 0.9;
}

.list-container {
  flex: 1;
  padding: 20rpx;
  background-color: #f5f7fa;
}

.list-item {
  position: relative;
  height: 160rpx;
  background: white;
  border-radius: 16rpx;
  margin-bottom: 20rpx;
  overflow: hidden;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}

.item-content {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: white;
  padding: 20rpx 30rpx;
  display: flex;
  align-items: center;
  transition: transform 0.3s ease;
  z-index: 2;
}

.item-content.active {
  transform: translateX(-160rpx);
}

.avatar {
  width: 100rpx;
  height: 100rpx;
  border-radius: 50%;
  background: linear-gradient(45deg, #4361ee, #4cc9f0);
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-weight: bold;
  font-size: 42rpx;
  margin-right: 25rpx;
}

.item-info {
  flex: 1;
}

.item-title {
  font-weight: 600;
  font-size: 34rpx;
  margin-bottom: 10rpx;
  color: #333;
}

.item-subtitle {
  font-size: 28rpx;
  color: #777;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 400rpx;
}

.item-time {
  font-size: 26rpx;
  color: #999;
  margin-right: 10rpx;
}

.delete-btn {
  position: absolute;
  top: 0;
  right: -160rpx;
  width: 160rpx;
  height: 100%;
  background: #e63946;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  transition: all 0.3s ease;
  z-index: 1;
}

.delete-btn.active {
  right: 0;
}

.delete-btn .icon {
  font-size: 32rpx;
}

.footer {
  display: flex;
  padding: 20rpx;
  background: white;
  border-top: 1rpx solid #eee;
}

.btn {
  flex: 1;
  height: 90rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 45rpx;
  font-size: 32rpx;
  font-weight: 500;
  margin: 0 15rpx;
  transition: all 0.2s;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}

.btn:active {
  transform: translateY(4rpx);
  box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1);
}

.add-btn {
  background: linear-gradient(135deg, #2ecc71, #27ae60);
  color: white;
}

.clear-btn {
  background: linear-gradient(135deg, #e74c3c, #c0392b);
  color: white;
}

.empty-tip {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 100rpx 0;
}

.empty-image {
  width: 300rpx;
  height: 300rpx;
  margin-bottom: 40rpx;
  opacity: 0.6;
}

.empty-text {
  font-size: 32rpx;
  color: #999;
}

3. swipe-delete.js

// pages/swipe-delete/swipe-delete.js
Page({
  data: {
    list: [
      { id: 1, name: "张三", message: "拒绝加班!!!", time: "10:30" },
      { id: 2, name: "李四", message: "拒绝996!!!", time: "09:15" },
      { id: 3, name: "王五", message: "拒绝内卷!!!", time: "昨天" },
      { id: 4, name: "赵六", message: "拒绝搅乱市场价格!!!", time: "昨天" },
      { id: 5, name: "钱七", message: "拒绝拖欠工资!!!", time: "前天" }
    ],
    activeIndex: -1, // 当前激活的列表项索引
    startX: 0,       // 触摸开始位置
    startY: 0,       // 触摸开始位置(Y轴)
    isMoving: false  // 是否正在滑动
  },

  // 处理触摸开始事件
  handleTouchStart(e) {
    const index = e.currentTarget.dataset.index;
    const touch = e.touches[0];
    
    // 记录触摸起始位置
    this.setData({
      startX: touch.clientX,
      startY: touch.clientY,
      isMoving: false
    });
    
    // 关闭之前打开的项目
    if (this.data.activeIndex !== index) {
      this.setData({ activeIndex: -1 });
    }
  },

  // 处理触摸移动事件
  handleTouchMove(e) {
    const index = e.currentTarget.dataset.index;
    const touch = e.touches[0];
    
    // 计算滑动距离
    const deltaX = touch.clientX - this.data.startX;
    const deltaY = touch.clientY - this.data.startY;
    
    // 如果Y轴滑动距离大于X轴,则判定为垂直滚动
    if (Math.abs(deltaY) > Math.abs(deltaX)) {
      this.setData({ isMoving: true });
      return;
    }
    
    // 标记为水平滑动
    this.setData({ isMoving: false });
    
    // 向左滑动超过阈值(50px)时激活当前项
    if (deltaX < -50) {
      this.setData({ activeIndex: index });
    }
    
    // 向右滑动关闭当前项
    if (deltaX > 20 && this.data.activeIndex === index) {
      this.setData({ activeIndex: -1 });
    }
  },

  // 处理触摸结束事件
  handleTouchEnd() {
    // 如果是垂直滚动,关闭所有删除按钮
    if (this.data.isMoving) {
      this.setData({ activeIndex: -1 });
    }
  },

  // 删除项目
  handleDelete(e) {
    const index = e.currentTarget.dataset.index;
    const list = this.data.list;
    
    // 显示删除动画
    this.setData({
      activeIndex: index,
      ['list[' + index + '].deleting']: true
    });
    
    // 延迟执行删除操作
    setTimeout(() => {
      list.splice(index, 1);
      this.setData({
        list,
        activeIndex: -1
      });
      
      wx.showToast({
        title: '已删除',
        icon: 'success',
        duration: 1000
      });
    }, 300);
  },

  // 添加新项目
  handleAddItem() {
    const names = ["赵六", "钱七", "孙八", "周九", "吴十"];
    const messages = [
      "做人要有良心!!!",
      "没钱你开什么公司!!!",
      "没钱还要大小周!!!",
      "加班为什么不给加班工资!!!",
      "五险为什么也要拖欠!!!"
    ];
    
    const newItem = {
      id: Date.now(),
      name: names[Math.floor(Math.random() * names.length)],
      message: messages[Math.floor(Math.random() * messages.length)],
      time: "刚刚"
    };
    
    // 添加到列表顶部
    this.setData({
      list: [newItem, ...this.data.list]
    });
    
    wx.showToast({
      title: '已添加',
      icon: 'success',
      duration: 1000
    });
  },

  // 清空列表
  handleClearAll() {
    if (this.data.list.length === 0) return;
    
    wx.showModal({
      title: '确认清空',
      content: '确定要清空所有项目吗?',
      success: (res) => {
        if (res.confirm) {
          this.setData({
            list: [],
            activeIndex: -1
          });
          
          wx.showToast({
            title: '列表已清空',
            icon: 'success',
            duration: 1500
          });
        }
      }
    });
  }
});

4. swipe-delete.json

{
  "usingComponents": {},
  "navigationBarTitleText": "左滑删除功能",
  "navigationBarBackgroundColor": "#4361ee",
  "navigationBarTextStyle": "white"
}