One Concept Notes

To complete this component, you first need to understand several events and their parameters

Touchstart: Triggered when a finger touches the screen, even if a finger is already on the screen.

Touchmove: Triggered continuously when a finger is swiped across the screen. Call the preventDefault() event to prevent scrolling during this event.

Touchend: Triggered when the finger moves away from the screen.

We can get the required data in the callback parameter E of these three events by e.Touches [0](single finger)

  • The horizontal coordinate of the pageX touch point on the page
  • The vertical coordinate of the pageY touch point on the page
  • The horizontal coordinate of the clientX touch point in the browser window
  • The vertical coordinate of the clientY touch point in the browser window
  • ScreenX The horizontal coordinate of a touch point on a screen
  • ScreenY The vertical coordinate of a touch point on the screen
  • Force Indicates the pressure of the touch point
  • Identifier The unique identifier of the touch point
  • RadiusX Horizontal radius of the touch point ellipse
  • RadiusY The vertical radius of the touch point ellipse
  • RotationAngle rotationAngle

Two main points

How to accurately determine the user sliding direction

According to the principle, it is only necessary to compare and judge the coordinates of Touchmove and TouchStart, but there are exceptions. For example, when the user slides left for a long distance and then suddenly slides right for a short distance, the coordinates of Touchmove are still on the left of the coordinates of TouchStart, so it is impossible to accurately judge the sliding direction.

A: In this case, we need A variable to record the amount of displacement preTouchS of the last slide

PreTouchS = TouchMove coordinates – touchStart coordinates

This variable has an initial value of 0. When sliding, you can determine the real-time sliding direction by comparing preTouchS with the current touchS

2. How to prevent excessive sliding

  // Estimate displacement = initial left + touch offset
  let s = touchStartLeft + touchS

  // Adjust the displacement to prevent excessive slip
  if (s < 0) {
    / / left smooth
    s = Math.abs(s) > maxLeftS ? -maxLeftS : s
  } else {
    / / right slide
    s = s > 5 ? 5 : s
  }
Copy the code

Where, touchStartLeft is the current offset left value recorded by each touchStart; maxLeftS is the maximum distance allowed to slide left; in the sliding deletion function, it can be set to the width of the right button group; 5 is the buffer value of the right slide

Iii Complete Code

import React, { useCallback, useEffect, useRef } from 'react'

type baseFnType = (. args:any) = > void | unknown

type RightOptionsType = {
  text: string
  onPress: baseFnType
  style: {
    backgroundColor: string
    color: string}}export interface SwipeActionProps {
  index: numberright? : RightOptionsType[] onOpen? : baseFnType onClose? : baseFnType disabled? :boolean
  children: React.ReactNode
  autoClose: boolean
}

const SwipeAction: React.FC<SwipeActionProps> = (props) = > {
  const {
    right: btnOptions,
    index,
    onOpen,
    onClose,
    disabled = false,
    children,
    autoClose = false,
  } = props

  const getLeft = (dom: HTMLElement): number= >
    parseInt(dom.style.left || '0px'.10)

  const contentDomRef = useRef<HTMLDivElement>(null)
  const btnDomRef = useRef<HTMLDivElement>(null)
  const touchStartX = useRef<number> (0) // Save the initial click coordinates
  const touchStartLeft = useRef<number> (0) // Save the initial position when clicked
  const directionRef = useRef<string> (' ') // Save the slide direction, used in both TouchMove and TouchEnd

  const preTouchS = useRef<number> (0)

  / * * *@description Sliding process@param e* /
  const touchmove = useCallback(
    (e) = > {
      if (disabled) return
      const contentDom = contentDomRef.current
      const startX = touchStartX.current // Start touch
      const currentX = e.touches[0].pageX // Real-time location
      const btnWidth = btnDomRef.current.offsetWidth // Button width

      const maxLeftS = btnWidth + 15 // Slide left for maximum distance

      const touchS = currentX - startX

      if (touchS - preTouchS.current < 0) {
        directionRef.current = 'left'
      } else {
        directionRef.current = 'right'
      }

      if (
        (directionRef.current === 'right' && getLeft(contentDom) > 5) ||
        (directionRef.current === 'left' && Math.abs(touchS) >= maxLeftS)
      ) {
        return
      }

      // Estimate displacement = initial left + touch offset
      let s = touchStartLeft.current + touchS

      // Adjust the displacement to prevent excessive slip
      if (s < 0) {
        s = Math.abs(s) > maxLeftS ? -maxLeftS : s
      } else {
        s = s > 5 ? 5 : s
      }

      // window.requestAnimationFrame(() => {
      contentDom.style.left = `${s}px`
      // })

      preTouchS.current = touchS
    },
    [disabled]
  )

  / * * *@description Slide to start recording the starting position *@param e* /
  const touchstart = useCallback((e) = > {
    touchStartLeft.current = getLeft(contentDomRef.current)
    touchStartX.current = e.touches[0].pageX
  }, [])

  / * * *@description Slide end restore position *@param e* /
  const touchend = useCallback(() = > {
    const contentDom = contentDomRef.current
    const direction = directionRef.current // Where is it going
    const btnWidth = btnDomRef.current.offsetWidth // The width of the button is the maximum distance to slide
    if (direction === 'left') {
      contentDom.style.left = `${-btnWidth}px`
      typeof onOpen === 'function' && onOpen(index)
    } else if (direction === 'right') {
      contentDom.style.left = `The ${0}px`
      typeof onClose === 'function' && onClose(index)
    }
  }, [index, onClose, onOpen])

  /** * restore */
  const handleReset = () = > {
    const contentDom = contentDomRef.current
    contentDom.style.left = '0px'
  }

  / * * *@description Component mounts listener/remove events */
  useEffect(() = > {
    const contentDom = contentDomRef.current
    contentDom.style.transition = 's 0.1 all'
    contentDom.addEventListener('touchstart', touchstart)
    contentDom.addEventListener('touchmove', touchmove)
    contentDom.addEventListener('touchend', touchend)
    return () = > {
      contentDom.removeEventListener('touchstart', touchstart)
      contentDom.removeEventListener('touchmove', touchmove)
      contentDom.removeEventListener('touchend', touchend)
    }
  }, [touchend, touchmove, touchstart])

  return (
    <div className="slider-wrap">
      <div ref={contentDomRef} className="slider-content">
        {children}
      </div>
      <div ref={btnDomRef} className="slider-btn">
        {btnOptions.map((item) => (
          <button
            type="button"
            key={item.text}
            onClick={()= > {
              autoClose && handleReset()
              item.onPress(index)
            }}
            style={{
              background: item.style.backgroundColor,
              color: item.style.color,
            }}
          >
            {item.text}
          </button>
        ))}
      </div>
    </div>)}export default SwipeAction

Copy the code

scss

$swipeActionPrefixCls: 'slider-wrap';
$listItemPrefixCls: 'au-list-item';
.slider-wrap {
  position: relative;
  display: inline-block;
  overflow-x: hidden; // Slide the left part off
}

.slider-content {
  display: inline-block;
  width: 100%;
  height: 100%;
  position: relative;
  z-index: 20;
  vertical-align: bottom; // If this is not set, wrap will be higher than content
}

.slider-btn {
  position: absolute;
  right: 0;
  // width: 100px;
  height: 100%;

  display: inline-flex;
  flex-direction: row;

  transform: scale(0.98);

  button {
    flex: 1;
    padding: 0;
    border: none; # {}}.$swipeActionPrefixCls} {
  border-top: 1px solid $gray-400;
  &:last-child {
    border-bottom: 1px solid $gray-400; # {}.$listItemPrefixCls} {
    border: none ! important;
    &:active {
      opacity: 0.8; }}}Copy the code