微信小程序:列表拖拽排序的简单实现

index.wxml:
<!--pages/index/index.wxml-->

<view class="index">

  <!-- 数据展示区 -->
  <scroll-view
    class="scroll-list" 
    scroll-y 
    style="height: {{windowHeight - bottomHeight}}px"
    bindscroll="bindscroll">
    <view class="scroll-item" wx:for="{{optionsList}}" wx:key="index">
      <view>{{item.time}}</view>
    </view>
  </scroll-view>

  <!-- 页面可拖拽区域 -->
  <view
    wx:if='{{editting}}'
    class="touchView" 
    catchtouchstart="scrollTouchStart" 
    catchtouchmove="scrollTouchMove" 
    catchtouchend="scrollTouchEnd"
    style="height: {{windowHeight - bottomHeight}}px">
	</view>

  <!-- 页面拖拽处理区域 -->
  <movable-area
    wx:if='{{editting}}'
    class="moveable_row"
    style="height: {{windowHeight - bottomHeight}}px">
		<movable-view  
      class="moveable_item {{moveData ? 'show' : 'hide'}}" 
      style="height: {{scrollItemHeight}}px;" 
      direction="vertical" 
      x="{{moveData.x}}" 
      y="{{moveData.y}}" 
      inertia="false" 
      damping="9999"
      friction="9999"
    >
      <view class="scroll-item moving" hidden='{{!moveData}}'>
        <view>{{selectItem.time}}</view>
      </view>
		</movable-view>
	</movable-area>

  <!-- 底部栏 -->
  <view class="index-bottom">
    <button type="primary" wx:if='{{!editting}}' bindtap="changeEditing">编辑</button>
    <button type="warn" wx:else bindtap="changeEditing">取消</button>
  </view>

</view>
index.js:
// pages/index/index.js
Page({

  data: {
    optionsList: [],      // 数据源
    windowHeight: 0,      // 屏幕高度
    scrollItemHeight: 0,  // 列表单项高度
    bottomHeight: 0,      // 底部按钮高度
    selectItem: {},       // 当前选中元素
    selectIndex: 0,       // 当前选中索引
    editting: false,      // 是否是“编辑”状态
    moveData: null,       // 列表项移动时记录移动位置
    scrollTop: 0          // scroll-view距离顶部距离
  },

  onLoad: function () {
    this.init()
  },

  // 初始化页面数据
  init() {
    this.setData({
      optionsList: [{time: '12:00'}, {time: '13:00'}, {time: '14:00'}, {time: '15:00'}, {time: '16:00'}, {time: '17:00'}, {time: '18:00'}, {time: '19:00'}, {time: '20:00'}, {time: '21:00'}, {time: '22:00'}, {time: '23:00'}, {time: '24:00'}, {time: '25:00'}, {time: '26:00'}, {time: '27:00'}, {time: '28:00'}, {time: '29:00'}, {time: '30:00'}, {time: '31:00'}, {time: '32:00'}, {time: '33:00'}, {time: '34:00'}, {time: '35:00'}],
      windowHeight: wx.getSystemInfoSync().windowHeight
    }, () => {
      wx.createSelectorQuery().select('.index-bottom').boundingClientRect(res => {
        this.setData({
          bottomHeight: res.height
        })
      }).exec()
      wx.createSelectorQuery().select('.scroll-item').boundingClientRect(res => {
        this.setData({
          scrollItemHeight: res.height
        })
      }).exec()
    });
  },

  // 开始拖拽
  scrollTouchStart(event) {
    const { scrollItemHeight } = this.data
    const firstTouchPosition = {
      x: event.changedTouches[0].pageX,
      y: event.changedTouches[0].pageY,
    }
    const { data, index } = this.getPositionDomByXY(firstTouchPosition);
    this.setData({
      moveData:{
        x: 0,
        y: firstTouchPosition.y - scrollItemHeight / 2
      },
      selectItem: data,
      selectIndex: index
    })

  },

  // 拖拽ing...
  scrollTouchMove(event) {
    const { scrollItemHeight } = this.data
    this.setData({
      moveData:{
        x: 0,
        y: event.changedTouches[0].pageY - scrollItemHeight / 2
      },
    })

  },

  // 拖拽结束
  scrollTouchEnd:function (event) {
    const { selectIndex, optionsList } = this.data
    const endTouchPosition = {
      x: event.changedTouches[0].pageX,
      y: event.changedTouches[0].pageY,
    }
    const { index } = this.getPositionDomByXY(endTouchPosition)
    // 交换顺序
    const temp = optionsList[selectIndex]
    optionsList[selectIndex] = optionsList[index]
    optionsList[index] = temp
    this.setData({
      optionsList,
      moveData: null
    })
  },

  // 根据(x,y)坐标轴获取页面元素
  getPositionDomByXY(potions) {
    const { scrollItemHeight, optionsList, scrollTop } = this.data
    const y = potions.y + scrollTop;
    const len = optionsList.length
    for(let i = 0; i < len; i++){
      if(y >= i*scrollItemHeight && y < (i+1)*scrollItemHeight){
        // 返回匹配到的数据项
        return {
          data: optionsList[i],
          index: i
        };
      }
    }
    return y > (len-1)*scrollItemHeight ? {
      // 匹配项位于列表之下
      data: optionsList[len - 1],
      index: len - 1
    } : {
      // 匹配项位于列表之上
      data: optionsList[0],
      index: 0
    }
  },

  // 切换编辑状态
  changeEditing() {
    const { editting } = this.data
    this.setData({ editting: !editting })
  },

  // 监听滚动事件
  bindscroll(e) {
    this.data.scrollTop = e.detail.scrollTop
  }

})
index.wxss:
/* pages/index/index.wxss */
.index {
  position: relative;
}

/* 数据展示区 */
.scroll-list{
  width: 100%;
}
.scroll-item {
  width: 100%;
  padding: 10rpx 20rpx;
  box-sizing: border-box;
  border: 1px solid #999;
}
.scroll-item.moving {
  border: 1px dashed blue;
}

/* 页面可拖拽区域 */
.touchView {
  width: 100%;
  position: absolute;
  left: 0;
  top: 0;
  z-index: 1;
}

/* 页面拖拽处理区域 */
.moveable_row {
  width: 100%;
  position: absolute;
  left: 0;
  top: 0;
}
.moveable_item {
  width: 100%;
}
.moveable_item.show {
  background: #fff;
}
.moveable_item.hide {
  background: transparent;
}


/* 底部栏 */
.index-bottom {
  width: 100%;
  display: flex;
  justify-content: flex-end;
  padding: 10rpx 20rpx;
  box-sizing: border-box;
}

例子展示:
在这里插入图片描述

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值