This is the 22nd day of my participation in Gwen Challenge

preface

Work needs to add a suspension button, but the general suspension button is very ugly, and occupy a fixed position is not very easy to use, will feel very awkward. So I was wondering if I could make a hover button that could be dragged, and it could automatically lean up when it was near the edge of the screen. The idea is ok. The next step is implementation.

implementation

Because the project time is limited, it is better to go straight to the off-the-shelf. Sure enough, github found ready-made components, directly to use it is also very good, the effect is very good. address

parsing

Now that I don’t have to write it myself, let’s see how it works.

In fact, a drag hover button is mainly composed of three parts:

  1. Move the trigger
  2. In the mobile
  3. Mobile end

Let’s look at what variables are needed to control the movement of the button:

1. Constructor

constructor(props) {
    super(props)
    this.state = {
        oLeft: "".// The distance to the left of the window
        oTop: "" // The distance to the top of the window
    }
    this.$vm = null // Hover button
    this.moving = false // Move state

    this.oW = null // The distance of the suspension button
    this.oH = null

    this.htmlWidth = null // Page width
    this.htmlHeight = null

    this.bWidth = null // Switch width
    this.bHeight = null

    this.click = null
}
Copy the code

2. Movement trigger

// Move trigger
onTouchStart(e) {
    e = e.touches[0]
    this.click = true

    // e.clientx ==> The X coordinate of the contact relative to the left edge of the visual viewPort. Does not include any roll offset.
    // this.$vm.getBoundingClientRect(). Left ==> relative to the left side of the window
    this.oW = e.clientX - this.$vm.getBoundingClientRect().left
    this.oH = e.clientY - this.$vm.getBoundingClientRect().top

    this.htmlWidth = document.documentElement.clientWidth
    this.htmlHeight = document.documentElement.clientHeight

    this.bWidth = this.$vm.offsetWidth
    this.bHeight = this.$vm.offsetHeight

    let oLeft = e.clientX - this.oW
    let oTop = e.clientY - this.oH

    // Record the initial position when the button moves
    this.setState({
        oLeft,
        oTop
    })

    this.moving = true
}
Copy the code

3. In the mobile

// Start moving
onTouchMove(e) {
    this.$vm.className = "t-suspend-button"
    this.moving && this.onMove(e)
}

/ / move
onMove(e) {
    e = e.touches[0]
    this.click = false

    // Left distance
    let oLeft = e.clientX - this.oW
    let oTop = e.clientY - this.oH

    // Reset the left and right critical points
    if (oLeft < 0) {
        oLeft = 0
    } else if (oLeft > this.htmlWidth - this.bWidth) {
        oLeft = this.htmlWidth - this.bWidth
    }
    // Reset the top and right critical points
    if (oTop < 0) {
        oTop = 0
    } else if (oTop > this.htmlHeight - this.bHeight) {
        oTop = this.htmlHeight - this.bHeight
    }

    this.setState({
        oLeft,
        oTop
    })
}
Copy the code

4. The move is complete

// The move is complete
onTouchEnd(e) {
    this.moving = false

    // Added a delay animation to make the pull over look less abrupt
    this.$vm.className = this.$vm.className + " t-suspend-button-animate"

    // Left distance
    // Calculate the distance to the left, if more than half go to the right, less than half go back to the left
    let oLeft = this.state.oLeft
    if (oLeft < (this.htmlWidth - this.bWidth) / 2) {
        oLeft = 0
    } else {
        oLeft = this.htmlWidth - this.bWidth
    }

    if (this.click) {
        this.props.onClick()
    }
    // }
    // Same as on the left, calculate the top distance
    // if(oTop < 0) {
    // oTop = 0
    // } else if (oTop > this.htmlHeight - this.bHeight) {
    // oTop = this.htmlHeight - this.bHeight
    // }

    this.setState({
        oLeft
        // oTop})}Copy the code

5. Render components

// Display the final position of the button via fix positioning
<span
    className="t-suspend-button"
    ref={$vm => (this.$vm = $vm)}
    onTouchStart={e= > this.onTouchStart(e)}
    onTouchMove={e= > this.onTouchMove(e)}
    onTouchEnd={e= > this.onTouchEnd(e)}
    style={{
      left: `The ${this.state.oLeft}px`.top: `The ${this.state.oTop}px`. style }} > {img &&<img src={img} alt="" />}
</span>
Copy the code

6. The final

// Add passive to the DOM listener to make the animation more silky
this.$vm.addEventListener(
  "touchmove".e= > {
    if (e.cancelable) {
      e.preventDefault()
    }
  },
  {
    passive: false})Copy the code

The final result