vue2实现可拖拽悬浮球效果
使用场景:vue2.0悬浮球可拖拽得效果,划过后展开可跳转链接,下面上代码:
<template> <div ref="draggable" class="draggable" @mousedown="onMouseDown"> <div class="hover-area"> <nav class="menu"> <input id="menu-open" type="checkbox" class="menu-open" name="menu-open" v-model="menuOpen" /> <label class="menu-open-button" for="menu-open"> <span class="hamburger hamburger-1"></span> <span class="hamburger hamburger-2"></span> <span class="hamburger hamburger-3"></span> </label> <a class="menu-item" @click="openItem('1')"> <i class="fa fa-heart"></i> 模拟 <br />收入</a > <a class="menu-item" @click="openItem('2')"> <i class="fa fa-envelope"></i> 我的<br />公告</a > <a class="menu-item" @click="openItem('3')"> <i class="fa fa-ellipsis-h"></i> 我的<br />舆情</a > <a class="menu-item" @click="openItem('4')"> <i class="fa fa-cog"></i>监控<br />设置 </a> </nav> </div> </div> </template>
<script> export default { data() { return { menuOpen: false, }; }, mounted() { const element = this.$refs.draggable; if (element) { element.style.position = 'fixed'; element.style.right = '50px'; element.style.top = `${document.documentElement.clientHeight / 2}px`; } }, methods: { onMouseDown(event) { const element = this.$refs.draggable; if (!element) return; const rect = element.getBoundingClientRect(); const offsetX = event.clientX - rect.left; const offsetY = event.clientY - rect.top; const onMouseMove = (moveEvent) => { // 解决onmouseup事件有时候不触发 if (moveEvent.stopPropagation) { moveEvent.stopPropagation(); } if (moveEvent.preventDefault) { moveEvent.preventDefault(); } const newLeft = moveEvent.clientX - offsetX; const newTop = moveEvent.clientY - offsetY; // 获取视口边界 const viewportWidth = document.documentElement.clientWidth; const viewportHeight = document.documentElement.clientHeight; // 获取元素的尺寸 const elementWidth = element.offsetWidth; const elementHeight = element.offsetHeight + 100; // 检查边界,确保div不超出视口 const boundedLeft = Math.max( 0, Math.min(newLeft, viewportWidth - elementWidth) ); const boundedTop = Math.max( 0, Math.min(newTop, viewportHeight - elementHeight) ); element.style.left = `${boundedLeft}px`; element.style.top = `${boundedTop}px`; }; const onMouseUp = () => { document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); }; document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); }, openItem(key) {
//跳转逻辑 //this.$emit('openItem',key) }, }, }; </script>
<style scoped> .draggable { z-index: 9999999; width: 100px; height: 100px; cursor: grab; user-select: none; } i { font-style: normal; } h1, h2, h3, h4 { margin: 0; margin-top: 10px; margin-bottom: 10px; } h1 { font-size: 3em; } .menu-item { position: absolute; top: 20px; width: 4em; height: 4em; padding-top: 4px; color: white; font-size: 14px; text-align: center; background: #585247; background-image: url('@/assets/images/yuan.png'); /* width: 80px; */ /* height: 80px; */ border: 0.5em solid #4956bd; /* background: #e91e63; */ border-radius: 100%; transform: translate3d(0, 0, 0); visibility: hidden; cursor: pointer; opacity: 0; transition: opacity 1s; } .menu-open-button { position: absolute; /* top: 20px; */ width: 3em; height: 3em; color: white; text-align: center; background: #585247; background-image: url('@/assets/images/yuan.png'); background-size: cover; /* width: 80px; */ /* height: 80px; */ border: 0.5em solid #4956bd; /* background: #e91e63; */ border-radius: 100%; transform: translate3d(0, 0, 0); opacity: 0.3; transition: transform ease-out 200ms; } .menu-open { display: none; } .hamburger { position: absolute; top: 50%; left: 60%; display: block; width: 18px; height: 3px; margin-top: -1.5px; margin-left: -12.5px; background: white; transition: transform 200ms; } .hamburger-1 { transform: translate3d(0, -8px, 0); } .hamburger-2 { transform: translate3d(0, 0, 0); } .hamburger-3 { transform: translate3d(0, 8px, 0); } .hover-area { width: 10px; height: 10px; background-color: transparent; border-radius: 50px; } .hover-area:hover { width: 100px; height: 200px; } .menu { position: absolute; top: 50%; left: 50%; box-sizing: border-box; width: 3em; height: 3em; /* margin-left: -190px; */ /* padding-top: 20px; */ /* padding-left: 190px; */ font-size: 20px; text-align: left; } .menu-item:hover { opacity: 0.9; } .menu-item:nth-child(3) { transition-duration: 180ms; } .menu-item:nth-child(4) { transition-duration: 180ms; } .menu-item:nth-child(5) { transition-duration: 180ms; } .menu-item:nth-child(6) { transition-duration: 180ms; } .menu-open-button { z-index: 2; transform: scale(1.1, 1.1) translate3d(0, 0, 0); cursor: pointer; transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275); transition-duration: 400ms; } .hover-area .menu { transform: rotate(0deg); transition: all 0.8s; } .hover-area:hover .menu { transform: rotate(-90deg); } .menu-open-button:hover { transform: scale(1.2, 1.2) translate3d(0, 0, 0); opacity: 0.9; } .hover-area:hover .menu-open-button { transform: scale(0.8, 0.8) translate3d(0, 0, 0); opacity: 0.9; transition-timing-function: linear; transition-duration: 200ms; } .hover-area:hover .menu-item { visibility: visible; opacity: 1; /* transition-timing-function: cubic-bezier(0.935, 0, 0.34, 1.33); */ } .hover-area .menu-item:nth-child(3) { transform: translate3d(67.96994px, -19.33095px, 0) rotate(90deg); transition-duration: 1s; } .hover-area .menu-item:nth-child(4) { transform: translate3d(33.96994px, -75.33095px, 0) rotate(90deg); transition-duration: 1s; } .hover-area .menu-item:nth-child(5) { /* top: -2.8em; */ /* left: 5em; */ transform: translate3d(-31.91639px, -75.00003px, 0) rotate(90deg); transition-duration: 1.2s; } .hover-area .menu-item:nth-child(6) { transform: translate3d(-64.13709px, -17.37936px, 0) rotate(90deg); transition-duration: 1.5s; } </style>
vue3+ts版本 css跟上面通用
<template> <div ref="draggable" class="draggable" @mousedown="onMouseDown"> <div class="hover-area"> <nav class="menu"> <input id="menu-open" type="checkbox" class="menu-open" name="menu-open" /> <label class="menu-open-button" for="menu-open"> <span class="hamburger hamburger-1"></span> <span class="hamburger hamburger-2"></span> <span class="hamburger hamburger-3"></span> </label> <a class="menu-item" @click="openItem('1')"> <i class="fa fa-heart"></i> 模拟 <br />收入</a > <a class="menu-item" @click="openItem('2')"> <i class="fa fa-envelope"></i> 我的<br />公告</a > <a class="menu-item" @click="openItem('3')"> <i class="fa fa-ellipsis-h"></i> 我的<br />舆情</a > <a class="menu-item" @click="openItem('4')"> <i class="fa fa-cog"></i>监控<br />设置 </a> </nav> </div> </div> </template> <script lang="ts"> import { defineComponent, onMounted, ref } from 'vue' export default defineComponent({ setup() { const draggable = ref<HTMLDivElement | null>(null) const onMouseDown = (event: MouseEvent) => { const element = draggable.value if (!element) return const rect = element.getBoundingClientRect() const offsetX = event.clientX - rect.left const offsetY = event.clientY - rect.top const onMouseMove = (moveEvent: MouseEvent) => { // 解决onmouseup事件有时候不触发 if (moveEvent.stopPropagation) { moveEvent.stopPropagation() } if (moveEvent.preventDefault) { moveEvent.preventDefault() } // element.style.pointerEvents = 'none' const newLeft = moveEvent.clientX - offsetX const newTop = moveEvent.clientY - offsetY // 获取视口边界 const viewportWidth = document.documentElement.clientWidth const viewportHeight = document.documentElement.clientHeight // 获取元素的尺寸 const elementWidth = element.offsetWidth const elementHeight = element.offsetHeight + 100 // 检查边界,确保div不超出视口 const boundedLeft = Math.max( 0, Math.min(newLeft, viewportWidth - elementWidth) ) const boundedTop = Math.max( 0, Math.min(newTop, viewportHeight - elementHeight) ) element.style.left = `${boundedLeft}px` element.style.top = `${boundedTop}px` } const onMouseUp = () => { document.removeEventListener('mousemove', onMouseMove) document.removeEventListener('mouseup', onMouseUp) // element.style.pointerEvents = '' } document.addEventListener('mousemove', onMouseMove) document.addEventListener('mouseup', onMouseUp) } onMounted(() => { const element = draggable.value if (element) { element.style.position = 'fixed' element.style.right = `50px` element.style.top = `${document.documentElement.clientHeight / 2}px` } }) const openItem = (key: string) => {
//跳转逻辑 } return { draggable, onMouseDown, openItem, } }, }) </script>
最终效果展示:

鼠标滑过后:


浙公网安备 33010602011771号