麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁(yè) > 編程 > JavaScript > 正文

微信小程序中懸浮窗功能的實(shí)現(xiàn)代碼

2019-11-19 11:05:07
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

問(wèn)題場(chǎng)景

所謂懸浮窗就是圖中微信圖標(biāo)的按鈕,采用fixed定位,可拖動(dòng)和點(diǎn)擊。

這算是一個(gè)比較常見(jiàn)的實(shí)現(xiàn)場(chǎng)景了。

為什么要用cover-view做懸浮窗?原生組件出來(lái)背鍋了~

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

這是簡(jiǎn)化的代碼結(jié)構(gòu):

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組件,用來(lái)輸入一些信息'></textarea><view> 一大段test,占個(gè)位,表示下存在感</view>index.js:Page({ /** * 頁(yè)面的初始數(shù)據(jù) */ data: { left: 20, top: 250, isIos: true }, /** * 拖拽移動(dòng) */ 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, //默認(rèn)顯示位置 left距離  top: 250 //默認(rèn)顯示位置 top距離  }) } }, /** * 返回首頁(yè) */ goToHome: () => { wx.reLaunch({  url: '/pages/index/index', }) }})

為什么要用cover-view呢?

因?yàn)轫?yè)面上有個(gè)textarea組件,這個(gè)組件是原生組件,當(dāng)懸浮窗移動(dòng)到這個(gè)textarea組件上時(shí),將無(wú)法繼續(xù)拖動(dòng)和點(diǎn)擊。

如果懸浮窗一開(kāi)始就定位在textarea上,那么就更慘了,一開(kāi)始就不能點(diǎn)擊和拖動(dòng)了。

這個(gè)原因時(shí)因?yàn)槲⑿判〕绦虻脑M件層級(jí)高于非原生組件,不是你修改幾下樣式就能解決的問(wèn)題。

這里就不講什么原生組件了,如果想進(jìn)一步了解,可以參考我之前寫(xiě)的一篇博客:微信小程序在ios下Echarts圖表不能滑動(dòng)的解決方案。

如果你的頁(yè)面上面沒(méi)有原生組件,那么像上面的代碼一樣用view做懸浮窗即可。

如果有,那么就可以跟著我繼續(xù)踩坑,使用cover-view這個(gè)原生組件層級(jí)的組件來(lái)做懸浮窗。

安卓下的cover-view拖動(dòng)起來(lái),抖得不像帕金森,像是魔鬼的步伐

以下是我們修改為cover-view之后的代碼:

<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組件,用來(lái)輸入一些信息'></textarea><view> 一大段test,占個(gè)位,表示下存在感</view>

注意這里,我們的image也改為了cover-image,因?yàn)閏over-view只支持嵌套 cover-view、cover-image,不過(guò)可在 cover-view 中使用 button。

這樣雖然解決了可在原生組件上自由拖動(dòng)點(diǎn)擊的問(wèn)題,但是在安卓上出現(xiàn)了一個(gè)很奇怪的現(xiàn)象,以至于我認(rèn)為已經(jīng)無(wú)法用抖動(dòng)可以來(lái)形容了:

上圖是就是我滑動(dòng)這個(gè)懸浮窗之后的效果,我只是很緩慢地在移動(dòng)手指,但是這個(gè)懸浮窗的表現(xiàn)簡(jiǎn)直就像一個(gè)受驚的兔子。

當(dāng)我第一眼看見(jiàn)這個(gè)效果的時(shí)候一臉懵逼,我都不知道說(shuō)什么好。

雖然在ios上cover-view移動(dòng)起來(lái)表現(xiàn)良好,但是在安卓上拖動(dòng)起來(lái)的表現(xiàn)簡(jiǎn)直沒(méi)法看。

勉強(qiáng)能看的補(bǔ)丁方案

安卓上這么挫,還不如原來(lái)的呢。

所以來(lái)個(gè)補(bǔ)丁方案好了,在ios下用cover-view完美拖動(dòng),在安卓上用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組件,用來(lái)輸入一些信息'></textarea><view> 一大段test,占個(gè)位,表示下存在感</view>

當(dāng)然少不了要在js里面加上這句代碼:

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

不要忘記isIos默認(rèn)為true哦。

反正ios環(huán)境下可以完美使用了,至于安卓下拖到textarea組件上沒(méi)法再拖的問(wèn)題,調(diào)整下懸浮框的初始位置就好了。

而且只要不是刻意移動(dòng)到textarea組件上,拖動(dòng)著懸浮框經(jīng)過(guò)textarea組件也是沒(méi)有問(wèn)題的嘛。

像我這么聰明的用戶還懂得滑動(dòng)下面的頁(yè)面來(lái)使懸浮窗移動(dòng)到非原生組件的地方,這樣就又可以拖動(dòng)了嘛。

你又以為你的測(cè)試一定能發(fā)現(xiàn)這個(gè)問(wèn)題?發(fā)現(xiàn)了又怎樣,我已經(jīng)盡力了,還給你整出這么多理論依據(jù),足夠你把鍋牢牢地按在微信小程序官方的頭上。

使用movable-view:仿佛發(fā)現(xiàn)了新大陸,結(jié)果發(fā)現(xiàn)這個(gè)還是個(gè)弟弟

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

所以要遍查官方文檔,探討一切可能性,以免甩鍋的時(shí)候被打臉。

我們仔細(xì)觀察小程序官方文檔,發(fā)現(xiàn)還是有個(gè)專門用來(lái)拖動(dòng)的組件叫movable-view

這個(gè)組件和cover-view擺放在一起仿佛很厲害的樣子,緊接著我們?cè)谠M件使用限制文檔中發(fā)現(xiàn)了它并不是原生組件。

也就是說(shuō)這個(gè)東西的層級(jí)一定還是低于咱們的textarea組件的。

雖然已經(jīng)很確定這個(gè)東西沒(méi)什么用了,但是最后還是試探一把,結(jié)果發(fā)現(xiàn)是個(gè)真弟弟,這里就不給出代碼了。

我寫(xiě)這個(gè)弟弟方案放在這里的目的主要是為了不要浪費(fèi)你的驗(yàn)證時(shí)間。

理論上行得通的方案:將拖動(dòng)事件的捕獲放在父級(jí)

現(xiàn)在我們確認(rèn)的最優(yōu)甩鍋方案里,已經(jīng)實(shí)現(xiàn)了功能和甩鍋兩不誤。

那么作為一名有追求的技術(shù)人員,還是需要去探討以下這個(gè)問(wèn)題到底有沒(méi)有完美的解決方案。

因?yàn)槲易铋_(kāi)始是把這個(gè)懸浮窗做成了一個(gè)組件,那么作為組件來(lái)講,這個(gè)東西就只能做到這個(gè)地步了。

不過(guò)如果你是像我現(xiàn)在的例子一樣直接做在了頁(yè)面里,那么實(shí)現(xiàn)起來(lái)也不是說(shuō)沒(méi)有辦法的。

我們將拖動(dòng)的事件放在父級(jí)上就可以了,請(qǐng)看接下來(lái)的代碼:

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組件,用來(lái)輸入一些信息'></textarea> <view>  一大段test,占個(gè)位,表示下存在感 </view></view>index.js:Page({ /** * 頁(yè)面的初始數(shù)據(jù) */ data: { left: 20, top: 250 }, /** * 拖拽移動(dòng) */ 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 // 確保手指在懸浮窗上才可以移動(dòng) 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, // 默認(rèn)顯示位置 left距離   top: 250 // 默認(rèn)顯示位置 top距離  })  } } }, /** * 返回首頁(yè) */ goToHome: () => { wx.reLaunch({  url: '/pages/index/index', }) }})

關(guān)鍵代碼就是這塊了:

// 確保手指在懸浮窗上才可以移動(dòng)if (Math.abs(moveViewCenterPosX - touchPosX) < MOVE_VIEW_RADIUS + 60 && Math.abs(moveViewCenterPosY - touchPosY) < MOVE_VIEW_RADIUS + 60) {}

只要確保手指在懸浮窗的范圍內(nèi)就可以觸發(fā)移動(dòng)了,這里的60是為了確保你的手指太大,或者移動(dòng)得比較快時(shí)超出了懸浮窗區(qū)域依然可以觸發(fā)拖動(dòng),這個(gè)可以自己設(shè)定數(shù)值。

這個(gè)方案在理論上很合理,并且還加上了60這個(gè)緩沖區(qū)域,但是實(shí)際在拖動(dòng)的時(shí)候你仍然會(huì)面臨下面三個(gè)問(wèn)題:

1.如果懸浮窗下方有滾動(dòng)區(qū)域,那么拖動(dòng)的時(shí)候就會(huì)滾動(dòng)頁(yè)面,效果會(huì)顯得比較奇怪。
2.實(shí)際移動(dòng)沒(méi)法移動(dòng)太順暢,只能拖著懸浮窗亦步亦趨,要不然很容易超過(guò)60這個(gè)緩沖區(qū)域,導(dǎo)致拖動(dòng)不繼續(xù)觸發(fā)。
2.如果將緩沖區(qū)域設(shè)置過(guò)大,那么又會(huì)出現(xiàn)一種比較奇怪的場(chǎng)景:明明不準(zhǔn)備拖動(dòng)懸浮窗,只是準(zhǔn)備滑動(dòng)頁(yè)面,懸浮窗卻跳到自己手指這里了。

進(jìn)階解決方案:禁止冒泡的拖動(dòng) + 理論方案

這個(gè)解決方案基于我們的最初方案,并且使用我們的理論方案作為補(bǔ)充。

先上代碼:

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組件,用來(lái)輸入一些信息'></textarea> <view>  一大段test,占個(gè)位,表示下存在感 </view></view>index.js:Page({ /** * 頁(yè)面的初始數(shù)據(jù) */ data: { left: 20, top: 250 }, /** * 拖拽移動(dòng)(補(bǔ)丁) */ 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 // 確保手指在懸浮窗上才可以移動(dòng) 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, // 默認(rèn)顯示位置 left距離   top: 250 // 默認(rèn)顯示位置 top距離  })  } } }, /** * 拖拽移動(dòng) */ 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, //默認(rèn)顯示位置 left距離  top: 250 //默認(rèn)顯示位置 top距離  }) } }, /** * 返回首頁(yè) */ goToHome: () => { wx.reLaunch({  url: '/pages/index/index', }) }})

這個(gè)方案的核心點(diǎn)在于:catchtouchmove="handleTouchMove"

當(dāng)我們正常拖動(dòng)懸浮窗時(shí),通過(guò)catchtouchmove,我們可以捕獲在懸浮窗上的滑動(dòng)事件,并且不冒泡到父元素,那么我們綁在父層級(jí)的滑動(dòng)事件就不會(huì)觸發(fā)。

而當(dāng)我們拖動(dòng)在原生組件之上的懸浮窗時(shí),因?yàn)辄c(diǎn)不到這個(gè)懸浮窗,就不會(huì)觸發(fā)handleTouchMove函數(shù),只會(huì)觸發(fā)綁定在父元素上的handleSetMoveViewPos函數(shù)。

另外如果你細(xì)心的話,就會(huì)發(fā)現(xiàn)在handleSetMoveViewPos函數(shù)這里我縮小了那個(gè)60的緩沖區(qū)域?yàn)?0,這樣做的目的是因?yàn)橛|發(fā)這個(gè)函數(shù)只會(huì)在原生組件上,所以多番權(quán)衡距離之后,盡量避免近距離滑動(dòng)操作就觸發(fā)拖動(dòng)懸浮框。

通過(guò)我們的方案,我們可以在非原生組件上自由拖動(dòng),在原生組件上比較順暢地拖動(dòng)。

本來(lái)我是準(zhǔn)備將這個(gè)方案作為最終方案的,但是ios下,懸浮窗在原生組件上時(shí),在父元素上的滑動(dòng)事件竟然不觸發(fā)。

棋差一招,棋差一招啊!

最終解決方案:更多的補(bǔ)丁,更多的快樂(lè)

這個(gè)最終解決方案,當(dāng)然是把我們之前所有的補(bǔ)丁方案全部結(jié)合起來(lái)。

代碼如下:

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組件,用來(lái)輸入一些信息'></textarea> <view>  一大段test,占個(gè)位,表示下存在感 </view></view>index.js:Page({ /** * 頁(yè)面的初始數(shù)據(jù) */ data: { left: 20, top: 250, isIos: true }, /** * 生命周期函數(shù)--監(jiān)聽(tīng)頁(yè)面加載 */ onLoad: function (options) { wx.getSystemInfo({  success: (res) => {  if (res.platform == "android") {   this.setData({   isIos: false   })  }  } }) }, /** * 拖拽移動(dòng)(補(bǔ)丁) */ handleSetMoveViewPos: function (e) { // 在ios下永遠(yuǎn)都不會(huì)走這個(gè)方案,以免引起無(wú)用的計(jì)算 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  // 確保手指在懸浮窗上才可以移動(dòng)  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, // 默認(rèn)顯示位置 left距離   top: 250 // 默認(rèn)顯示位置 top距離   })  }  } } }, /** * 拖拽移動(dòng) */ 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, //默認(rèn)顯示位置 left距離  top: 250 //默認(rèn)顯示位置 top距離  }) } }, /** * 返回首頁(yè) */ goToHome: () => { wx.reLaunch({  url: '/pages/index/index', }) }})

這個(gè)最終解決方案在ios下直接使用cover-view來(lái)做懸浮窗,而在android的非原生組件上移動(dòng)時(shí),使用view來(lái)做懸浮窗,不冒泡滑動(dòng)事件,在原生組件上移動(dòng)時(shí)捕獲冒泡的滑動(dòng)事件來(lái)繼續(xù)移動(dòng)操作。

總結(jié)

雖然問(wèn)題解決了,但是這仍然只是一個(gè)補(bǔ)丁方案。

最好的方式依然是微信小程序官方能修復(fù)cover-view在安卓移動(dòng)時(shí)的BUG,但是我發(fā)現(xiàn)最早有人反饋這個(gè)問(wèn)題是在2018年11月,到了現(xiàn)在2019年8月都沒(méi)有結(jié)果。

如果不是微信小程序的官方態(tài)度有問(wèn)題,那么只能說(shuō)明這個(gè)問(wèn)題的解決確實(shí)有難度或者優(yōu)先級(jí)并不高,無(wú)論是哪一種,暫時(shí)都還是得用補(bǔ)丁方案。

這個(gè)方案并沒(méi)有那么完美,他在一些邊界的銜接上面可能還是會(huì)存在一些小問(wèn)題,但它至少可用,并且應(yīng)該是大多數(shù)用戶可以接受的。

以上所述是小編給大家介紹的微信小程序中懸浮窗功能的實(shí)現(xiàn) ,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)武林網(wǎng)網(wǎng)站的支持!
如果你覺(jué)得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 一本色道久久综合亚洲精品图片 | 久久色播 | av免费不卡国产观看 | 懂色av懂色aⅴ精彩av | 九九黄色影院 | 伦理三区 | 在线91观看 | 高清国产午夜精品久久久久久 | 一级片九九| 中国字幕av | 国产成人av免费 | 欧美日韩亚洲成人 | 国产va在线观看免费 | 免费观看高清视频网站 | 国产精品视频在 | 91精品国产777在线观看 | 一级毛片在线免费观看 | 精品久久久久久久久久久αⅴ | 一级黄色影院 | 国产一区二区三区在线观看视频 | 日韩精品中文字幕在线播放 | 一级黄色毛片免费 | 久久99亚洲精品久久99果 | 毛片电影网址 | 欧美中文字幕一区二区三区亚洲 | 国产午夜亚洲精品午夜鲁丝片 | 伊人久久电影网 | 亚洲国产精品久久久久 | 特片网久久 | 亚洲影视中文字幕 | 成人羞羞视频在线观看免费 | 亚洲 综合 欧美 动漫 丝袜图 | 欧美一级在线免费 | 亚洲va久久久噜噜噜久牛牛影视 | 欧洲精品久久久久69精品 | 亚洲无av| 亚洲一级片在线观看 | 国产精品自在线拍 | 国产精品久久久久久久久久10秀 | 91在线色| 久久久久久久久淑女av国产精品 |