這兩天完成了一個仿樊登 H5 的音樂播放懸浮球效果,這個效果跟微信音樂播放懸浮球的效果也很相似,今天總結(jié)一下實現(xiàn)的思路過程(基于 Vue 實現(xiàn))。
先來看看樊登 H5 的效果。

再來看看我的實現(xiàn),挺完美的,哈哈。

實現(xiàn)起來也不復(fù)雜,主要是一步步想清楚下面的實現(xiàn)步驟:
第一步:小球被限制在固定范圍內(nèi)滑動
第二步:停止滑動時,小球吸邊效果
第三步:點擊吸邊狀態(tài)下的小球,變換為音頻播放控制面板,點擊毛玻璃,恢復(fù)成小球
第四步:背景毛玻璃效果
一步步來說。
第一步:小球被限制在固定范圍內(nèi)滑動
touchmove可以獲得小球的滑動事件,所以可以使用touchmove?來限制小球的滑動范圍。
先給小球的 touchmove 傳入 handleTouchMove 函數(shù),在 handleTouchMove 中獲取到滑動事件 e,使用 e.targetTouches[0]可以獲得手指滑動的坐標(biāo) clientX 和 clientY,來計算小球 left、top 值,從而來控制小球隨手指滑動。
<div
class="circle"
v-if="!showCircleContent"
@click="handleClickCircle"
@touchend="handleTouchEnd"
@touchmove.prevent="handleTouchMove"
></div>
當(dāng)手指或鼠標(biāo)滑動到超過屏幕的左邊界時,就將小球的 left 重置為 0,不讓小球超出左邊界。當(dāng)滑動超過右邊界時,就將小球的 left 重置為視口的寬度減去小球的寬度,不讓小球超出右邊界。
頂部和底部邊界也一樣,當(dāng)手指或鼠標(biāo)滑動到超過頂部邊界時,將小球的 top 重置為 0,超過底部邊界時,將 top 重置為視口的高度減去小球的高度。
當(dāng)然滑動范圍可以自己來控制,詳細(xì)控制小球滑動的代碼如下:
//限制懸浮球一個范圍內(nèi)滑動
handleTouchMove(e) {
this.isAnimated = false; //手指拖動小球滑動的時候不需要動畫,滑動停止的時候添加動畫
this.$refs.circle.style.borderRadius = "50%";
let offsetX = e.targetTouches[0].clientX - this.circleWidth / 2; //減去this.circleWidth / 2目的是讓手指按在懸浮球正中
let offsetY = e.targetTouches[0].clientY - this.circleHeight / 2; //減去this.circleHeight / 2目的是讓手指按在懸浮球正中
if (offsetX <= 0 + this.placeholderWidth) {
offsetX = 0 + this.placeholderWidth; //不讓懸浮球完全貼邊
} else if (
offsetX >=
document.documentElement.clientWidth -
this.circleWidth -
this.placeholderWidth //微信懸浮球不是完全貼邊,離屏幕邊緣有一段距離
) {
offsetX =
document.documentElement.clientWidth -
this.circleWidth -
this.placeholderWidth;
}
if (offsetY <= 0 + this.placeholderWidth) {
offsetY = 0 + this.placeholderWidth;
} else if (
offsetY >=
document.documentElement.clientHeight -
this.circleHeight -
this.placeholderWidth
) {
offsetY =
document.documentElement.clientHeight -
this.circleHeight -
this.placeholderWidth;
}
this.$refs.circle.style.left = offsetX + "px";
this.$refs.circle.style.top = offsetY + "px";
},
第二步:停止滑動,懸浮球吸邊效果
滑動小球,當(dāng)手指松開時,小球需要吸附到屏幕邊緣。根據(jù)小球中心的 x 坐標(biāo)判斷,如果 x 坐標(biāo)小于屏幕寬度的一半,就吸附到屏幕左邊,如果大于等于屏幕寬的一半則吸附到屏幕右邊,實現(xiàn)代碼走一波:
//touchend的時候,懸浮球吸邊顯示
handleTouchEnd(e) {
this.isAnimated = true; //手指拖動小球滑動的時候不需要動畫,滑動停止的時候添加動畫
let circleCenterX =
parseInt(this.$refs.circle.style.left.replace("px", "")) +
this.circleWidth / 2; //懸浮球中心的x坐標(biāo)
let circleCenterY =
parseInt(this.$refs.circle.style.top.replace("px", "")) +
this.circleHeight / 2; //懸浮球中心的y坐標(biāo)
if (circleCenterX < this.clientWidth / 2) {
//吸附左側(cè)
this.$refs.circle.style.left = 0;
this.$refs.circle.style.borderRadius = "0 50% 50% 0";
} else {
//吸附在右側(cè)
this.$refs.circle.style.left =
this.clientWidth - this.circleWidth + "px";
this.$refs.circle.style.borderRadius = "50% 0 0 50%";
}
if (circleCenterY >= this.clientHeight - this.placeholderBottom) {
this.$refs.circle.style.top =
this.clientHeight - this.placeholderBottom + "px";
}
},
第三步:點擊吸邊小球,變換為音頻播放控制面板
當(dāng)小球吸附到屏幕邊緣時,點擊小球,小球展開成音頻控制面板,點擊毛玻璃背景,小球收縮恢復(fù)原狀。

在實現(xiàn)時我將小球 circle 跟音頻控制面板 content 分開布局,大概是這個樣子。
<div
class="circle-box"
:class="{
opened: showCircleContent,
animation: isAnimated
}"
ref="circle"
>
<div class="content" v-if="showCircleContent">
<!-- 音頻控制面板,?顯示歌曲名字、播放時長,同時還能控制播放暫停音頻 -->
</div>
<div class="circle" v-if="!showCircleContent">
<!-- 小球懸浮狀態(tài) -->
</div>
</div>
使用 circle-box 包裹懸浮小球 circle 和音頻控制面板 content,使用變量 showCircleContent 來判斷當(dāng)前應(yīng)該顯示懸浮球還是音頻播放控制面板。
當(dāng)點擊懸浮球時 showCircleContent 為 true,小球伸長變換為音頻播放控制面板。
點擊毛玻璃背景,設(shè)置 showCircleContent 為 false,音頻播放控制面板收縮恢復(fù)成懸浮小球。
伸縮動畫

小球從懸浮球變?yōu)橐纛l控制面板,或者從控制面板變?yōu)閼腋∏驎r是有動畫的,這個動畫應(yīng)該在 touchend 的時候才添加,在 touchmove 的時候去掉。因為小球滑動時如果有動畫的話,會有卡頓的現(xiàn)象。
使用 isAnimated 來控制動畫的添加和刪除,在 touchend 的時候為 true,在 touchmove 改為 false。
第四步:背景毛玻璃效果
backdrop-filter 屬性實現(xiàn)毛玻璃了解一下,filter 只能讓當(dāng)前元素有模糊效果,當(dāng)前元素之下的元素還是清晰的。而 backdrop-filter 可以讓當(dāng)前元素及以下的元素都模糊,從而實現(xiàn)毛玻璃的效果。
backdrop-filter: blur(10px); //毛玻璃效果,ios有效
-webkit-backdrop-filter: blur(10px);
但是目前 backdrop-filter 的兼容性不是太好,在 IOS 平臺能看到毛玻璃效果,Android 平臺看不到。

OK,以上就是實現(xiàn)音樂播放懸浮球的思路總結(jié),其實在實現(xiàn)過程中還有很多細(xì)節(jié)上的處理,如果想了解更多細(xì)節(jié),可以關(guān)注我的公眾號「程序員張晴天」,并在后臺回復(fù)「懸浮球」即可獲得demo完整源碼。
如果對你有幫助的話,點贊、評論、贊賞都是對我的鼓勵,也是支持我寫下去的動力,謝謝!
本文原創(chuàng)發(fā)布于微信公眾號「程序員張晴天」,歡迎關(guān)注第一時間獲取最新分享,一起進(jìn)步。
