vant3-swipe 源码学习

769 阅读2分钟

首先,需求是在手机端实现一个左右滑动的tab的功能。大概的思路是:

  • 1.判断手指滑动方向
  • 2.然后通过 transform 滚动div。

这里使用的是Vant-ui的swipe组件来实现功能。

    <van-swipe :autoplay="false" indicator-color="white">
      <van-swipe-item v-for="cardItem in cardList" :key="cardItem.id">
        <div>
          卡片内容1
        </div>
        </van-swipe-item>
    </van-swipe>

Vant3 源码位置packages/vant/src/swipe/Swipe.tsx. 用的是jsx语法。注意,这里用了render 方式,就自动忽视模板方式

return () => (
      <div ref={root} class={bem()}>
        <div
          style={trackStyle.value}
          class={bem('track', { vertical: props.vertical })}
          onTouchstart={onTouchStart}
          onTouchmove={onTouchMove}
          onTouchend={onTouchEnd}
          onTouchcancel={onTouchEnd}
        >
          {slots.default?.()}
        </div>
        {renderIndicator()}
      </div>
    );

其中 bem() 来创建类命( van-swipe__track van-swipe-item 等这样的格式)

  • Bem 是块(block)、元素(element)、修饰符(modifier)的简写

-中划线 :仅作为连字符使用,表示某个块或者某个子元素的多单词之间的连接记号。

__ 双下划线:双下划线用来连接块和块的子元素

_ 单下划线:单下划线用来描述一个块或者块的子元素的一种状态

trackStyle 用来实现div的滚动,通过transform滚动div

const trackStyle = computed(() => {
      const style: CSSProperties = {
        transitionDuration: `${state.swiping ? 0 : props.duration}ms`,
        transform: `translate${props.vertical ? 'Y' : 'X'}(${state.offset}px)`,
      };

      if (size.value) {
        const mainAxis = props.vertical ? 'height' : 'width';
        const crossAxis = props.vertical ? 'width' : 'height';
        style[mainAxis] = `${trackSize.value}px`;
        style[crossAxis] = props[crossAxis] ? `${props[crossAxis]}px` : '';
      }
      
      return style;
    });

slots.default 用来以编程方式访问通过插槽分发的内容

下面是滑动相关的

touch / touchEvent 相关(参考mdn)

Touch

Touch 对象表示在触控设备上的触摸点。通常是指手指或者触控笔在触屏设备或者触摸板上的操作。

TouchEvent

touchstart(详情点我)

大白话来说,记录手指开始滑动时,反馈坐标等。

touchend (详情点我)

大白话来说,记录手指停止滑动时,反馈坐标等。

touchmove (详情点我)

大白话来说,记录手指滑动时,反馈坐标等。

``

  import { useTouch } from '../composables/use-touch';
  ...
  // hook的方式引用 touch相关
  const touch = useTouch();
  ...
  

代码有删减

   const onTouchStart = (event: TouchEvent) => {
      ...
      touch.start(event);
      touchStartTime = Date.now();
      console.log(touchStartTime)
      ...
    };

    const onTouchMove = (event: TouchEvent) => {
      // console.log('onTouchMove-->',props)
      if (props.touchable && state.swiping) {
        touch.move(event);
        if (isCorrectDirection.value) {
          preventDefault(event, props.stopPropagation);
          move({ offset: delta.value });
        }
      }
    };

    const onTouchEnd = () => {
      ...
      const duration = Date.now() - touchStartTime;
      const speed = delta.value / duration;
      // 判断是否需要滑动
      const shouldSwipe =
        Math.abs(speed) > 0.25 || Math.abs(delta.value) > size.value / 2;

      if (shouldSwipe && isCorrectDirection.value) {
        const offset = props.vertical
          ? touch.offsetY.value
          : touch.offsetX.value;

        let pace = 0;

        if (props.loop) {
          pace = offset > 0 ? (delta.value > 0 ? -1 : 1) : 0;
        } else {
          pace = -Math[delta.value > 0 ? 'ceil' : 'floor'](
            delta.value / size.value
          );
        }

        move({
          pace,
          emitChange: true,
        });
      } else if (delta.value) {
        move({ pace: 0 });
      }

      state.swiping = false;
      autoplay();
    };

总结一下

记录手指滑动的坐标,判断滑动的方向,通过transform滚动div。