vue?懸浮窗且?guī)ё詣?dòng)吸附功能實(shí)現(xiàn)demo
封裝的組件代碼
之前寫過懸浮窗的效果,這次做了個(gè)總結(jié),網(wǎng)頁(yè)端和移動(dòng)端都可以用兼容,封裝的組件代碼,可以引到頁(yè)面直接使用
代碼
做了簡(jiǎn)單的注釋 大家自行了解
HTML
<template> <div ref="floatDrag" class="float-position" :style="{ left: left + 'px', top: top + 'px', zIndex: zIndex }" @touchmove.prevent @mousemove.prevent @mousedown="mouseDown" @mouseup="mouseUp" > <div id="side-windows"> <div class="shrink"> <div class="problem-feedback" @click.stop="showDialog()"> <img :src="feedback" alt="" /> <p>問題<br />反饋</p> </div> </div> </div> </div> </template>
JS
<script> export default { name: "DragBall", props: { distanceRight: { // 初始化定位 type: Number, default: 0 }, distanceBottom: { // 初始化定位 type: Number, default: 100 }, isScrollHidden: { //滾動(dòng)是否 隱藏 type: Boolean, default: false }, isCanDraggable: { //是否允許拖拽 type: Boolean, default: true }, zIndex: { // 初始化層級(jí) type: Number, default: 50 }, value: { type: String, default: "懸?。? } }, //data 域 data() { return { clientWidth: null, clientHeight: null, left: 0, top: 0, timer: null, currentTop: 0, mousedownX: 0, mousedownY: 0, feedback: require('') // 問題 }; }, created() { this.clientWidth = document.documentElement.clientWidth; this.clientHeight = document.documentElement.clientHeight; }, mounted() { this.isCanDraggable && this.$nextTick(() => { this.floatDrag = this.$refs.floatDrag; // 獲取元素位置屬性 this.floatDragDom = this.floatDrag.getBoundingClientRect(); // 設(shè)置初始位置 this.left = this.clientWidth - this.floatDragDom.width - this.distanceRight; this.top = this.clientHeight - this.floatDragDom.height - this.distanceBottom; this.initDraggable(); }); this.isScrollHidden && window.addEventListener("scroll", this.handleScroll); window.addEventListener("resize", this.handleResize); }, methods: { showDialog() { let url window.open(url, '_blank') }, /** * 設(shè)置滾動(dòng)時(shí)隱藏懸浮按鈕,停止時(shí)顯示 */ handleScroll() { this.timer && clearTimeout(this.timer); this.timer = setTimeout(() => { this.handleScrollEnd(); }, 200); this.currentTop = document.documentElement.scrollTop || document.body.scrollTop; if (this.left > this.clientWidth / 2) { // 判斷元素位置再左側(cè)還是右側(cè) this.left = this.clientWidth + this.floatDragDom.width; } else { this.left = -this.floatDragDom.width; } }, /** * 滾動(dòng)結(jié)束 */ handleScrollEnd() { let scrollTop = document.documentElement.scrollTop || document.body.scrollTop; if (scrollTop === this.currentTop) { console.log(this.left); if (this.left > this.clientWidth / 2) { // 判斷元素位置 this.left = this.clientWidth - this.floatDragDom.width; } else { this.left = 0; } clearTimeout(this.timer); } }, /** * 窗口監(jiān)聽 */ handleResize() { this.clientWidth = document.documentElement.clientWidth; this.clientHeight = document.documentElement.clientHeight; this.checkDraggablePosition(); }, /** * 初始化 */ initDraggable() { this.floatDrag.addEventListener("touchstart", this.toucheStart); this.floatDrag.addEventListener("touchmove", e => this.touchMove(e)); this.floatDrag.addEventListener("touchend", this.touchEnd); }, mouseDown(e) { const event = e || window.event; this.mousedownX = event.screenX; this.mousedownY = event.screenY; const that = this; let floatDragWidth = this.floatDragDom.width / 2; let floatDragHeight = this.floatDragDom.height / 2; if (event.preventDefault) { event.preventDefault(); } this.canClick = false; this.floatDrag.style.transition = "none"; setTimeout(() => { document.onmousemove = function(e) { var event = e || window.event; that.left = event.clientX - floatDragWidth; that.top = event.clientY - floatDragHeight; if (that.left < 0) that.left = 0; if (that.top < 0) that.top = 0; if (that.left >= that.clientWidth - floatDragWidth * 2) { that.left = that.clientWidth - floatDragWidth * 2; } if (that.top >= that.clientHeight - floatDragHeight * 2) { that.top = that.clientHeight - floatDragHeight * 2; } // 解決鼠標(biāo)移出窗口 松開鼠標(biāo)后 回到窗口內(nèi) 懸浮繼續(xù)跟隨問題 if(event.clientX<=0 || event.clientY<=0 || event.clientY>= that.clientHeight || event.clientX>= that.clientWidth){ that.mouseUp(event) } }; }, 20); }, mouseUp(e) { const event = e || window.event; //判斷只是單純的點(diǎn)擊,沒有拖拽 if ( this.mousedownY == event.screenY && this.mousedownX == event.screenX ) { this.$emit("handlepaly"); } document.onmousemove = null; this.checkDraggablePosition(); this.floatDrag.style.transition = "all 0.3s"; }, toucheStart() { this.canClick = false; this.floatDrag.style.transition = "none"; }, touchMove(e) { this.canClick = true; if (e.targetTouches.length === 1) { let touch = event.targetTouches[0]; this.left = touch.clientX - this.floatDragDom.width / 2; this.top = touch.clientY - this.floatDragDom.height / 2; } }, touchEnd() { if (!this.canClick) return; // 解決點(diǎn)擊事件和touch事件沖突的問題 this.floatDrag.style.transition = "all 0.3s"; this.checkDraggablePosition(); }, /** * 判斷元素顯示位置 * 在窗口改變和move end時(shí)調(diào)用 */ checkDraggablePosition() { let details = document.querySelector('.details') if (this.left + this.floatDragDom.width / 2 >= this.clientWidth / 2) { // 判斷位置是往左往右滑動(dòng) this.left = this.clientWidth - this.floatDragDom.width; details.style.right = '56px' } else { this.left = 0; details.style.right = '-233px' } if (this.top < 0) { // 判斷是否超出屏幕上沿 this.top = 0; } if (this.top + this.floatDragDom.height >= this.clientHeight) { // 判斷是否超出屏幕下沿 this.top = this.clientHeight - this.floatDragDom.height; } } }, beforeDestroy() { window.removeEventListener("scroll", this.handleScroll); window.removeEventListener("resize", this.handleResize); } }; </script>
樣式
<style lang="less" scoped> .float-position{ font-size: 12px; position: fixed; z-index: 500!important; right: 0; top: 50%; width: 48px; height: 168px; display: flex; align-items: center; justify-content: center; user-select: none; } </style>
以上就是vue 懸浮窗且?guī)ё詣?dòng)吸附功能實(shí)現(xiàn)demo的詳細(xì)內(nèi)容,更多關(guān)于vue 懸浮窗自動(dòng)吸附的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
淺談VueUse中useAsyncState的實(shí)現(xiàn)原理
useAsyncState?是 VueUse 庫(kù)中提供的一個(gè)實(shí)用工具,它用于處理異步狀態(tài),本文主要介紹了VueUse中useAsyncState的實(shí)現(xiàn)及其原理,具有一定的參考價(jià)值,感興趣的可以了解一下2024-08-08vue scroll滾動(dòng)判斷的實(shí)現(xiàn)(是否滾動(dòng)到底部、滾動(dòng)方向、滾動(dòng)節(jié)流、獲取滾動(dòng)區(qū)域dom元素)
這篇文章主要介紹了vue scroll滾動(dòng)判斷的實(shí)現(xiàn)(是否滾動(dòng)到底部、滾動(dòng)方向、滾動(dòng)節(jié)流、獲取滾動(dòng)區(qū)域dom元素),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06Vue多層數(shù)據(jù)結(jié)構(gòu)響應(yīng)式失效,視圖更新失敗問題
這篇文章主要介紹了Vue多層數(shù)據(jù)結(jié)構(gòu)響應(yīng)式失效,視圖更新失敗問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02vue項(xiàng)目中的webpack-dev-sever配置方法
下面小編就為大家分享一篇vue項(xiàng)目中的webpack-dev-sever配置方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2017-12-12vue addRoutes實(shí)現(xiàn)動(dòng)態(tài)權(quán)限路由菜單的示例
本篇文章主要介紹了vue addRoutes實(shí)現(xiàn)動(dòng)態(tài)權(quán)限路由菜單的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧2018-05-05簡(jiǎn)單理解vue中el、template、replace元素
這篇文章主要幫助大家簡(jiǎn)單理解vue中el、template、replace元素,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10Vue3實(shí)現(xiàn)列表無(wú)限滾動(dòng)的示例詳解
這篇文章主要為大家詳細(xì)介紹了如何使用Vue3實(shí)現(xiàn)列表無(wú)限滾動(dòng)的效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-07-07