微信小程序中可全屏拖动悬浮按钮的实现方案

最近遇到一个需求,有个悬浮按钮在某些情况下会遮挡内容,所以想把它做成可以全屏移动的。在小程序中实现一个可全屏拖动的悬浮按钮,我第一时间想到了微信官方组件 movable-area 和movable-view。

看起来好像没毛病,实际上悬浮的 movable-area 遮挡了底层的所有内容,底层无法点击、划动,也无法触发 onPullDownRefresh (下拉)事件。改进,将所有内容放进 movable-area 中。

内容是可以点击、拖动了、但是仍然无法触发下拉事件,而且 movable-area 必须固定宽高,内容拖动后看起来按钮不可全屏移动了。网上有方案说在这里使用 scroll-view来实现,并通过滚动的位置来触发对应的事件。

顺便贴上代码

<movable-area style="min-height:100vh;width:100vw;">
  <scroll-view style="max-height:100vh;" scroll-y="true">
     <!-- 内容部分 -->
  </scroll-view>
  <movable-view style="height: 100px; width: 100px; background: blue;" direction="all">
  </movable-view>
</movable-area>

此方案确实可行,但是个人认为这个方案过于麻烦(主要在于要自行处理各种事件),因此、下决心自己写一个悬浮按钮的组件。

<!-- wxml -->
  <view style="left:{{x}}px;top:{{y}}px;position: fixed;z-index: 1;" bindtap='show' bindtouchmove="touchmove">
    <button>按钮</button>
  </view>
  // js
Component({
  methods: {
    touchmove({ changedTouches}){
      let x = changedTouches[0].pageX
      let y = changedTouches[0].pageY
      this.setData({
        x:x,
        y:y
      })
    }
  }
});

在要使用的页面引入此组件,效果如下:

但是内容层会跟随移动,所以在组件中捕获点击和移动事件。把tap改为catch。

  <view style="left:{{x}}px;top:{{y}}px;position: fixed;z-index: 1;" catchtap='show' catchtouchmove="touchmove">
    <button>按钮</button>
  </view>

这已经很接近我们想要的效果了,

但我认为这并不完美,在touchmove事件是一个频繁事件,如果频繁的setData来设置坐标会带来性能的问题,重来!使用wxs在视图层实现。

//wxs代码
module.exports = {
  touchmove: function(event, ownerInstance) {
    var clientX = event.changedTouches[0].clientX
    var clientY = event.changedTouches[0].clientY
    var style = {}
    var windowWidth = event.currentTarget.dataset.windowwidth
    var windowHeight = event.currentTarget.dataset.windowheight
    var instance = ownerInstance.selectComponent('.move')
    if (clientX <= windowWidth) {
      style.left = clientX + 'px'
      style.right = 'auto'
    } else {
      style.right = '0px'
      style.left = 'auto'
    }
    if (clientY <= windowHeight) {
      style.top = clientY + 'px'
      style.bottom = 'auto'
    } else {
      style.bottom = '0px'
      style.top = 'auto'
    }
    instance.setStyle(style)
  }
}
<!--wxml代码-->
  <view class="move" style="left:{{x}}px;top:{{y}}px;position: fixed;z-index: 1;" catchtap='show' catchtouchmove="{{move.touchmove}}" data-windowwidth="{{data-windowwidth}}" data-windowheight="{{windowheight}}">
    <button>按钮</button>
  </view>
  <wxs module="move" src="index.wxs"></wxs>

在这一版本中考虑了按钮拖出屏幕自动贴边(需要根据按钮的实际尺寸调整),至此,整个组件完成。如果大家有更好的方案欢迎与我讨论。

发表评论

电子邮件地址不会被公开。