Floating window function to achieve the micro-letter applet (address and resolve major drag on the native component)

Problem Scenario

FIG floating window is called the micro-channel icon button using fixed positioning, drag and click.

This is regarded as a relatively common implementation scenarios of.

Why do cover-view floating window? Native components back out of the pot -

Initially I did not use floating window cover-view, but the view.

This is a simplified structure of the code:

index.wxml:

<view class="move-view" style=" top:{{top}}px;left:{{left}}px;" bindtap="goToHome" catchtouchmove="setTouchMove">
    <image class="img" src="https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=4294841024,3545417298&fm=179&app=42&f=PNG?w=56&h=56">
    </image>
</view>
<textarea placeholder='我是textarea组件,用来输入一些信息'></textarea>
<view>
  一大段test,占个位,表示下存在感
</view>

index.js:

Page({

  /**
  * 页面的初始数据
  */
  data: {
    left: 20,
    top: 250,
    isIos: true
  },
  /**
  * 拖拽移动
  */
  setTouchMove: function (e) {
    if (e.touches[0].clientX > 0 && e.touches[0].clientY > 0) {
      this.setData({
        left: e.touches[0].clientX - 30,
        top: e.touches[0].clientY - 30
      })
    } else {
      this.setData({
        left: 20, //默认显示位置 left距离
        top: 250  //默认显示位置 top距离
      })
    }
  },
  /**
  * 返回首页
  */
  goToHome: () => {
    wx.reLaunch({
      url: '/pages/index/index',
    })
  }
})

Why cover-view it?

Because the page has a textarea component, this component is the primary component, when the floating window to move to the textarea component, will not continue to drag and click.

If you start floating window is positioned on the textarea, then it is even worse, at the beginning you can not click and drag up.

When this because the primary components of the micro-letter applet level is higher than the non-native components, is not your style to modify a few problems can be solved.

Here is not to talk about what the native components, if you want to learn more about, you can refer to before I wrote a blog: micro-channel small program in ios Echarts chart solution can not slide .

If your page is not above the native components, like the above code so as to do with the view window can be suspended.

If so, then you can follow me to continue to step on the pit, using the cover-view native component level components to do the floating window.

the cover-view Andrews dragging up, unlike Parkinson shaking, such as pace devil

Here is the code we modified to cover-view after:

<cover-view class="move-view" style=" top:{{top}}px;left:{{left}}px;" bindtap="goToHome" catchtouchmove="setTouchMove">
    <cover-image class="img" src="https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=4294841024,3545417298&fm=179&app=42&f=PNG?w=56&h=56">
    </cover-image>
</cover-view>
<textarea placeholder='我是textarea组件,用来输入一些信息'></textarea>
<view>
  一大段test,占个位,表示下存在感
</view>

Note that we have changed the image of the cover-image, because the cover-view only supports nesting cover-view, cover-image, but you can use the button in the cover-view.

Although this resolved issue can be freely dragged click on native components, but there is a very strange phenomenon on Android, so I think it has been unable to shake can use to describe it:

The figure is what I slip effect after the floating window, I was just moving very slowly in the fingers, but the floating window's performance was like a frightened rabbit.

When I first saw this effect look ignorant force, I do not know what to say.

虽然在ios上cover-view移动起来表现良好,但是在安卓上拖动起来的表现简直没法看。

勉强能看的补丁方案

安卓上这么挫,还不如原来的呢。

所以来个补丁方案好了,在ios下用cover-view完美拖动,在安卓上用view先跑着。

<cover-view wx-if="{{isIos}}" class="move-view" style=" top:{{top}}px;left:{{left}}px;" bindtap="goToHome" catchtouchmove="setTouchMove">
    <cover-image class="img" src="https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=4294841024,3545417298&fm=179&app=42&f=PNG?w=56&h=56">
    </cover-image>
</cover-view>
<view wx-if="{{!isIos}}" class="move-view" style=" top:{{top}}px;left:{{left}}px;" bindtap="goToHome" catchtouchmove="setTouchMove">
    <image class="img" src="https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=4294841024,3545417298&fm=179&app=42&f=PNG?w=56&h=56">
    </image>
</view>
<textarea placeholder='我是textarea组件,用来输入一些信息'></textarea>
<view>
  一大段test,占个位,表示下存在感
</view>

当然少不了要在js里面加上这句代码:

onLoad: function (options) {
  wx.getSystemInfo({
    success: (res) => {
      if (res.platform == "android") {
        this.setData({
          isIos: false
        })
      }
    }
  })
}

不要忘记isIos默认为true哦。

反正ios环境下可以完美使用了,至于安卓下拖到textarea组件上没法再拖的问题,调整下悬浮框的初始位置就好了。

而且只要不是刻意移动到textarea组件上,拖动着悬浮框经过textarea组件也是没有问题的嘛。

像我这么聪明的用户还懂得滑动下面的页面来使悬浮窗移动到非原生组件的地方,这样就又可以拖动了嘛。

你又以为你的测试一定能发现这个问题?发现了又怎样,我已经尽力了,还给你整出这么多理论依据,足够你把锅牢牢地按在微信小程序官方的头上。

使用movable-view:仿佛发现了新大陆,结果发现这个还是个弟弟

甩锅是一定要甩锅的,但是段位要高。

所以要遍查官方文档,探讨一切可能性,以免甩锅的时候被打脸。

我们仔细观察小程序官方文档,发现还是有个专门用来拖动的组件叫movable-view。

这个组件和cover-view摆放在一起仿佛很厉害的样子,紧接着我们在原生组件使用限制文档中发现了它并不是原生组件。

也就是说这个东西的层级一定还是低于咱们的textarea组件的。

虽然已经很确定这个东西没什么用了,但是最后还是试探一把,结果发现是个真弟弟,这里就不给出代码了。

我写这个弟弟方案放在这里的目的主要是为了不要浪费你的验证时间。

理论上行得通的方案:将拖动事件的捕获放在父级

现在我们确认的最优甩锅方案里,已经实现了功能和甩锅两不误。

那么作为一名有追求的技术人员,还是需要去探讨以下这个问题到底有没有完美的解决方案。

因为我最开始是把这个悬浮窗做成了一个组件,那么作为组件来讲,这个东西就只能做到这个地步了。

不过如果你是像我现在的例子一样直接做在了页面里,那么实现起来也不是说没有办法的。

我们将拖动的事件放在父级上就可以了,请看接下来的代码:

index.wxml:

<view bindtouchmove="setTouchMove">
    <view class="move-view" style=" top:{{top}}px;left:{{left}}px;" bindtap="goToHome">
        <image class="img" src="https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=4294841024,3545417298&fm=179&app=42&f=PNG?w=56&h=56">
        </image>
    </view>
    <textarea placeholder='我是textarea组件,用来输入一些信息'></textarea>
    <view>
      一大段test,占个位,表示下存在感
    </view>
</view>

index.js:

Page({

  /**
  * 页面的初始数据
  */
  data: {
    left: 20,
    top: 250
  },

  /**
  * 拖拽移动
  */
  setTouchMove: function (e) {
    const MOVE_VIEW_RADIUS = 30 // 悬浮窗半径

    const touchPosX = e.touches[0].clientX
    const touchPosY = e.touches[0].clientY

    const moveViewCenterPosX = this.data.left + MOVE_VIEW_RADIUS
    const moveViewCenterPosY = this.data.top + MOVE_VIEW_RADIUS

    // 确保手指在悬浮窗上才可以移动
    if (Math.abs(moveViewCenterPosX - touchPosX) < MOVE_VIEW_RADIUS + 60 && Math.abs(moveViewCenterPosY - touchPosY) < MOVE_VIEW_RADIUS + 60) {
      if (touchPosX > 0 && touchPosY > 0) {
        this.setData({
          left: touchPosX - MOVE_VIEW_RADIUS,
          top: touchPosY - MOVE_VIEW_RADIUS
        })
      } else {
        this.setData({
          left: 20, // 默认显示位置 left距离
          top: 250  // 默认显示位置 top距离
        })
      }
    }
  },
  /**
  * 返回首页
  */
  goToHome: () => {
    wx.reLaunch({
      url: '/pages/index/index',
    })
  }
})

关键代码就是这块了:

// 确保手指在悬浮窗上才可以移动
if (Math.abs(moveViewCenterPosX - touchPosX) < MOVE_VIEW_RADIUS + 60 && Math.abs(moveViewCenterPosY - touchPosY) < MOVE_VIEW_RADIUS + 60) {

}

只要确保手指在悬浮窗的范围内就可以触发移动了,这里的60是为了确保你的手指太大,或者移动得比较快时超出了悬浮窗区域依然可以触发拖动,这个可以自己设定数值。

这个方案在理论上很合理,并且还加上了60这个缓冲区域,但是实际在拖动的时候你仍然会面临下面三个问题:

1.如果悬浮窗下方有滚动区域,那么拖动的时候就会滚动页面,效果会显得比较奇怪。
2.实际移动没法移动太顺畅,只能拖着悬浮窗亦步亦趋,要不然很容易超过60这个缓冲区域,导致拖动不继续触发。
2.如果将缓冲区域设置过大,那么又会出现一种比较奇怪的场景:明明不准备拖动悬浮窗,只是准备滑动页面,悬浮窗却跳到自己手指这里了。

进阶解决方案:禁止冒泡的拖动 + 理论方案

这个解决方案基于我们的最初方案,并且使用我们的理论方案作为补充。

先上代码:

index.wxml:

<view bindtouchmove="handleSetMoveViewPos">
    <view class="move-view" style=" top:{{top}}px;left:{{left}}px;" bindtap="goToHome" catchtouchmove="handleTouchMove">
        <image class="img" src="https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=4294841024,3545417298&fm=179&app=42&f=PNG?w=56&h=56">
        </image>
    </view>
    <textarea placeholder='我是textarea组件,用来输入一些信息'></textarea>
    <view>
      一大段test,占个位,表示下存在感
    </view>
</view>

index.js:

Page({
  /**
  * 页面的初始数据
  */
  data: {
    left: 20,
    top: 250
  },
  /**
  * 拖拽移动(补丁)
  */
  handleSetMoveViewPos: function (e) {
    const MOVE_VIEW_RADIUS = 30 // 悬浮窗半径

    const touchPosX = e.touches[0].clientX
    const touchPosY = e.touches[0].clientY

    const moveViewCenterPosX = this.data.left + MOVE_VIEW_RADIUS
    const moveViewCenterPosY = this.data.top + MOVE_VIEW_RADIUS

    // 确保手指在悬浮窗上才可以移动
    if (Math.abs(moveViewCenterPosX - touchPosX) < MOVE_VIEW_RADIUS+30 && Math.abs(moveViewCenterPosY - touchPosY) < MOVE_VIEW_RADIUS+30 ) {
      if (touchPosX > 0 && touchPosY > 0) {
        this.setData({
          left: touchPosX - MOVE_VIEW_RADIUS,
          top: touchPosY - MOVE_VIEW_RADIUS
        })
      } else {
        this.setData({
          left: 20, // 默认显示位置 left距离
          top: 250  // 默认显示位置 top距离
        })
      }
    }
  },
  /**
  * 拖拽移动
  */
  handleTouchMove: function (e) {
    const MOVE_VIEW_RADIUS = 30 // 悬浮窗半径

    const touchPosX = e.touches[0].clientX
    const touchPosY = e.touches[0].clientY

    if (touchPosX > 0 && touchPosY > 0) {
      this.setData({
        left: touchPosX - MOVE_VIEW_RADIUS,
        top: touchPosY - MOVE_VIEW_RADIUS
      })
    } else {
      this.setData({
        left: 20, //默认显示位置 left距离
        top: 250  //默认显示位置 top距离
      })
    }
  },
  /**
  * 返回首页
  */
  goToHome: () => {
    wx.reLaunch({
      url: '/pages/index/index',
    })
  }
})

这个方案的核心点在于:catchtouchmove="handleTouchMove"

当我们正常拖动悬浮窗时,通过catchtouchmove,我们可以捕获在悬浮窗上的滑动事件,并且不冒泡到父元素,那么我们绑在父层级的滑动事件就不会触发。

而当我们拖动在原生组件之上的悬浮窗时,因为点不到这个悬浮窗,就不会触发handleTouchMove函数,只会触发绑定在父元素上的handleSetMoveViewPos函数。

另外如果你细心的话,就会发现在handleSetMoveViewPos函数这里我缩小了那个60的缓冲区域为30,这样做的目的是因为触发这个函数只会在原生组件上,所以多番权衡距离之后,尽量避免近距离滑动操作就触发拖动悬浮框。

通过我们的方案,我们可以在非原生组件上自由拖动,在原生组件上比较顺畅地拖动。

本来我是准备将这个方案作为最终方案的,但是ios下,悬浮窗在原生组件上时,在父元素上的滑动事件竟然不触发。

棋差一招,棋差一招啊!

最终解决方案:更多的补丁,更多的快乐

这个最终解决方案,当然是把我们之前所有的补丁方案全部结合起来。

代码如下:

index.wxml:

<view bindtouchmove="handleSetMoveViewPos">
    <view wx-if="{{!isIos}}" class="move-view" style=" top:{{top}}px;left:{{left}}px;" bindtap="goToHome" catchtouchmove="handleTouchMove">
        <image class="img" src="https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=4294841024,3545417298&fm=179&app=42&f=PNG?w=56&h=56">
        </image>
    </view>
    <cover-view wx-if="{{isIos}}" class="move-view" style=" top:{{top}}px;left:{{left}}px;" bindtap="goToHome" catchtouchmove="handleTouchMove">
        <cover-image class="img" src="https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=4294841024,3545417298&fm=179&app=42&f=PNG?w=56&h=56">
        </cover-image>
    </cover-view>
    <textarea placeholder='我是textarea组件,用来输入一些信息'></textarea>
    <view>
      一大段test,占个位,表示下存在感
    </view>
</view>

index.js:

Page({

  /**
  * 页面的初始数据
  */
  data: {
    left: 20,
    top: 250,
    isIos: true
  },

  /**
  * 生命周期函数--监听页面加载
  */
  onLoad: function (options) {
    wx.getSystemInfo({
      success: (res) => {
        if (res.platform == "android") {
          this.setData({
            isIos: false
          })
        }
      }
    })
  },

  /**
  * 拖拽移动(补丁)
  */
  handleSetMoveViewPos: function (e) {
    // 在ios下永远都不会走这个方案,以免引起无用的计算
    if (!ios) {
      const MOVE_VIEW_RADIUS = 30 // 悬浮窗半径

      const touchPosX = e.touches[0].clientX
      const touchPosY = e.touches[0].clientY

      const moveViewCenterPosX = this.data.left + MOVE_VIEW_RADIUS
      const moveViewCenterPosY = this.data.top + MOVE_VIEW_RADIUS

      // 确保手指在悬浮窗上才可以移动
      if (Math.abs(moveViewCenterPosX - touchPosX) < MOVE_VIEW_RADIUS && Math.abs(moveViewCenterPosY - touchPosY) < MOVE_VIEW_RADIUS) {
        if (touchPosX > 0 && touchPosY > 0) {
          this.setData({
            left: touchPosX - MOVE_VIEW_RADIUS,
            top: touchPosY - MOVE_VIEW_RADIUS
          })
        } else {
          this.setData({
            left: 20, // 默认显示位置 left距离
            top: 250  // 默认显示位置 top距离
          })
        }
      }
    }
  },
  /**
  * 拖拽移动
  */
  handleTouchMove: function (e) {
    const MOVE_VIEW_RADIUS = 30 // 悬浮窗半径

    const touchPosX = e.touches[0].clientX
    const touchPosY = e.touches[0].clientY

    if (touchPosX > 0 && touchPosY > 0) {
      this.setData({
        left: touchPosX - MOVE_VIEW_RADIUS,
        top: touchPosY - MOVE_VIEW_RADIUS
      })
    } else {
      this.setData({
        left: 20, //默认显示位置 left距离
        top: 250  //默认显示位置 top距离
      })
    }
  },
  /**
  * 返回首页
  */
  goToHome: () => {
    wx.reLaunch({
      url: '/pages/index/index',
    })
  }
})

这个最终解决方案在ios下直接使用cover-view来做悬浮窗,而在android的非原生组件上移动时,使用view来做悬浮窗,不冒泡滑动事件,在原生组件上移动时捕获冒泡的滑动事件来继续移动操作。

总结

虽然问题解决了,但是这仍然只是一个补丁方案。

最好的方式依然是微信小程序官方能修复cover-view在安卓移动时的BUG,但是我发现最早有人反馈这个问题是在2018年11月,到了现在2019年8月都没有结果。

如果不是微信小程序的官方态度有问题,那么只能说明这个问题的解决确实有难度或者优先级并不高,无论是哪一种,暂时都还是得用补丁方案。

这个方案并没有那么完美,他在一些边界的衔接上面可能还是会存在一些小问题,但它至少可用,并且应该是大多数用户可以接受的。

Guess you like

Origin www.cnblogs.com/vvjiang/p/11286880.html