评论

用movable组件写出简短的拖放/拖拽/拖动 排序,含详细的讲解【拎包哥】

组件基础学习

「前言」

这应该是社区目前(2020/12/8)最简短的拖拽排序教程之一,助你快速上手哦。

拖放排序是前端中可以和订单规格选择等等比较的,知识点最密集的基础之一。

如果你有html的基础知识,你会发现微信小程序其实是集成度非常高的框架,和vue,react等响应式前端框架没有本质的区别,甚至集成度还更高。

所以在这里好好利用小程序自身的组件及其属性,就能快速写出简短的拖拽排序。

注:感谢@烟斗 留言帮助!

========================效果图=============================

微信小程序

========================HTML篇=============================

  • 只使用小程序提供的movable组件即可。它简化了拖放排序的条件 ,让我们只需要控制y值就可以确定组件的位置。
  • 拖放中的放动作有手指离开的动作,而movable组件没有这个属性,所以引用了touchend。
  • 注意z-index判断层级
<movable-area class='ctr'>
<block wx:for='{{arr}}' wx:key='x'>
<movable-view bindchange='change' bindtouchend='end' y='{{item.y}}' class='item' direction='vertical' style="z-index:{{index==dragId?2:1}}">
{{item.name}}
</movable-view>
</block>
</movable-area>

(ps. 由于微信社区难以理解的bug,这里的代码不能放在代码片段里)

========================CSS篇=============================

  • 在这个CSS我只有item的height用到了px,因为y值的像素单位是px。
  • 在css尽量不要增加额外的height属性,否则这个组件就不精准了。
.ctr{
  width400rpx;
  height800rpx;
  border1rpx solid black;
}
.item{
  width400rpx;
  height50px;
  /* 与后来确定y值的的 i * 50对应 */
  border-bottom1rpx solid black;
  box-sizing: border-box;
  background:white;
    /* 让边框内嵌,否则会随着1rpx的叠加而让y值变得不精准 */
  }


=========================JS篇==============================

主要步骤

  1. 用y值来确定拖放动作中放的位置
  2. 将源item放置在目标item前(这也是排序的本质)

注意

  • 拖拽的数组arr一开始就放在onLoad方法而不是data里,否则会因为data的提前渲染而产生缓慢的位移。
  • movable-view一开始是重叠的,所以要根据下标来确定每个item的y值。
  • bindchange对应的是拖行为,我们只需要在这个方法里获取我们在拖行为时产生的y值。
  • 拖动行为不会触发bindtap

那么在touchend的时候就可以获得bindchange最后一个y值,并借此确定放行为的对应的下标。

Page({
  onLoad() {
    var arr = [
      {name: 'Mike'},
      {name: 'Paul'},
      {name: 'Peter'},
      {name: 'Andy'},
      {name: 'Larry'}
    ]
    for (var i in arr) {
      arr[i].y = i * 50
    }
    // movable-view的y值单位是px
    console.log(arr)
    this.setData({
      arr
    })
  },


  tap(){
    // console.log('在拖拽时是否出发点击行为?')  // 在拖拽时不触发点击行为
  },


  change(e) {
    this.y = e.detail.y
    var dragId = e.currentTarget.id
    // 默认item id,wx-for 分配给每个item的index,我在html里id={{index}},即用id变量记录分配后的index
    this.setData({
      dragId  
    })
  },


  end(e) {
    console.log('im 触摸结束')
    console.log(this.y)  // this.y item下边线到movearea顶端的距离
    var arr = this.data.arr
    var id = e.currentTarget.id  
    
    var currentId = this.y / 50  // 移动时不断计算的id
    if (id > currentId) {
      var transferId = Math.ceil(currentId)
    } else {
      var transferId = Math.floor(currentId)
    }
    var save = arr[id]  // 保存初始id


    arr.splice(id,1)
    arr.splice(transferId,0,save)  // 精华


    for (var i in arr) {
      arr[i].y = i * 50
    }
    this.setData({
      arr
    })
  }


})


------------------------------------------进阶篇vue-cli-------------------------------------------

vue-cli4

========================HTML篇=============================

挖坑,在研究vue脚手架vue-cli4的拖拽排序,未完待续。

最后一次编辑于  2021-01-17  
点赞 2
收藏
评论

7 个评论

  • 猛男陈阔
    猛男陈阔
    2020-12-22

    点个赞

    2020-12-22
    赞同 1
    回复
  • 桜⃰吹⃰雪⃰
    桜⃰吹⃰雪⃰
    2023-03-06

    如果是scroll-view布局呢,有没有办法手指滑动到顶部时scroll-view向上滚动,或者移动到底部时向下滚动

    2023-03-06
    赞同 1
    回复
  • 理想
    理想
    2021-11-23

    我赵上面代码,在 change(e) 和 end(e) 函数里获取不到 id,id 是空的,执行会报错,最后修改代码如下:

    wxml:

    1. wx:key='x' 改为 wx:key='id';
    2. <movable-view> 上加: data-index="{{id}}" index="{{id}},如:<movable-view data-index="{{id}}" index="{{id}}></movable-view>

    js:

    change(e) 函数下的 let dragId = e.currentTarget.id 改为 let dragId = e.currentTarget.dataset.id

    end(e) 函数下 id = e.currentTarget.id 改为 id = e.currentTarget.dataset.id

    2021-11-23
    赞同
    回复
  • 执
    2021-03-19

    如果我只是点击了 但是没有报错 这个时候 你得在里面判断当前点击的元素 是否有this.y这个属性 没有再去默认设置一个


    2021-03-19
    赞同
    回复
  • 一点客服
    一点客服
    2021-03-17

    用了一个比你这个更简单的方法

    2021-03-17
    赞同
    回复 1
    • 理想
      理想
      2021-11-23
      方便发一教程学习一下吗?
      2021-11-23
      回复
  • 烟斗
    烟斗
    2020-12-14

    1、应该要处理一下当前拖动的 movable-view 的 z-index,保证它层级高于其他

    touchStart时记录一下当前拖动的id,如dragID,再z-index:{{index == dragID ? 2 : 1}}

    2、判断拖动的代码建议优化

    if (id < currentId) {
          var transferId = Math.ceil(currentId)
        } else {
          var transferId = Math.floor(currentId)
        }
    

    改为

    if (id > currentId) {
          var transferId = Math.ceil(currentId)
        } else {
          var transferId = Math.floor(currentId)
        }
    

    3、拖动不应该是源和目标对换,而应该是拖动到目标的前面或后面

    var dragItem = arr.splice(id, 1);
        arr.splice(transferId, 0, dragItem[0]);
    
    2020-12-14
    赞同
    回复 2
    • 拎包哥
      拎包哥
      2020-12-16
      感谢评论,我有空回来好好修改!
      2020-12-16
      回复
    • 拎包哥
      拎包哥
      2021-01-17
      感谢,我修改好了!
      2021-01-17
      回复
  • 拎包哥
    拎包哥
    2020-12-08

    有问题或改进建议请留言哦。

    2020-12-08
    赞同
    回复 1
    • 张先绅
      张先绅
      2021-01-22
      要是每个元素的高度不一样啦
      2021-01-22
      回复
登录 后发表内容