问题场景

所谓悬浮窗就是图中微信图标的按钮,采用fixed定位,可拖动和点击。

这算是一个比较常见的实现场景了。

为什么要用cover-view做悬浮窗?原生组件出来背锅了~

最初我做悬浮窗用的不是cover-view,而是view。

这是简化的代码结构:

index.wxml:

一大段test,占个位,表示下存在感

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',

})

}

})

为什么要用cover-view呢?

因为页面上有个textarea组件,这个组件是原生组件,当悬浮窗移动到这个textarea组件上时,将无法继续拖动和点击。

如果悬浮窗一开始就定位在textarea上,那么就更惨了,一开始就不能点击和拖动了。

这个原因时因为微信小程序的原生组件层级高于非原生组件,不是你修改几下样式就能解决的问题。

这里就不讲什么原生组件了,如果想进一步了解,可以参考我之前写的一篇博客:微信小程序在ios下Echarts图表不能滑动的解决方案。

如果你的页面上面没有原生组件,那么像上面的代码一样用view做悬浮窗即可。

如果有,那么就可以跟着我继续踩坑,使用cover-view这个原生组件层级的组件来做悬浮窗。

安卓下的cover-view拖动起来,抖得不像帕金森,像是魔鬼的步伐

以下是我们修改为cover-view之后的代码:

一大段test,占个位,表示下存在感

注意这里,我们的image也改为了cover-image,因为cover-view只支持嵌套 cover-view、cover-image,不过可在 cover-view 中使用 button。

这样虽然解决了可在原生组件上自由拖动点击的问题,但是在安卓上出现了一个很奇怪的现象,以至于我认为已经无法用抖动可以来形容了:

上图是就是我滑动这个悬浮窗之后的效果,我只是很缓慢地在移动手指,但是这个悬浮窗的表现简直就像一个受惊的兔子。

当我第一眼看见这个效果的时候一脸懵逼,我都不知道说什么好。

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

勉强能看的补丁方案

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

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

一大段test,占个位,表示下存在感

当然少不了要在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:

一大段test,占个位,表示下存在感

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:

一大段test,占个位,表示下存在感

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:

一大段test,占个位,表示下存在感

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月都没有结果。

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

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

以上所述是小编给大家介绍的微信小程序中悬浮窗功能的实现 ,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

小程序全局悬浮窗_微信小程序中悬浮窗功能的实现代码相关推荐

  1. 小程序全局悬浮窗_微信小程序悬浮窗弹出怎么实现?

    微信小程序悬浮窗弹出怎么实现?很多的微信小程序管理员会在微信小程序界面开发微信小程序悬浮窗弹出功能,接下来小编会为大家介绍微信小程序悬浮窗弹出关注实现的全部步骤哦. 微信小程序悬浮窗弹出怎么实现? 微 ...

  2. python天气查询小程序加背景图_微信小程序开发背景图显示功能

    这两天开发微信小程序,在设置背景图片时,发现在wxss里面设置background-image:(url) 属性,不管是开发工具还是线上都无法显示.经过查资料发现,background-image只能 ...

  3. java小程序显示多种按钮_微信小程序 多行文本显示...+显示更多按钮和收起更多按钮功能...

    看了很多帖子,但是效果都不是很好.还是找微信小程序官方文档,自己写比较方便.自己动手丰衣足食!话不多说,上代码! 先来个效果图 html {{item.text}} 查看更多 收起 wxss .box ...

  4. 小程序商店刷榜_微信小程序游戏跳一跳刷榜原理解析!

    最近微信出了一个"跳一跳"的小游戏,这个游戏其实在之前有手机端版本,无奈微信借助强大的用户把这游戏又拿起来弄火了,而且通过最新版的微信可以看出,微信已经开始重视小程序的入口释放了, ...

  5. 微信小程序存在的风险_微信小程序开发技术风险存在,如何规避是重点

    微信小程序开发技术风险存在,如何规避是重点 微信小程序自上线以来已经历经三年的时间,不断推陈出新,推展业务,如今已形成了一定规模和影响力,线上购物.在线点餐.预订服务.便捷出行.小游戏等多种多样的小程 ...

  6. 微信小程序存在的风险_微信小程序存在哪些风险

    微信小程序存在哪些风险?之前一直在给大家爱关于微信小程序的各种好处,但是它其实也是存在一定的风险的,今天小编就来给大家讲一下微信小程序都存在哪些风险. 由于微信主程序会通过 JS 接口向小程序暴露规定 ...

  7. 微信小程序超级占内存_微信小程序占用内存小,用户再也不用担心内存不足问题了...

    内存占用小,微信小程序,让用户再也不用担心小程序不足内存问题了,时下,一站式支持常用APP(手机应用软件)的微信小程序受到市民青睐. 微信小程序,是一种不需要下载安装即可使用的应用,它实现了应用&qu ...

  8. 小程序商店刷榜_微信小程序怎么通过“硬广”“软广”来运营引流?运营干货...

    随着互联网的飞速发展,如今微信小程序已经成为家喻户晓的一款应用了,生活中随处可见的微信小程序成为了许多人日常生活中必不可少的应用.那么对于小程序商家来说营销策划方案显得格外重要,如果想要引入更多流量用 ...

  9. 微信小程序js数组初始化_微信小程序开发之改变data中数组或对象的某一属性值...

    前言:在小程序的开发中,我们在view中便利data中数组或对象时,很多情况下需要在js中动态改变数组或者对象中某一香的属性值. 效果图: 我给大家总结了案例如下: wxml如下: {{item.we ...

  10. 小程序获取城市经纬度_微信小程序获取当前所在城市的方法

    现在很多企业商家为了提供更准确的服务,基本都要获取用户当前所在的地理位置,城市是其中最基本的.而微信小程序官方提供的API只能获取当前地理位置的经纬度,需要经过第三方转换才可以得到我所需的城市名,经过 ...

最新文章

  1. 工业界AI项目落地血泪教训总结
  2. 增加一列为主键 oracle,给oracle数据库增加一列做主键,增加一个sequence,数据库怎么获得主键。...
  3. GCC弱符号的一个应用示例
  4. [Python] L1-021. 重要的话说三遍-PAT团体程序设计天梯赛GPLT
  5. 清除stoped impdp/expdp job的方法
  6. linux mv复制命令,linux中删除复制移动文件rm,mv,cp命令详解linux操作系统 -电脑资料...
  7. find命令与locate命令的区别
  8. php图片翻转函数,PHP图片处理之图片旋转和图片翻转实例
  9. 基于C#语言的可编程表达式计算器设计
  10. 2014.9.20CSS样式表
  11. BZOJ 1055 [HAOI2008]玩具取名 DP
  12. 读书笔记之《Redis开发与运维》—— 一
  13. Abaqus齿轮有限元分析
  14. win10怎么更新显卡驱动_如何更新电脑的显卡驱动(驱动精灵)
  15. 自制Alfred/Wox插件推荐
  16. JavaScript 火焰
  17. 银行ATM操作系统作业
  18. Tiled有java版本吗_使用TILED映射的Java碰撞检测
  19. 连锁不平衡的计算以及LDSC分析多基因遗传
  20. 一个大型虚拟项目包含位于不同地点的许多干系人_PLAN8T ART 丨在虚拟维度的拼贴现实中,漫游,想象。...

热门文章

  1. Android Scroller 滑动机制
  2. 领导想延长我的试用期,我该怎么办?
  3. Resolution和Scale关系
  4. 收藏:产品经理和技术经理等的OKR模板大全
  5. 服务器是用集成网卡好还是独立网卡好
  6. 素描正确握笔的姿势是怎么样的?
  7. php laravel mix,Laravel前端工程化之mix
  8. python处理ts_python将ts转换成MP4
  9. 《根道果:禅修的方法与次第》读后感
  10. 项目使用mybatis-plus采用mysql/clickhouse多数据库配置,报错Invalid bound statement (not found)