支付宝/钉钉小程序 mini-ddui swipe-action 解决滑开时组件内可点击的问题

前言

在使用钉钉自己的mini-ddui时,发现swipe-action组件有个坑,swipe-action左右滑动时无法禁止组件本身被点击,导致滑动时如果组件内元素可以点击容易误操作用户体验很差.

在查看了官方文档后,发现并没有给出什么解决办法,so…只能把官方库的组件改了一下

思路

1.给swipe-action组件增加一个onSwipeEnd的钩子
2.当手放开时判断当前swipe状态,如果在swipe,则点击触发无效

swipe-action 修改版 完整组件代码

东西不是特别多不想放到git上了

swipe-action.acss

.am-swipe {
  position: relative;
  overflow: hidden;
}

.am-swipe-content {
  position: relative;
  transition: all 250ms;
}

.am-swipe-right {
  position: absolute;
  top: 0;
  bottom: 0;
  display: flex;
  right: 0;
}

.am-swipe-btn {
  padding: 0 14px;
  justify-content: center;
  align-items: center;
  display: flex;
  color: #fff;
}

.am-swipe-btn-delete {
  background-color: rgb(244, 51, 60);
}

.am-swipe-btn-edit {
  background-color: #108ee9;
} //=== v2 ===
.am-swipe-movable {}

.am-swipe-area {
  position: absolute;
}

.am-swipe-area .am-swipe-content {
  transition: none;
}

.am-swiping .am-swipe-content {
  transition: none;
}

swipe-action.axml

<view a:if={{!useV2}} class="am-swipe {{swiping ? 'am-swiping' : ''}} {{className}}">
  <block a:if="{{right.length > 0}}">
    <view class="am-swipe-right am-swipe-right-{{$id}}">
      <block a:for="{{right}}">
        <view
          class="am-swipe-btn am-swipe-btn-{{item.type}}"
          onTap="onItemClick"
          data-index="{{index}}"
          key="swipe-item-{{index}}"
        >
          {{item.text}}
        </view>
      </block>
    </view>
  </block>
  <view
    class="am-swipe-content"
    onTap="onSwipeTap"
    onTouchStart="onSwipeStart"
    onTouchEnd="onSwipeEnd"
    catchTouchMove="onSwipeMove"
    data-type="content"
    style="-webkit-transform: translateX({{leftPos}}px); transform: translateX({{leftPos}}px);"
  >
    <slot />
  </view>
</view>
<view
  class="am-swipe {{className}}"
  style="width: {{viewWidth}}px; height: {{height}}px;"
  key="{{key}}"
  a:else
>
  <movable-area
    class="am-swipe-movable-area"
    style="width: {{ viewWidth + actionWidth}}px;height: {{height}}px;left: -{{actionWidth}}px;"
  >
    <block a:if="{{right.length > 0}}">
      <view class="am-swipe-right am-swipe-right-{{$id}}" style="right: 0;">
        <block a:for="{{right}}">
          <view
            class="am-swipe-btn am-swipe-btn-{{item.type}}"
            onTap="onItemClick"
            data-index="{{index}}"
            key="swipe-item-{{index}}"
          >
            {{item.text}}
          </view>
        </block>
      </view>
    </block>
    <movable-view
      class="am-swipe-content"
      onTap="onSwipeTap"
      onTouchStart="onSwipeStart"
      direction="horizontal"
      data-type="content"
      style="width: {{viewWidth}}px;height:{{height}}px; transition: {{transitionVal}};"
      x="{{x}}"
      onChange="onChange"
      onChangeEnd="onChangeEnd"
    >
      <slot />
    </movable-view>
  </movable-area>
</view>

swipe-action.js

const { windowWidth } = dd.getSystemInfoSync();
const isV2 = dd.canIUse('movable-view.onTouchStart');

Component({
  data: {
    leftPos: 0,
    swiping: false,
    holdSwipe: true,
    viewWidth: windowWidth,
    x: 0,
    actionWidth: 0,
    transitionVal: 'none',
  },
  props: {
    className: '',
    right: [],
    restore: false,
    index: null,
    height: 52,
    enableNew: false,
  },
  didMount() {
    const { enableNew } = this.props;
    const useV2 = isV2 && enableNew;
    this.btnWidth = 0;
    this.setData({
      useV2,
    });
    this.setBtnWidth();
    if (useV2) {
      setTimeout(() => {
        this.setData({
          transitionVal: 'transform 100ms',
        });
      }, 500);
    }
  },
  didUpdate(_prevProps, prevData) {
    const { restore } = this.props;
    const { holdSwipe, useV2 } = this.data;
    if ((restore === true && _prevProps.restore !== restore) ||
      (prevData.holdSwipe === true && holdSwipe === false)) {
      this.setData({
        leftPos: 0,
        swiping: false,
        x: this.btnWidth, // V2
      });
    }

    if (!useV2) {
      this.setBtnWidth();
    }
  },
  methods: {
    setBtnWidth() {
      my.createSelectorQuery()
        .select(`.am-swipe-right-${this.$id}`)
        .boundingClientRect()
        .exec((ret) => {
          this.btnWidth = (ret && ret[0] && ret[0].width) || 0;
          if (isV2 && this.props.enableNew) {
            this.setData({
              actionWidth: this.btnWidth,
              x: this.btnWidth,
            });
          }
        });
    },
    onSwipeTap() {
      if (!this.data.swiping) {
        this.setData({
          leftPos: 0,
          swiping: false,
          x: 0,
        });
      }
    },
    onSwipeStart(e) {
      this.touchObject = {
        startX: e.touches[0].pageX,
        startY: e.touches[0].pageY,
      };
      const { index, onSwipeStart } = this.props;
      if (onSwipeStart) {
        onSwipeStart({ index });
      }
    },
    onSwipeMove(e) {
      const { touchObject } = this;
      const touchePoint = e.touches[0];
      const { leftPos } = this.data;

      touchObject.endX = touchePoint.pageX;

      // 首次触发时,计算滑动角度
      if (touchObject.direction === undefined) {
        let direction = 0;

        const xDist = touchObject.startX - touchePoint.pageX || 0;
        const yDist = touchObject.startY - touchePoint.pageY || 0;

        const r = Math.atan2(yDist, xDist);
        let swipeAngle = Math.round((r * 180) / Math.PI);

        if (swipeAngle < 0) {
          swipeAngle = 360 - Math.abs(swipeAngle);
        }
        if (swipeAngle <= 45 && swipeAngle >= 0) {
          direction = 1;
        }
        if (swipeAngle <= 360 && swipeAngle >= 315) {
          direction = 1;
        }
        if (swipeAngle >= 135 && swipeAngle <= 225) {
          direction = -1;
        }

        touchObject.direction = direction;
      }

      // 通过角度判断是左右方向
      if (touchObject.direction !== 0) {
        let newLeftPos = leftPos;
        // 滑动距离
        const distance = touchObject.endX - touchObject.startX;
        // 左划
        if (distance < 0) {
          newLeftPos = Math.max(distance, -this.btnWidth);
          // 右划
        } else {
          newLeftPos = 0;
        }
        if (Math.abs(distance) > 10) {
          this.setData({
            leftPos: newLeftPos,
            swiping: distance < 0,
          });
        }
      }
    },
    onSwipeEnd(e) {
      const { touchObject } = this;
      if (touchObject.direction !== 0) {
        const touchePoint = e.changedTouches[0];

        touchObject.endX = touchePoint.pageX;

        const { leftPos } = this.data;
        const distance = touchObject.endX - touchObject.startX;
        let newLeftPos = leftPos;
        if (distance < 0) {
          if (Math.abs(distance + leftPos) > this.btnWidth * 0.7) {
            newLeftPos = (-this.btnWidth);
          } else {
            newLeftPos = 0;
          }
        }
        this.setData({
          leftPos: newLeftPos,
          swiping: false,
        });

        const { index, onSwipeEnd } = this.props;
        if (onSwipeEnd) {
          onSwipeEnd({index, newLeftPos });
        }
      }
    },
    onChange() {
      if (!this.data.swiping) {
        this.setData({
          swiping: true,
        });
      }
    },
    onChangeEnd(e) {
      const { actionWidth } = this.data;
      const { x } = e.detail;
      this.setData({
        x: x < actionWidth / 2 ? -1 : actionWidth - 1,
        swiping: false,
      }, () => {
        this.setData({
          x: this.data.x === -1 ? 0 : actionWidth,
        });
      });
    },
    done() {
      this.setData({
        holdSwipe: true,
      }, () => {
        this.setData({
          holdSwipe: false,
        });
      });
    },
    onItemClick(e) {
      const { onRightItemClick } = this.props;
      const { holdSwipe } = this.data;
      if (onRightItemClick) {
        const { index } = e.target.dataset;
        onRightItemClick({
          index,
          extra: this.props.extra,
          detail: this.props.right[index],
          done: this.done.bind(this),
        });
      }

      if (!this.data.swiping && holdSwipe === false) {
        setTimeout(() => {
          this.setData({
            leftPos: 0,
            swiping: false,
            x: 0,
          });
        }, 300);
      }
    },
  },
});

组件与scroll-view配合的使用

<!--这里是伪代码,非相关代码请自行根据官方文档补充,这里只展示相关代码-->
<swipe-action  className="card-swipe-item"   onSwipeStart="onSwipeStart" onSwipeEnd="onSwipeEnd" extra="{{item}}">
	 <view  onTap="goInfo"></view>
	 </swipe-action>
...
Page({
	data:{
		isSwiping:false
	},
	 onSwipeStart(e) {
	    console.log('swipe start', e)
	  },
	  onSwipeEnd(e) {
	    this.setData({
	      isSwiping: e.newLeftPos === 0 ? false : true
	    });
	    console.log('swipe end', e.index, e.newLeftPos)
	  },
	   goInfo(e) {
	    if (this.data.isSwiping) {
	      return false;
	    }
	
	    //do something....
	  },
})

后记

onSwipeEnd这个钩子还是很有用的,可以结合scroll-view控制侧滑开后不能滚动等等…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值